花狗

导航

 

图:

1.传递信息

给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。 链接:https://leetcode-cn.com/problems/chuan-di-xin-xi

输入:n = 5, relation = [[0,2],[2,1],[3,4],[2,3],[1,4],[2,0],[0,4]], k = 3

输出:3

解释:信息从小 A 编号 0 处开始,经 3 轮传递,到达编号 4。共有 3 种方案,分别是 0->2->0->4, 0->2->1->4, 0->2->3->4。
//深度优先搜索
class Solution {
public:
   int numWays(int n, vector<vector<int>> &relation, int k) {
       vector<vector<int>> edges(n);
       for (auto &edge : relation) {
           int src = edge[0], dst = edge[1];
           edges[src].push_back(dst);
      }

       int ways = 0;
       function<void(int, int)> dfs = [&](int index, int steps) {
           if (steps == k) {
               if (index == n - 1) {
                   ++ways;
              }
               return;
          }
           for (int to : edges[index]) {
               dfs(to, steps + 1);
          }
      };
       dfs(0, 0);
       return ways;
  }
};

//广度优先搜索

class Solution {
public:
   int numWays(int n, vector<vector<int>> &relation, int k) {
       vector<vector<int>> edges(n);
       for (auto &edge : relation) {
           int src = edge[0], dst = edge[1];
           edges[src].push_back(dst);
      }

       int steps = 0;
       queue<int> que;
       que.push(0);
       while (!que.empty() && steps < k) {
           steps++;
           int size = que.size();
           for (int i = 0; i < size; i++) {
               int index = que.front();
               que.pop();
               for (auto &nextIndex : edges[index]) {
                   que.push(nextIndex);
              }
          }
      }

       int ways = 0;
       if (steps == k) {
           while (!que.empty()) {
               if (que.front() == n - 1) {
                   ways++;
              }
               que.pop();
          }
      }
       return ways;
  }
};

链表:

复杂链表的复制

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

方法一:

利用哈希表的查询特点,考虑构建 **原链表节点** 和 **新链表对应节点** 的键值对映射关系,再遍历构建新链表各节点的 `next` 和 `random` 引用指向即可。

 

class Solution {
public:
   Node* copyRandomList(Node* head) {
       if(head == nullptr) return nullptr;
       Node* cur = head;
       unordered_map<Node*, Node*> map;
       // 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
       while(cur != nullptr) {
           map[cur] = new Node(cur->val);
           cur = cur->next;
      }
       cur = head;
       // 4. 构建新链表的 next 和 random 指向
       while(cur != nullptr) {
           map[cur]->next = map[cur->next];
           map[cur]->random = map[cur->random];
           cur = cur->next;
      }
       // 5. 返回新链表的头节点
       return map[head];
  }
};

方法二:

image-20210806100621543

class Solution {
public:
   Node* copyRandomList(Node* head) {
       if(head == nullptr) return nullptr;
       Node* cur = head;
       // 1. 复制各节点,并构建拼接链表
       while(cur != nullptr) {
           Node* tmp = new Node(cur->val);
           tmp->next = cur->next;
           cur->next = tmp;
           cur = tmp->next;
      }
       // 2. 构建各新节点的 random 指向
       cur = head;
       while(cur != nullptr) {
           if(cur->random != nullptr)
               cur->next->random = cur->random->next;
           cur = cur->next->next;
      }
       // 3. 拆分两链表
       cur = head->next;
       Node* pre = head, *res = head->next;
       while(cur->next != nullptr) {
           pre->next = pre->next->next;
           cur->next = cur->next->next;
           pre = pre->next;
           cur = cur->next;
      }
       pre->next = nullptr; // 单独处理原链表尾节点
       return res;      // 返回新链表头节点
  }
};

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9plk45/

 

两数相加

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。 链接:https://leetcode-cn.com/problems/add-two-numbers-ii

输入:l1 = [7,2,4,3], l2 = [5,6,4]
输出:[7,8,0,7]
class Solution {
public:
   ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
       stack<int> s1, s2;
       while (l1) {
           s1.push(l1 -> val);
           l1 = l1 -> next;
      }
       while (l2) {
           s2.push(l2 -> val);
           l2 = l2 -> next;
      }
       int carry = 0;
       ListNode* ans = nullptr;
       while (!s1.empty() or !s2.empty() or carry != 0) {
           int a = s1.empty() ? 0 : s1.top();
           int b = s2.empty() ? 0 : s2.top();
           if (!s1.empty()) s1.pop();
           if (!s2.empty()) s2.pop();
           int cur = a + b + carry;
           carry = cur / 10;
           cur %= 10;
           auto curnode = new ListNode(cur);
           curnode -> next = ans;
           ans = curnode;
      }
       return ans;
  }
};

翻转链表

class Solution {
public:
   ListNode* reverseList(ListNode* head) {
       ListNode* cur = head;
       ListNode* pre = NULL;
       while(cur != NULL){
           ListNode* tmp = cur->next;
           cur->next = pre;
           pre = cur;
           cur = tmp;
      }
       return pre;
  }
};

 

字符串

两字符串相加

给定两个字符串形式的非负整数 num1num2 ,计算它们的和。

class Solution {
public:
   string addStrings(string num1, string num2) {
       int i = num1.length() - 1, j = num2.length() - 1, add = 0;
       string ans = "";
       while (i >= 0 || j >= 0 || add != 0) {
           int x = i >= 0 ? num1[i] - '0' : 0;
           int y = j >= 0 ? num2[j] - '0' : 0;
           int result = x + y + add;
           ans.push_back('0' + result % 10);
           add = result / 10;
           i -= 1;
           j -= 1;
      }
       // 计算完以后的答案需要翻转过来
       reverse(ans.begin(), ans.end());
       return ans;
  }
};

字符串翻转

例如:字符串s = s1 + s2;

image-20210806093529107

class Solution {
public:
   string reverseLeftWords(string s, int n) {
       reverseString(s, 0, n - 1);
       reverseString(s, n, s.size() - 1);
       reverseString(s, 0, s.size() - 1);
       return s;
  }
private:
   void reverseString(string& s, int i, int j) {
       while(i < j) swap(s[i++], s[j--]);
  }
};

 

 

有效的括号

class Solution {
public:
   bool isValid(string s) {
       int n = s.size();
       if (n % 2 == 1) {
           return false;
      }

       unordered_map<char, char> pairs = {
          {')', '('},
          {']', '['},
          {'}', '{'}
      };
       stack<char> stk;
       for (char ch: s) {
           if (pairs.count(ch)) {
               if (stk.empty() || stk.top() != pairs[ch]) {
                   return false;
              }
               stk.pop();
          }
           else {
               stk.push(ch);
          }
      }
       return stk.empty();
  }
};

把数字变成字符串(a-z 对应 0-25)有多少种组成方法

image-20210806221531606

class Solution {
public:
   int translateNum(int num) {
       int a = 1, b = 1, x, y = num % 10;
       while(num > 9) {
           num /= 10;
           x = num % 10;
           int tmp = 10 * x + y;
           int c = (tmp >= 10 && tmp <= 25) ? a + b : a;
           b = a;
           a = c;
           y = x;
      }
       return a;
  }
};

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/99dnh6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最长不含重复字符的字符串

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
class Solution {
public:
   int lengthOfLongestSubstring(string s) {
       unordered_map<char, int> dic;
       int i = -1, res = 0, len = s.size();
       for(int j = 0; j < len; j++) {
           if(dic.find(s[j]) != dic.end())
               i = max(i, dic.find(s[j])->second); // 更新左指针
           dic[s[j]] = j; // 哈希表记录
           res = max(res, j - i); // 更新结果
      }
       return res;
  }
};

字符串的排列

class Solution {
public:
   vector<string> permutation(string s) {
       dfs(s, 0);
       return res;
  }
private:
   vector<string> res;
   void dfs(string s, int x) {
       if(x == s.size() - 1) {
           res.push_back(s);                       // 添加排列方案
           return;
      }
       set<int> st;
       for(int i = x; i < s.size(); i++) {
           if(st.find(s[i]) != st.end()) continue; // 重复,因此剪枝
           st.insert(s[i]);
           swap(s[i], s[x]);                       // 交换,将 s[i] 固定在第 x 位
           dfs(s, x + 1);                          // 开启固定第 x + 1 位字符
           swap(s[i], s[x]);                       // 恢复交换
      }
  }
};


作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/50hah3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

LRU缓存算法

设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。

它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 链接:https://leetcode-cn.com/problems/lru-cache-lcci

struct DLinkedNode {
   int key, value;
   DLinkedNode* prev;
   DLinkedNode* next;
   DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
   DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};

class LRUCache {
private:
   unordered_map<int, DLinkedNode*> cache;
   DLinkedNode* head;
   DLinkedNode* tail;
   int size;
   int capacity;

public:
   LRUCache(int _capacity): capacity(_capacity), size(0) {
       // 使用伪头部和伪尾部节点
       head = new DLinkedNode();
       tail = new DLinkedNode();
       head->next = tail;
       tail->prev = head;
  }
   
   int get(int key) {
       if (!cache.count(key)) {
           return -1;
      }
       // 如果 key 存在,先通过哈希表定位,再移到头部
       DLinkedNode* node = cache[key];
       moveToHead(node);
       return node->value;
  }
   
   void put(int key, int value) {
       if (!cache.count(key)) {
           // 如果 key 不存在,创建一个新的节点
           DLinkedNode* node = new DLinkedNode(key, value);
           // 添加进哈希表
           cache[key] = node;
           // 添加至双向链表的头部
           addToHead(node);
           ++size;
           if (size > capacity) {
               // 如果超出容量,删除双向链表的尾部节点
               DLinkedNode* removed = removeTail();
               // 删除哈希表中对应的项
               cache.erase(removed->key);
               // 防止内存泄漏
               delete removed;
               --size;
          }
      }
       else {
           // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
           DLinkedNode* node = cache[key];
           node->value = value;
           moveToHead(node);
      }
  }

   void addToHead(DLinkedNode* node) {
       node->prev = head;
       node->next = head->next;
       head->next->prev = node;
       head->next = node;
  }
   
   void removeNode(DLinkedNode* node) {
       node->prev->next = node->next;
       node->next->prev = node->prev;
  }

   void moveToHead(DLinkedNode* node) {
       removeNode(node);
       addToHead(node);
  }

   DLinkedNode* removeTail() {
       DLinkedNode* node = tail->prev;
       removeNode(node);
       return node;
  }
};

链接:https://leetcode-cn.com/problems/lru-cache-lcci/solution/lruhuan-cun-by-leetcode-solution/

2021.08.03

数组查找

一个n+1的数组,乱序,只有一个重复的数字,把他找出来(不允许排序、不允许使用额外空间)

class Solution {
public:
int duplicateInArray(vector<int>& nums) {
  if(nums.empty()) return -1;
  int n = nums.size();
 
  for(int i = 0 ; i < n ; i ++)
  {
      if(nums[i] < 0 || nums[i] > n-1) return -1;
  }
 
  for(int i = 0 ; i < n ; i++)
  {
      while(nums[i] != i)  //按照一个个坑进行排序
      {
      //要先进行判断再排序
           if(nums[i] == nums[nums[i]]) return nums[i];  //这个数不仅在它原本的坑上出现,还在其他坑上也出现
           swap(nums[i],nums[nums[i]]);    //交换,以满足按坑排序
      }  
  }
return -1;
}
};

替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例:

输入:s = "We are happy."
输出:"We%20are%20happy."

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/50ywkd/

class Solution {
public:
   string replaceSpace(string s) {
       string result;
       for(int i = 0; i <= s.size(); ++i){
           if( s[i] != ' '){
               result += s[i];

          }
           else {
               result += '%';
               result += '2';
               result += '0';
          }
      }
       return result;

  }
};

从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

输入:head = [1,3,2]
输出:[2,3,1]

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5dt66m/

/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
   vector<int> reversePrint(ListNode* head) {
       dfs(head);
       return res;
  }

   void dfs(ListNode* head){
       if(head == NULL) return ;
       dfs(head->next);
       res.push_back(head->val);
  }
private:
   vector <int> res;

};

滑动窗口:

无重复的字符的最长字串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
class Solution {
public:
   int lengthOfLongestSubstring(string s) {
       // 哈希集合,记录每个字符是否出现过
       unordered_set<char> occ;
       int n = s.size();
       // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
       int rk = -1, ans = 0;
       // 枚举左指针的位置,初始值隐性地表示为 -1
       for (int i = 0; i < n; ++i) {
           if (i != 0) {
               // 左指针向右移动一格,移除一个字符
               occ.erase(s[i - 1]);
          }
           while (rk + 1 < n && !occ.count(s[rk + 1])) {
               // 不断地移动右指针
               occ.insert(s[rk + 1]);
               ++rk;
          }
           // 第 i 到 rk 个字符是一个极长的无重复字符子串
           ans = max(ans, rk - i + 1);
      }
       return ans;
  }
};

滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

https://leetcode-cn.com/problems/sliding-window-maximum/

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
class Solution {
public:
   vector<int> maxSlidingWindow(vector<int>& nums, int k) {
       vector<int> ans;
       int n = nums.size();
       if(n==0){return ans;}
       priority_queue<pair<int, int>> q;
       for (int i = 0; i < k; ++i) {
           q.emplace(nums[i], i);
      }
        ans = {q.top().first};
       for (int i = k; i < n; ++i) {
           q.emplace(nums[i], i);
           while (q.top().second <= i - k) {
               q.pop();
          }
           ans.push_back(q.top().first);
      }
       return ans;
  }
};



class Solution {
public:
   vector<int> maxSlidingWindow(vector<int>& nums, int k) {
       int n = nums.size();
       deque<int> q;
       for (int i = 0; i < k; ++i) {
           while (!q.empty() && nums[i] >= nums[q.back()]) {
               q.pop_back();
          }
           q.push_back(i);
      }

       vector<int> ans = {nums[q.front()]};
       for (int i = k; i < n; ++i) {
           while (!q.empty() && nums[i] >= nums[q.back()]) {
               q.pop_back();
          }
           q.push_back(i);
           while (q.front() <= i - k) {
               q.pop_front();
          }
           ans.push_back(nums[q.front()]);
      }
       return ans;
  }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/sliding-window-maximum/solution/hua-dong-chuang-kou-zui-da-zhi-by-leetco-ki6m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

用两个栈实现队列,队尾删除,队首插入

class CQueue {
public:
   stack<int> begin;
   stack<int> end;
   CQueue() {
  }
   
   void appendTail(int value) {
       begin.push(value);
  }
   
   int deleteHead() {
       if(!end.empty()){
           int a = end.top();
           end.pop();
           return a;
      }
       if(begin.empty()) return -1;
       while(!begin.empty()){
           int a = begin.top();
           end.push(a);
           begin.pop();
      }
       int a = end.top();
       end.pop();
       return a;

  }
};

走方格

矩阵中的路径

剑指 Offer 12. 矩阵中的路径

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58wowd/
class Solution {
public:
   bool exist(vector<vector<char>>& board, string word) {
       rows = board.size();
       cols = board[0].size();
       for(int i = 0; i < rows; i++) {
           for(int j = 0; j < cols; j++) {
               if(dfs(board, word, i, j, 0)) return true;
          }
      }
       return false;
  }
private:
   int rows, cols;
   bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
       if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
       if(k == word.size() - 1) return true;
       board[i][j] = '\0';
       bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
                     dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
       board[i][j] = word[k];
       return res;
  }
};

剑指 Offer 47. 礼物的最大价值

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物? 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vokvr/

输入: 
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
class Solution {
public:
   int maxValue(vector<vector<int>>& grid) {
       int m = grid.size(), n = grid[0].size();
       for(int j = 1; j < n; j++) // 初始化第一行
           grid[0][j] += grid[0][j - 1];
       for(int i = 1; i < m; i++) // 初始化第一列
           grid[i][0] += grid[i - 1][0];
       for(int i = 1; i < m; i++)
           for(int j = 1; j < n; j++)
               grid[i][j] += max(grid[i][j - 1], grid[i - 1][j]);
       return grid[m - 1][n - 1];
  }
};

机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子? 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9h6vo2/

示例 1:
输入:m = 2, n = 3, k = 1
输出:3
//方法一:DFS
class Solution {
public:
   int movingCount(int m, int n, int k) {
       vector<vector<bool>> visited(m, vector<bool>(n, 0));
       return dfs(0, 0, 0, 0, visited, m, n, k);
  }
private:
   int dfs(int i, int j, int si, int sj, vector<vector<bool>> &visited, int m, int n, int k) {
       if(i >= m || j >= n || k < si + sj || visited[i][j]) return 0;
       visited[i][j] = true;
       return 1 + dfs(i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj, visited, m, n, k) +
                  dfs(i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8, visited, m, n, k);
  }
};
//方法二BFS
class Solution {
public:
   int movingCount(int m, int n, int k) {
       vector<vector<bool>> visited(m, vector<bool>(n, 0));
       int res = 0;
       queue<vector<int>> que;
       que.push({ 0, 0, 0, 0 });
       while(que.size() > 0) {
           vector<int> x = que.front();
           que.pop();
           int i = x[0], j = x[1], si = x[2], sj = x[3];
           if(i >= m || j >= n || k < si + sj || visited[i][j]) continue;
           visited[i][j] = true;
           res++;
           que.push({ i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj });
           que.push({ i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8 });
      }
       return res;
  }
};


链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9hka9c/

 

数组

数组的 4 种操作

 

丑数

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9h3im5/
class Solution {
public:
   int nthUglyNumber(int n) {
       int a = 0, b = 0, c = 0;
       int dp[n];
       dp[0] = 1;
       for(int i = 1; i < n; i++){
           int n2 = dp[a]*2, n3 = dp[b]*3, n5 = dp[c] * 5;
           dp[i] = min(min(n2,n3),n5);
           if(dp[i] == n2) a++;
           if(dp[i] == n3) b++;
           if(dp[i] == n5) c++;
      }
       return dp[n - 1];
     
  }
};

剑指 Offer 60. n 个骰子的点数

把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。

你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/ozzl1r/

输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/ozzl1r/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
   vector<double> dicesProbability(int n) {

       vector<double> dp(6, 1.0 / 6.0);
       for (int i = 2; i <= n; i++) {
           vector<double> tmp(5 * i + 1, 0);

           for (int j = 0; j < dp.size(); j++) {
               for (int k = 0; k < 6; k++) {
                   tmp[j + k] += dp[j] / 6.0;
                   cout<<"j+k   :"<<j+k<<" tmp[j+k]"<< tmp[j+k] <<endl;
              }
          }
           dp = tmp;
      }
       return dp;
  }
};

买卖股票的最大利润

class Solution {
public:
   int maxProfit(vector<int>& prices) {
       int cost = INT_MAX, profit = 0;
       for(int price : prices) {
           cost = min(cost, price);
           profit = max(profit, price - cost);
      }
       return profit;
  }
};


链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58vmds/

数组中的逆序对

二分加归并

class Solution {
public:
   int reversePairs(vector<int>& nums) {
       vector <int> tmp(nums.size());
       return mergeSort(0,nums.size() - 1, nums ,tmp);
  }
private:
   int mergeSort(int l, int r, vector<int>& nums, vector<int>& tmp) {
       // 终止条件
       if( l >= r) return 0;
       int m = (l + r) /2;
       int res = mergeSort(1 , m ,nums ,tmp) + mergeSort(m + 1, r, nums ,tmp);
       int i = l;
       int j = m + 1;
       for(int k = l; k <= r; k++){
           tmp[k] = nums[k];
      }
       for(int k = l; k <= r; k++){
           if(j == r + 1 || tmp[i] <= tmp[j]){
               nums[k] = tmp[i++];
          }
           if(i == m + 1){
               nums[k] = tmp[j++];
          }
           else{
               nums[k] = tmp[j++];
               res += m - i + 1; // 统计逆序对
          }
      }
       return res;
  }
};

一个数组是否为二叉搜索树的后序遍历序列

//栈
class Solution {
public:
   bool verifyPostorder(vector<int>& postorder) {
       stack<int> stk;
       int root = INT_MAX;
       for(int i = postorder.size() - 1; i >= 0; i--) {
           if(postorder[i] > root) return false;
           while(!stk.empty() && stk.top() > postorder[i]) {
               root = stk.top();
               stk.pop();
          }
           stk.push(postorder[i]);
      }
       return true;
  }
};
//递推分治
class Solution {
public:
   bool verifyPostorder(vector<int>& postorder) {
       return recur(postorder, 0, postorder.size() - 1);
  }
private:
   bool recur(vector<int>& postorder, int i, int j) {
       if(i >= j) return true;
       int p = i;
       while(postorder[p] < postorder[j]) p++;
       int m = p;
       while(postorder[p] > postorder[j]) p++;
       return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
  }
};

数据流中的中位数

class MedianFinder {
public:
   priority_queue<int, vector<int>, greater<int>> A; // 小顶堆,保存较大的一半
   priority_queue<int, vector<int>, less<int>> B; // 大顶堆,保存较小的一半
   MedianFinder() { }
   void addNum(int num) {
       if(A.size() != B.size()) {
           A.push(num);
           B.push(A.top());
           A.pop();
      } else {
           B.push(num);
           A.push(B.top());
           B.pop();
      }
  }
   double findMedian() {
       return A.size() != B.size() ? A.top() : (A.top() + B.top()) / 2.0;
  }
};

把数组排成最小的数

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

示例 1:

输入: [10,2]
输出: "102"
class Solution {
public:
   string minNumber(vector<int>& nums) {
       vector<string> strs;
       for(int i = 0; i < nums.size(); i++)
           strs.push_back(to_string(nums[i]));
       quickSort(strs, 0, strs.size() - 1);
       string res;
       for(string s : strs)
           res.append(s);
       return res;
  }
private:
   void quickSort(vector<string>& strs, int l, int r) {
       if(l >= r) return;
       int i = l, j = r;
       while(i < j) {
           while(strs[j] + strs[l] >= strs[l] + strs[j] && i < j) j--;
           while(strs[i] + strs[l] <= strs[l] + strs[i] && i < j) i++;
           swap(strs[i], strs[j]);
      }
       swap(strs[i], strs[l]);
       quickSort(strs, l, i - 1);
       quickSort(strs, i + 1, r);
  }
};

数组中出现的次数

//1哈希表
class Solution {
public:
   int majorityElement(vector<int>& nums) {
       unordered_map<int, int> counts;
       int majority = 0, cnt = 0;
       for (int num: nums) {
           ++counts[num];
           if (counts[num] > cnt) {
               majority = num;
               cnt = counts[num];
          }
      }
       return majority;
  }
};


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/majority-element/solution/duo-shu-yuan-su-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
//2*投票
class Solution {
public:
   int majorityElement(vector<int>& nums) {
       int x = 0, votes = 0;
       for(int num : nums){
           if(votes == 0) x = num;
           votes += num == x ? 1 : -1;
      }
       return x;
  }
};

 

是否为子树

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:
给定的树 A:

    3
  / \

  4   5
/ \
1   2
给定的树 B:

  4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:

输入:A = [1,2,3], B = [3,1]
输出:false

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5dshwe/

class Solution {
public:
   bool isSubStructure(TreeNode* A, TreeNode* B) {
       return (A != nullptr && B != nullptr) && (recur(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B));
  }
private:
   bool recur(TreeNode* A, TreeNode* B) {
       if(B == nullptr) return true;
       if(A == nullptr || A->val != B->val) return false;
       return recur(A->left, B->left) && recur(A->right, B->right);
  }
};

二叉树镜像

class Solution {
public:
   bool isSymmetric(TreeNode* root) {
       return root == nullptr || recur(root->left, root->right);
  }
private:
   bool recur(TreeNode* L, TreeNode* R) {
       if(L == nullptr && R == nullptr) return true;
       if(L == nullptr || R == nullptr || L->val != R->val) return false;
       return recur(L->left, R->right) && recur(L->right, R->left);
  }
};


作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5d1zmj/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
public:
   TreeNode* mirrorTree(TreeNode* root) {
       if(root == nullptr) return nullptr;
       stack<TreeNode*> stack;
       stack.push(root);
       while (!stack.empty())
      {
           TreeNode* node = stack.top();
           stack.pop();
           if (node->left != nullptr) stack.push(node->left);
           if (node->right != nullptr) stack.push(node->right);
           TreeNode* tmp = node->left;
           node->left = node->right;
           node->right = tmp;
      }
       return root;
  }
};

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/59slxe/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


//递归
class Solution {
public:
   TreeNode* mirrorTree(TreeNode* root) {
       if(root == nullptr) return nullptr;
       TreeNode* tmp = root->left;
       cout<<root->val<<endl;
       root->left = mirrorTree(root->right);
       root->right = tmp;
       return root;
  }
};

//辅助栈
class Solution {
public:
   TreeNode* mirrorTree(TreeNode* root) {
       if(root == nullptr) return nullptr;
       stack<TreeNode*> stack;
       stack.push(root);
       while (!stack.empty())
      {
           TreeNode* node = stack.top();
           stack.pop();
           if (node->left != nullptr) stack.push(node->left);
           if (node->right != nullptr) stack.push(node->right);
           TreeNode* tmp = node->left;
           node->left = node->right;
           node->right = tmp;
      }
       return root;
  }
};

对称的二叉树

class Solution {
public:
   bool isSymmetric(TreeNode* root) {
       return root == nullptr || recur(root->left, root->right);
  }
private:
   bool recur(TreeNode* L, TreeNode* R) {
       if(L == nullptr && R == nullptr) return true;
       if(L == nullptr || R == nullptr || L->val != R->val) return false;
       return recur(L->left, R->right) && recur(L->right, R->left);
  }
};

按层遍历二叉树

从上到下打印二叉树 II

例如:
给定二叉树: [3,9,20,null,null,15,7],

  3

  / \
9 20
  / \
  15   7

返回其层次遍历结果:

[
[3],
[9,20],
[15,7]
]
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vawr3/
class Solution {
public:
   vector<vector<int>> levelOrder(TreeNode* root) {
       queue<TreeNode*> que;
       vector<vector<int>> res;
       int cnt = 0;
       if(root != NULL) que.push(root);
       while(!que.empty()) {
           vector<int> tmp;
           for(int i = que.size(); i > 0; --i) {
               root = que.front();
               que.pop();
               tmp.push_back(root->val);
               if(root->left != NULL) que.push(root->left);
               if(root->right != NULL) que.push(root->right);
          }
           res.push_back(tmp);
      }
       return res;
  }
};

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5v22om/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

树的路径

二叉树中和为某一值的路径

class Solution{
public:
vector<vector<int>> pathSum(TreeNode* root, int sum){
recur(root, sum);
return res;
}

private:
vector<vector<int>> res;
vector<int> path;
void recur(TreeNode* root, int tar){
if(root == nullptr) return;
path.push_back(root->val);
tar -= root->val;
if(tar == 0 && root->left == nullptr && root->right == nullptr)
res.push_back(path);

recur(root->left,tar);
recur(root->right,tar);
path.pop_back();
}
};

二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

为了让您更好地理解问题,以下面的二叉搜索树为例:

img

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

 

img

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5dbies/

class Solution {
public:
   Node* treeToDoublyList(Node* root) {
       if(root == nullptr) return nullptr;
       dfs(root);
       head->left = pre;
       pre->right = head;
       return head;
  }
private:
   Node *pre, *head;
   void dfs(Node* cur) {
       if(cur == nullptr) return;
       dfs(cur->left);
       if(pre != nullptr) pre->right = cur;
       else head = cur;
       cur->left = pre;
       pre = cur;
       dfs(cur->right);
  }
};

判断是否为平衡二叉树

class Solution {
public:
   bool isBalanced(TreeNode* root) {
       return recur(root) != -1;
  }
private:
   int recur(TreeNode* root) {
       if (root == nullptr) return 0;
       int left = recur(root->left);
       if(left == -1) return -1;
       int right = recur(root->right);
       if(right == -1) return -1;
       return abs(left - right) < 2 ? max(left, right) + 1 : -1;
  }
};

二叉搜索树的公共根节点

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
   TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
       if(p->val > q->val)
           swap(p, q);
       while(root != nullptr) {
           if(root->val < p->val) // p,q 都在 root 的右子树中
               root = root->right; // 遍历至右子节点
           else if(root->val > q->val) // p,q 都在 root 的左子树中
               root = root->left; // 遍历至左子节点
           else break;
      }
       return root;
  }
};

二叉树的公共根节点

class Solution {
public:
   TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
      if(root == nullptr || root == p || root == q) return root;
       TreeNode *left = lowestCommonAncestor(root->left, p, q);
       TreeNode *right = lowestCommonAncestor(root->right, p, q);
       if(left == nullptr && right == nullptr) return nullptr;
       if(left == nullptr) return right;
       if(right == nullptr) return left;
       return root;

  }
};

重建二叉树

class Solution {
public:

   TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
       if(preorder.empty() || inorder.size() != preorder.size()) return nullptr;
       int len = preorder.size();
       for(int i = 0; i < len; ++i){
           a[inorder[i]] = i;
      }
       return build(preorder, 0, len - 1, inorder, 0, len - 1);
  }
   TreeNode* build(vector<int>& preorder,int left1, int right1, vector<int>& inorder, int left2,int right2){
       if(left1 > right1) return nullptr;
       TreeNode* head = new TreeNode(preorder[left1]);
       int i = 0;
       i = a[preorder[left1]];
       if (left1 == right1) return head;
       head->left = build(preorder, left1 + 1, i - left2 + left1, inorder, left2, i - 1);
       head->right = build(preorder, i - left2 + left1 + 1, right1, inorder, i + 1, right2);
       return head;
  }
private:
   unordered_map<int, int> a;
};

排序

得到数组的第k小个数

堆排

class Solution {
public:
   vector<int> getLeastNumbers(vector<int>& arr, int k) {
       
       if(arr.empty()) return arr;
       int len = arr.size();;
       sort(arr,len);
       vector<int> m;
      for(int i  = 0; i < k; ++i){
           m.push_back(arr[i]);
      }
      return m;
  }

   void  sort(vector<int>& arg, int size){
       for(int i = size / 2 - 1; i >= 0; --i){
           create(arg , i, size);
      }

       for(int i = size - 1; i >= 0; i--){
           int tmp = arg[0];
           arg[0] = arg[i];
           arg[i] = tmp;
           create(arg, 0, i);
      }
  }

   void create(vector<int>& arg, int i, int size){
       int left = 2 * i + 1;
       int right = 2 * i + 2;
       int max = i;
       if(left < size && arg[left] > arg[max]) max = left;
       if(right < size && arg[right] > arg[max]) max = right;
       if(max != i){
           int tmp = arg[max];
           arg[max] = arg[i];
           arg[i] = tmp;
           create(arg, max, size);
      }      
  }
};

快排

void Quick_Sort(int ara[],int left,int right){

if(left < right){
 int i = left; int j = right; int tmp = ara[left];
}
while(i < j){
 while(i < j && ara[j] >= tmp){
  j--;
}
 if(i < j){
  a[i] = a[j];
  i++;
}
 while(i < j && ara[i] < tmp){
  i++;
}
 if(i < j){
  a[j] = a[i];
  j--;
}
}
a[i] = tmp;
Quick_Sort(a, left, i - 1);  
Quick_Sort(a, i + 1, right);
}

动态规划

剪绳子

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]k[1]...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5v1026/

class Solution {
public:
   int integerBreak(int n) {
       vector <int> dp(n + 1);
       for (int i = 2; i <= n; i++) {
           int curMax = 0;
           for (int j = 1; j < i; j++) {
               curMax = max(curMax, max(j * (i - j), j * dp[i - j]));
          }
           dp[i] = curMax;
      }
       return dp[n];
  }
};

数组中出现的次数

 

图:

1.传递信息

给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。 链接:https://leetcode-cn.com/problems/chuan-di-xin-xi

输入:n = 5, relation = [[0,2],[2,1],[3,4],[2,3],[1,4],[2,0],[0,4]], k = 3

输出:3

解释:信息从小 A 编号 0 处开始,经 3 轮传递,到达编号 4。共有 3 种方案,分别是 0->2->0->4, 0->2->1->4, 0->2->3->4。
//深度优先搜索
class Solution {
public:
   int numWays(int n, vector<vector<int>> &relation, int k) {
       vector<vector<int>> edges(n);
       for (auto &edge : relation) {
           int src = edge[0], dst = edge[1];
           edges[src].push_back(dst);
      }

       int ways = 0;
       function<void(int, int)> dfs = [&](int index, int steps) {
           if (steps == k) {
               if (index == n - 1) {
                   ++ways;
              }
               return;
          }
           for (int to : edges[index]) {
               dfs(to, steps + 1);
          }
      };
       dfs(0, 0);
       return ways;
  }
};

//广度优先搜索

class Solution {
public:
   int numWays(int n, vector<vector<int>> &relation, int k) {
       vector<vector<int>> edges(n);
       for (auto &edge : relation) {
           int src = edge[0], dst = edge[1];
           edges[src].push_back(dst);
      }

       int steps = 0;
       queue<int> que;
       que.push(0);
       while (!que.empty() && steps < k) {
           steps++;
           int size = que.size();
           for (int i = 0; i < size; i++) {
               int index = que.front();
               que.pop();
               for (auto &nextIndex : edges[index]) {
                   que.push(nextIndex);
              }
          }
      }

       int ways = 0;
       if (steps == k) {
           while (!que.empty()) {
               if (que.front() == n - 1) {
                   ways++;
              }
               que.pop();
          }
      }
       return ways;
  }
};

链表:

复杂链表的复制

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

方法一:

利用哈希表的查询特点,考虑构建 **原链表节点** 和 **新链表对应节点** 的键值对映射关系,再遍历构建新链表各节点的 `next` 和 `random` 引用指向即可。

 

class Solution {
public:
   Node* copyRandomList(Node* head) {
       if(head == nullptr) return nullptr;
       Node* cur = head;
       unordered_map<Node*, Node*> map;
       // 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
       while(cur != nullptr) {
           map[cur] = new Node(cur->val);
           cur = cur->next;
      }
       cur = head;
       // 4. 构建新链表的 next 和 random 指向
       while(cur != nullptr) {
           map[cur]->next = map[cur->next];
           map[cur]->random = map[cur->random];
           cur = cur->next;
      }
       // 5. 返回新链表的头节点
       return map[head];
  }
};

方法二:

image-20210806100621543

class Solution {
public:
   Node* copyRandomList(Node* head) {
       if(head == nullptr) return nullptr;
       Node* cur = head;
       // 1. 复制各节点,并构建拼接链表
       while(cur != nullptr) {
           Node* tmp = new Node(cur->val);
           tmp->next = cur->next;
           cur->next = tmp;
           cur = tmp->next;
      }
       // 2. 构建各新节点的 random 指向
       cur = head;
       while(cur != nullptr) {
           if(cur->random != nullptr)
               cur->next->random = cur->random->next;
           cur = cur->next->next;
      }
       // 3. 拆分两链表
       cur = head->next;
       Node* pre = head, *res = head->next;
       while(cur->next != nullptr) {
           pre->next = pre->next->next;
           cur->next = cur->next->next;
           pre = pre->next;
           cur = cur->next;
      }
       pre->next = nullptr; // 单独处理原链表尾节点
       return res;      // 返回新链表头节点
  }
};

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9plk45/

 

两数相加

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。 链接:https://leetcode-cn.com/problems/add-two-numbers-ii

输入:l1 = [7,2,4,3], l2 = [5,6,4]
输出:[7,8,0,7]
class Solution {
public:
   ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
       stack<int> s1, s2;
       while (l1) {
           s1.push(l1 -> val);
           l1 = l1 -> next;
      }
       while (l2) {
           s2.push(l2 -> val);
           l2 = l2 -> next;
      }
       int carry = 0;
       ListNode* ans = nullptr;
       while (!s1.empty() or !s2.empty() or carry != 0) {
           int a = s1.empty() ? 0 : s1.top();
           int b = s2.empty() ? 0 : s2.top();
           if (!s1.empty()) s1.pop();
           if (!s2.empty()) s2.pop();
           int cur = a + b + carry;
           carry = cur / 10;
           cur %= 10;
           auto curnode = new ListNode(cur);
           curnode -> next = ans;
           ans = curnode;
      }
       return ans;
  }
};

翻转链表

class Solution {
public:
   ListNode* reverseList(ListNode* head) {
       ListNode* cur = head;
       ListNode* pre = NULL;
       while(cur != NULL){
           ListNode* tmp = cur->next;
           cur->next = pre;
           pre = cur;
           cur = tmp;
      }
       return pre;
  }
};

 

字符串

两字符串相加

给定两个字符串形式的非负整数 num1num2 ,计算它们的和。

class Solution {
public:
   string addStrings(string num1, string num2) {
       int i = num1.length() - 1, j = num2.length() - 1, add = 0;
       string ans = "";
       while (i >= 0 || j >= 0 || add != 0) {
           int x = i >= 0 ? num1[i] - '0' : 0;
           int y = j >= 0 ? num2[j] - '0' : 0;
           int result = x + y + add;
           ans.push_back('0' + result % 10);
           add = result / 10;
           i -= 1;
           j -= 1;
      }
       // 计算完以后的答案需要翻转过来
       reverse(ans.begin(), ans.end());
       return ans;
  }
};

字符串翻转

例如:字符串s = s1 + s2;

image-20210806093529107

class Solution {
public:
   string reverseLeftWords(string s, int n) {
       reverseString(s, 0, n - 1);
       reverseString(s, n, s.size() - 1);
       reverseString(s, 0, s.size() - 1);
       return s;
  }
private:
   void reverseString(string& s, int i, int j) {
       while(i < j) swap(s[i++], s[j--]);
  }
};

 

 

有效的括号

class Solution {
public:
   bool isValid(string s) {
       int n = s.size();
       if (n % 2 == 1) {
           return false;
      }

       unordered_map<char, char> pairs = {
          {')', '('},
          {']', '['},
          {'}', '{'}
      };
       stack<char> stk;
       for (char ch: s) {
           if (pairs.count(ch)) {
               if (stk.empty() || stk.top() != pairs[ch]) {
                   return false;
              }
               stk.pop();
          }
           else {
               stk.push(ch);
          }
      }
       return stk.empty();
  }
};

把数字变成字符串(a-z 对应 0-25)有多少种组成方法

image-20210806221531606

class Solution {
public:
   int translateNum(int num) {
       int a = 1, b = 1, x, y = num % 10;
       while(num > 9) {
           num /= 10;
           x = num % 10;
           int tmp = 10 * x + y;
           int c = (tmp >= 10 && tmp <= 25) ? a + b : a;
           b = a;
           a = c;
           y = x;
      }
       return a;
  }
};

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/99dnh6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最长不含重复字符的字符串

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
class Solution {
public:
   int lengthOfLongestSubstring(string s) {
       unordered_map<char, int> dic;
       int i = -1, res = 0, len = s.size();
       for(int j = 0; j < len; j++) {
           if(dic.find(s[j]) != dic.end())
               i = max(i, dic.find(s[j])->second); // 更新左指针
           dic[s[j]] = j; // 哈希表记录
           res = max(res, j - i); // 更新结果
      }
       return res;
  }
};

字符串的排列

class Solution {
public:
   vector<string> permutation(string s) {
       dfs(s, 0);
       return res;
  }
private:
   vector<string> res;
   void dfs(string s, int x) {
       if(x == s.size() - 1) {
           res.push_back(s);                       // 添加排列方案
           return;
      }
       set<int> st;
       for(int i = x; i < s.size(); i++) {
           if(st.find(s[i]) != st.end()) continue; // 重复,因此剪枝
           st.insert(s[i]);
           swap(s[i], s[x]);                       // 交换,将 s[i] 固定在第 x 位
           dfs(s, x + 1);                          // 开启固定第 x + 1 位字符
           swap(s[i], s[x]);                       // 恢复交换
      }
  }
};


作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/50hah3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

LRU缓存算法

设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。

它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 链接:https://leetcode-cn.com/problems/lru-cache-lcci

struct DLinkedNode {
   int key, value;
   DLinkedNode* prev;
   DLinkedNode* next;
   DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
   DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};

class LRUCache {
private:
   unordered_map<int, DLinkedNode*> cache;
   DLinkedNode* head;
   DLinkedNode* tail;
   int size;
   int capacity;

public:
   LRUCache(int _capacity): capacity(_capacity), size(0) {
       // 使用伪头部和伪尾部节点
       head = new DLinkedNode();
       tail = new DLinkedNode();
       head->next = tail;
       tail->prev = head;
  }
   
   int get(int key) {
       if (!cache.count(key)) {
           return -1;
      }
       // 如果 key 存在,先通过哈希表定位,再移到头部
       DLinkedNode* node = cache[key];
       moveToHead(node);
       return node->value;
  }
   
   void put(int key, int value) {
       if (!cache.count(key)) {
           // 如果 key 不存在,创建一个新的节点
           DLinkedNode* node = new DLinkedNode(key, value);
           // 添加进哈希表
           cache[key] = node;
           // 添加至双向链表的头部
           addToHead(node);
           ++size;
           if (size > capacity) {
               // 如果超出容量,删除双向链表的尾部节点
               DLinkedNode* removed = removeTail();
               // 删除哈希表中对应的项
               cache.erase(removed->key);
               // 防止内存泄漏
               delete removed;
               --size;
          }
      }
       else {
           // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
           DLinkedNode* node = cache[key];
           node->value = value;
           moveToHead(node);
      }
  }

   void addToHead(DLinkedNode* node) {
       node->prev = head;
       node->next = head->next;
       head->next->prev = node;
       head->next = node;
  }
   
   void removeNode(DLinkedNode* node) {
       node->prev->next = node->next;
       node->next->prev = node->prev;
  }

   void moveToHead(DLinkedNode* node) {
       removeNode(node);
       addToHead(node);
  }

   DLinkedNode* removeTail() {
       DLinkedNode* node = tail->prev;
       removeNode(node);
       return node;
  }
};

链接:https://leetcode-cn.com/problems/lru-cache-lcci/solution/lruhuan-cun-by-leetcode-solution/

2021.08.03

数组查找

一个n+1的数组,乱序,只有一个重复的数字,把他找出来(不允许排序、不允许使用额外空间)

class Solution {
public:
int duplicateInArray(vector<int>& nums) {
  if(nums.empty()) return -1;
  int n = nums.size();
 
  for(int i = 0 ; i < n ; i ++)
  {
      if(nums[i] < 0 || nums[i] > n-1) return -1;
  }
 
  for(int i = 0 ; i < n ; i++)
  {
      while(nums[i] != i)  //按照一个个坑进行排序
      {
      //要先进行判断再排序
           if(nums[i] == nums[nums[i]]) return nums[i];  //这个数不仅在它原本的坑上出现,还在其他坑上也出现
           swap(nums[i],nums[nums[i]]);    //交换,以满足按坑排序
      }  
  }
return -1;
}
};

替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例:

输入:s = "We are happy."
输出:"We%20are%20happy."

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/50ywkd/

class Solution {
public:
   string replaceSpace(string s) {
       string result;
       for(int i = 0; i <= s.size(); ++i){
           if( s[i] != ' '){
               result += s[i];

          }
           else {
               result += '%';
               result += '2';
               result += '0';
          }
      }
       return result;

  }
};

从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

输入:head = [1,3,2]
输出:[2,3,1]

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5dt66m/

/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
   vector<int> reversePrint(ListNode* head) {
       dfs(head);
       return res;
  }

   void dfs(ListNode* head){
       if(head == NULL) return ;
       dfs(head->next);
       res.push_back(head->val);
  }
private:
   vector <int> res;

};

滑动窗口:

无重复的字符的最长字串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
class Solution {
public:
   int lengthOfLongestSubstring(string s) {
       // 哈希集合,记录每个字符是否出现过
       unordered_set<char> occ;
       int n = s.size();
       // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
       int rk = -1, ans = 0;
       // 枚举左指针的位置,初始值隐性地表示为 -1
       for (int i = 0; i < n; ++i) {
           if (i != 0) {
               // 左指针向右移动一格,移除一个字符
               occ.erase(s[i - 1]);
          }
           while (rk + 1 < n && !occ.count(s[rk + 1])) {
               // 不断地移动右指针
               occ.insert(s[rk + 1]);
               ++rk;
          }
           // 第 i 到 rk 个字符是一个极长的无重复字符子串
           ans = max(ans, rk - i + 1);
      }
       return ans;
  }
};

滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

https://leetcode-cn.com/problems/sliding-window-maximum/

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
class Solution {
public:
   vector<int> maxSlidingWindow(vector<int>& nums, int k) {
       vector<int> ans;
       int n = nums.size();
       if(n==0){return ans;}
       priority_queue<pair<int, int>> q;
       for (int i = 0; i < k; ++i) {
           q.emplace(nums[i], i);
      }
        ans = {q.top().first};
       for (int i = k; i < n; ++i) {
           q.emplace(nums[i], i);
           while (q.top().second <= i - k) {
               q.pop();
          }
           ans.push_back(q.top().first);
      }
       return ans;
  }
};



class Solution {
public:
   vector<int> maxSlidingWindow(vector<int>& nums, int k) {
       int n = nums.size();
       deque<int> q;
       for (int i = 0; i < k; ++i) {
           while (!q.empty() && nums[i] >= nums[q.back()]) {
               q.pop_back();
          }
           q.push_back(i);
      }

       vector<int> ans = {nums[q.front()]};
       for (int i = k; i < n; ++i) {
           while (!q.empty() && nums[i] >= nums[q.back()]) {
               q.pop_back();
          }
           q.push_back(i);
           while (q.front() <= i - k) {
               q.pop_front();
          }
           ans.push_back(nums[q.front()]);
      }
       return ans;
  }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/sliding-window-maximum/solution/hua-dong-chuang-kou-zui-da-zhi-by-leetco-ki6m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

用两个栈实现队列,队尾删除,队首插入

class CQueue {
public:
   stack<int> begin;
   stack<int> end;
   CQueue() {
  }
   
   void appendTail(int value) {
       begin.push(value);
  }
   
   int deleteHead() {
       if(!end.empty()){
           int a = end.top();
           end.pop();
           return a;
      }
       if(begin.empty()) return -1;
       while(!begin.empty()){
           int a = begin.top();
           end.push(a);
           begin.pop();
      }
       int a = end.top();
       end.pop();
       return a;

  }
};

走方格

矩阵中的路径

剑指 Offer 12. 矩阵中的路径

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58wowd/
class Solution {
public:
   bool exist(vector<vector<char>>& board, string word) {
       rows = board.size();
       cols = board[0].size();
       for(int i = 0; i < rows; i++) {
           for(int j = 0; j < cols; j++) {
               if(dfs(board, word, i, j, 0)) return true;
          }
      }
       return false;
  }
private:
   int rows, cols;
   bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
       if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
       if(k == word.size() - 1) return true;
       board[i][j] = '\0';
       bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
                     dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
       board[i][j] = word[k];
       return res;
  }
};

剑指 Offer 47. 礼物的最大价值

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物? 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vokvr/

输入: 
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
class Solution {
public:
   int maxValue(vector<vector<int>>& grid) {
       int m = grid.size(), n = grid[0].size();
       for(int j = 1; j < n; j++) // 初始化第一行
           grid[0][j] += grid[0][j - 1];
       for(int i = 1; i < m; i++) // 初始化第一列
           grid[i][0] += grid[i - 1][0];
       for(int i = 1; i < m; i++)
           for(int j = 1; j < n; j++)
               grid[i][j] += max(grid[i][j - 1], grid[i - 1][j]);
       return grid[m - 1][n - 1];
  }
};

机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子? 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9h6vo2/

示例 1:
输入:m = 2, n = 3, k = 1
输出:3
//方法一:DFS
class Solution {
public:
   int movingCount(int m, int n, int k) {
       vector<vector<bool>> visited(m, vector<bool>(n, 0));
       return dfs(0, 0, 0, 0, visited, m, n, k);
  }
private:
   int dfs(int i, int j, int si, int sj, vector<vector<bool>> &visited, int m, int n, int k) {
       if(i >= m || j >= n || k < si + sj || visited[i][j]) return 0;
       visited[i][j] = true;
       return 1 + dfs(i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj, visited, m, n, k) +
                  dfs(i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8, visited, m, n, k);
  }
};
//方法二BFS
class Solution {
public:
   int movingCount(int m, int n, int k) {
       vector<vector<bool>> visited(m, vector<bool>(n, 0));
       int res = 0;
       queue<vector<int>> que;
       que.push({ 0, 0, 0, 0 });
       while(que.size() > 0) {
           vector<int> x = que.front();
           que.pop();
           int i = x[0], j = x[1], si = x[2], sj = x[3];
           if(i >= m || j >= n || k < si + sj || visited[i][j]) continue;
           visited[i][j] = true;
           res++;
           que.push({ i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj });
           que.push({ i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8 });
      }
       return res;
  }
};


链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9hka9c/

 

数组

数组的 4 种操作

 

丑数

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9h3im5/
class Solution {
public:
   int nthUglyNumber(int n) {
       int a = 0, b = 0, c = 0;
       int dp[n];
       dp[0] = 1;
       for(int i = 1; i < n; i++){
           int n2 = dp[a]*2, n3 = dp[b]*3, n5 = dp[c] * 5;
           dp[i] = min(min(n2,n3),n5);
           if(dp[i] == n2) a++;
           if(dp[i] == n3) b++;
           if(dp[i] == n5) c++;
      }
       return dp[n - 1];
     
  }
};

剑指 Offer 60. n 个骰子的点数

把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。

你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/ozzl1r/

输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/ozzl1r/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
   vector<double> dicesProbability(int n) {

       vector<double> dp(6, 1.0 / 6.0);
       for (int i = 2; i <= n; i++) {
           vector<double> tmp(5 * i + 1, 0);

           for (int j = 0; j < dp.size(); j++) {
               for (int k = 0; k < 6; k++) {
                   tmp[j + k] += dp[j] / 6.0;
                   cout<<"j+k   :"<<j+k<<" tmp[j+k]"<< tmp[j+k] <<endl;
              }
          }
           dp = tmp;
      }
       return dp;
  }
};

买卖股票的最大利润

class Solution {
public:
   int maxProfit(vector<int>& prices) {
       int cost = INT_MAX, profit = 0;
       for(int price : prices) {
           cost = min(cost, price);
           profit = max(profit, price - cost);
      }
       return profit;
  }
};


链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58vmds/

数组中的逆序对

二分加归并

class Solution {
public:
   int reversePairs(vector<int>& nums) {
       vector <int> tmp(nums.size());
       return mergeSort(0,nums.size() - 1, nums ,tmp);
  }
private:
   int mergeSort(int l, int r, vector<int>& nums, vector<int>& tmp) {
       // 终止条件
       if( l >= r) return 0;
       int m = (l + r) /2;
       int res = mergeSort(1 , m ,nums ,tmp) + mergeSort(m + 1, r, nums ,tmp);
       int i = l;
       int j = m + 1;
       for(int k = l; k <= r; k++){
           tmp[k] = nums[k];
      }
       for(int k = l; k <= r; k++){
           if(j == r + 1 || tmp[i] <= tmp[j]){
               nums[k] = tmp[i++];
          }
           if(i == m + 1){
               nums[k] = tmp[j++];
          }
           else{
               nums[k] = tmp[j++];
               res += m - i + 1; // 统计逆序对
          }
      }
       return res;
  }
};

一个数组是否为二叉搜索树的后序遍历序列

//栈
class Solution {
public:
   bool verifyPostorder(vector<int>& postorder) {
       stack<int> stk;
       int root = INT_MAX;
       for(int i = postorder.size() - 1; i >= 0; i--) {
           if(postorder[i] > root) return false;
           while(!stk.empty() && stk.top() > postorder[i]) {
               root = stk.top();
               stk.pop();
          }
           stk.push(postorder[i]);
      }
       return true;
  }
};
//递推分治
class Solution {
public:
   bool verifyPostorder(vector<int>& postorder) {
       return recur(postorder, 0, postorder.size() - 1);
  }
private:
   bool recur(vector<int>& postorder, int i, int j) {
       if(i >= j) return true;
       int p = i;
       while(postorder[p] < postorder[j]) p++;
       int m = p;
       while(postorder[p] > postorder[j]) p++;
       return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
  }
};

数据流中的中位数

class MedianFinder {
public:
   priority_queue<int, vector<int>, greater<int>> A; // 小顶堆,保存较大的一半
   priority_queue<int, vector<int>, less<int>> B; // 大顶堆,保存较小的一半
   MedianFinder() { }
   void addNum(int num) {
       if(A.size() != B.size()) {
           A.push(num);
           B.push(A.top());
           A.pop();
      } else {
           B.push(num);
           A.push(B.top());
           B.pop();
      }
  }
   double findMedian() {
       return A.size() != B.size() ? A.top() : (A.top() + B.top()) / 2.0;
  }
};

把数组排成最小的数

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

示例 1:

输入: [10,2]
输出: "102"
class Solution {
public:
   string minNumber(vector<int>& nums) {
       vector<string> strs;
       for(int i = 0; i < nums.size(); i++)
           strs.push_back(to_string(nums[i]));
       quickSort(strs, 0, strs.size() - 1);
       string res;
       for(string s : strs)
           res.append(s);
       return res;
  }
private:
   void quickSort(vector<string>& strs, int l, int r) {
       if(l >= r) return;
       int i = l, j = r;
       while(i < j) {
           while(strs[j] + strs[l] >= strs[l] + strs[j] && i < j) j--;
           while(strs[i] + strs[l] <= strs[l] + strs[i] && i < j) i++;
           swap(strs[i], strs[j]);
      }
       swap(strs[i], strs[l]);
       quickSort(strs, l, i - 1);
       quickSort(strs, i + 1, r);
  }
};

数组中出现的次数

//1哈希表
class Solution {
public:
   int majorityElement(vector<int>& nums) {
       unordered_map<int, int> counts;
       int majority = 0, cnt = 0;
       for (int num: nums) {
           ++counts[num];
           if (counts[num] > cnt) {
               majority = num;
               cnt = counts[num];
          }
      }
       return majority;
  }
};


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/majority-element/solution/duo-shu-yuan-su-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
//2*投票
class Solution {
public:
   int majorityElement(vector<int>& nums) {
       int x = 0, votes = 0;
       for(int num : nums){
           if(votes == 0) x = num;
           votes += num == x ? 1 : -1;
      }
       return x;
  }
};

 

是否为子树

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:
给定的树 A:

    3
  / \

  4   5
/ \
1   2
给定的树 B:

  4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:

输入:A = [1,2,3], B = [3,1]
输出:false

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5dshwe/

class Solution {
public:
   bool isSubStructure(TreeNode* A, TreeNode* B) {
       return (A != nullptr && B != nullptr) && (recur(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B));
  }
private:
   bool recur(TreeNode* A, TreeNode* B) {
       if(B == nullptr) return true;
       if(A == nullptr || A->val != B->val) return false;
       return recur(A->left, B->left) && recur(A->right, B->right);
  }
};

二叉树镜像

class Solution {
public:
   bool isSymmetric(TreeNode* root) {
       return root == nullptr || recur(root->left, root->right);
  }
private:
   bool recur(TreeNode* L, TreeNode* R) {
       if(L == nullptr && R == nullptr) return true;
       if(L == nullptr || R == nullptr || L->val != R->val) return false;
       return recur(L->left, R->right) && recur(L->right, R->left);
  }
};


作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5d1zmj/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
public:
   TreeNode* mirrorTree(TreeNode* root) {
       if(root == nullptr) return nullptr;
       stack<TreeNode*> stack;
       stack.push(root);
       while (!stack.empty())
      {
           TreeNode* node = stack.top();
           stack.pop();
           if (node->left != nullptr) stack.push(node->left);
           if (node->right != nullptr) stack.push(node->right);
           TreeNode* tmp = node->left;
           node->left = node->right;
           node->right = tmp;
      }
       return root;
  }
};

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/59slxe/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


//递归
class Solution {
public:
   TreeNode* mirrorTree(TreeNode* root) {
       if(root == nullptr) return nullptr;
       TreeNode* tmp = root->left;
       cout<<root->val<<endl;
       root->left = mirrorTree(root->right);
       root->right = tmp;
       return root;
  }
};

//辅助栈
class Solution {
public:
   TreeNode* mirrorTree(TreeNode* root) {
       if(root == nullptr) return nullptr;
       stack<TreeNode*> stack;
       stack.push(root);
       while (!stack.empty())
      {
           TreeNode* node = stack.top();
           stack.pop();
           if (node->left != nullptr) stack.push(node->left);
           if (node->right != nullptr) stack.push(node->right);
           TreeNode* tmp = node->left;
           node->left = node->right;
           node->right = tmp;
      }
       return root;
  }
};

对称的二叉树

class Solution {
public:
   bool isSymmetric(TreeNode* root) {
       return root == nullptr || recur(root->left, root->right);
  }
private:
   bool recur(TreeNode* L, TreeNode* R) {
       if(L == nullptr && R == nullptr) return true;
       if(L == nullptr || R == nullptr || L->val != R->val) return false;
       return recur(L->left, R->right) && recur(L->right, R->left);
  }
};

按层遍历二叉树

从上到下打印二叉树 II

例如:
给定二叉树: [3,9,20,null,null,15,7],

  3

  / \
9 20
  / \
  15   7

返回其层次遍历结果:

[
[3],
[9,20],
[15,7]
]
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vawr3/
class Solution {
public:
   vector<vector<int>> levelOrder(TreeNode* root) {
       queue<TreeNode*> que;
       vector<vector<int>> res;
       int cnt = 0;
       if(root != NULL) que.push(root);
       while(!que.empty()) {
           vector<int> tmp;
           for(int i = que.size(); i > 0; --i) {
               root = que.front();
               que.pop();
               tmp.push_back(root->val);
               if(root->left != NULL) que.push(root->left);
               if(root->right != NULL) que.push(root->right);
          }
           res.push_back(tmp);
      }
       return res;
  }
};

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5v22om/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

树的路径

二叉树中和为某一值的路径

class Solution{
public:
vector<vector<int>> pathSum(TreeNode* root, int sum){
recur(root, sum);
return res;
}

private:
vector<vector<int>> res;
vector<int> path;
void recur(TreeNode* root, int tar){
if(root == nullptr) return;
path.push_back(root->val);
tar -= root->val;
if(tar == 0 && root->left == nullptr && root->right == nullptr)
res.push_back(path);

recur(root->left,tar);
recur(root->right,tar);
path.pop_back();
}
};

二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

为了让您更好地理解问题,以下面的二叉搜索树为例:

img

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

 

img

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5dbies/

class Solution {
public:
   Node* treeToDoublyList(Node* root) {
       if(root == nullptr) return nullptr;
       dfs(root);
       head->left = pre;
       pre->right = head;
       return head;
  }
private:
   Node *pre, *head;
   void dfs(Node* cur) {
       if(cur == nullptr) return;
       dfs(cur->left);
       if(pre != nullptr) pre->right = cur;
       else head = cur;
       cur->left = pre;
       pre = cur;
       dfs(cur->right);
  }
};

判断是否为平衡二叉树

class Solution {
public:
   bool isBalanced(TreeNode* root) {
       return recur(root) != -1;
  }
private:
   int recur(TreeNode* root) {
       if (root == nullptr) return 0;
       int left = recur(root->left);
       if(left == -1) return -1;
       int right = recur(root->right);
       if(right == -1) return -1;
       return abs(left - right) < 2 ? max(left, right) + 1 : -1;
  }
};

二叉搜索树的公共根节点

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
   TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
       if(p->val > q->val)
           swap(p, q);
       while(root != nullptr) {
           if(root->val < p->val) // p,q 都在 root 的右子树中
               root = root->right; // 遍历至右子节点
           else if(root->val > q->val) // p,q 都在 root 的左子树中
               root = root->left; // 遍历至左子节点
           else break;
      }
       return root;
  }
};

二叉树的公共根节点

class Solution {
public:
   TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
      if(root == nullptr || root == p || root == q) return root;
       TreeNode *left = lowestCommonAncestor(root->left, p, q);
       TreeNode *right = lowestCommonAncestor(root->right, p, q);
       if(left == nullptr && right == nullptr) return nullptr;
       if(left == nullptr) return right;
       if(right == nullptr) return left;
       return root;

  }
};

重建二叉树

class Solution {
public:

   TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
       if(preorder.empty() || inorder.size() != preorder.size()) return nullptr;
       int len = preorder.size();
       for(int i = 0; i < len; ++i){
           a[inorder[i]] = i;
      }
       return build(preorder, 0, len - 1, inorder, 0, len - 1);
  }
   TreeNode* build(vector<int>& preorder,int left1, int right1, vector<int>& inorder, int left2,int right2){
       if(left1 > right1) return nullptr;
       TreeNode* head = new TreeNode(preorder[left1]);
       int i = 0;
       i = a[preorder[left1]];
       if (left1 == right1) return head;
       head->left = build(preorder, left1 + 1, i - left2 + left1, inorder, left2, i - 1);
       head->right = build(preorder, i - left2 + left1 + 1, right1, inorder, i + 1, right2);
       return head;
  }
private:
   unordered_map<int, int> a;
};

排序

得到数组的第k小个数

堆排

class Solution {
public:
   vector<int> getLeastNumbers(vector<int>& arr, int k) {
       
       if(arr.empty()) return arr;
       int len = arr.size();;
       sort(arr,len);
       vector<int> m;
      for(int i  = 0; i < k; ++i){
           m.push_back(arr[i]);
      }
      return m;
  }

   void  sort(vector<int>& arg, int size){
       for(int i = size / 2 - 1; i >= 0; --i){
           create(arg , i, size);
      }

       for(int i = size - 1; i >= 0; i--){
           int tmp = arg[0];
           arg[0] = arg[i];
           arg[i] = tmp;
           create(arg, 0, i);
      }
  }

   void create(vector<int>& arg, int i, int size){
       int left = 2 * i + 1;
       int right = 2 * i + 2;
       int max = i;
       if(left < size && arg[left] > arg[max]) max = left;
       if(right < size && arg[right] > arg[max]) max = right;
       if(max != i){
           int tmp = arg[max];
           arg[max] = arg[i];
           arg[i] = tmp;
           create(arg, max, size);
      }      
  }
};

快排

void Quick_Sort(int ara[],int left,int right){

if(left < right){
 int i = left; int j = right; int tmp = ara[left];
}
while(i < j){
 while(i < j && ara[j] >= tmp){
  j--;
}
 if(i < j){
  a[i] = a[j];
  i++;
}
 while(i < j && ara[i] < tmp){
  i++;
}
 if(i < j){
  a[j] = a[i];
  j--;
}
}
a[i] = tmp;
Quick_Sort(a, left, i - 1);  
Quick_Sort(a, i + 1, right);
}

动态规划

剪绳子

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]k[1]...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5v1026/

class Solution {
public:
   int integerBreak(int n) {
       vector <int> dp(n + 1);
       for (int i = 2; i <= n; i++) {
           int curMax = 0;
           for (int j = 1; j < i; j++) {
               curMax = max(curMax, max(j * (i - j), j * dp[i - j]));
          }
           dp[i] = curMax;
      }
       return dp[n];
  }
};

数组中出现的次数

 

posted on 2021-09-01 20:20  花狗  阅读(65)  评论(0)    收藏  举报