算法通关之路的一些刷题笔记

算法通关之路

第2章 数学之美

1. 两数之和

  • 思路+解法

    1. 暴力 O(n^2)

    2. 排序+双指针 O(nlogn)

      class Solution {
      public:
          vector<int> twoSum(vector<int>& nums, int target) {
              int size = nums.size();
              vector<vector<int>> vec(size, vector<int>(2));
              for(int i = 0; i < size; i++){
                  vec[i][0] = nums[i];
                  vec[i][1] = i;
              }
              sort(vec.begin(), vec.end());
              int l = 0, r = size - 1;
              while(l < r){
                  if(vec[l][0] + vec[r][0] == target){
                      return vector<int>{vec[l][1], vec[r][1]};
                  }
                  else if(vec[l][0] + vec[r][0] > target){
                      r--;
                  }
                  else if(vec[l][0] + vec[r][0] < target){
                      l++;
                  }
              }
              return vector<int>{};
          }
      };
      
    3. 哈希表 O(n)

      class Solution {
      public:
          vector<int> twoSum(vector<int>& nums, int target) {
              unordered_map<int, int> m;
              vector<int> ans;
              for(int i = 0; i < nums.size(); i++){
                  if(m.find(target - nums[i]) != m.end()){
                      return vector<int>{m[target - nums[i]], i};
                  }
                  m[nums[i]] = i;
              }
              return ans;
          }
      };
      
  • 一点收获

    1. find()找的是键,不是值

15. 三数之和

  • 思路+解法

    1. 排序+双指针 O(n^2)

      class Solution {
      public:
          vector<vector<int>> threeSum(vector<int>& nums) {
              int size = nums.size();
              sort(nums.begin(), nums.end());
              vector<vector<int>> ans;
              for(int i = 0; i < size; i++){
                  //第一个元素大于0的剪枝很巧妙
                  if(nums[i] > 0) return ans;
                  //去重
                  if(i > 0 && nums[i] == nums[i-1]) continue;
                  int j = i + 1, k = size - 1;
                  while(j < k){
                      if(nums[j] + nums[k] == -nums[i]){
                          ans.push_back({nums[i], nums[j], nums[k]});
                          j++;
                          k--;
                          //去重
                          while(j < k && nums[j] == nums[j - 1]) j++;
                          while(j < k && nums[k] == nums[k + 1]) k--;
                      }
                      else if(nums[j] + nums[k] > -nums[i]){
                          k--;
                      }
                      else if(nums[j] + nums[k] < -nums[i]){
                          j++;
                      }
                  }
              }
              return ans;
          }
      };
      

18. 四数之和

  • 思路+解法

    1. 排序+双指针 O(n^3)

      class Solution {
      public:
          vector<vector<int>> fourSum(vector<int>& nums, int target) {
              sort(nums.begin(), nums.end());
              vector<vector<int>> ans;
              int size = nums.size();
              for(int i = 0; i < size; i++){
                  if(i > 0 && nums[i] == nums[i-1]) continue;
                  for(int j = i + 1; j < size; j++){
                      if(j > i + 1 && nums[j] == nums[j-1]) continue;
                      int l = j + 1, r = size - 1;
                      while(l < r){
                          //合超int范围
                          if((long long)nums[i] + nums[j] + nums[l] + nums[r] == target){
                              ans.push_back({nums[i], nums[j], nums[l], nums[r]});
                              l++;
                              r--;
                              while(l < r && nums[l] == nums[l - 1]) l++;
                              while(l < r && nums[r] == nums[r + 1]) r--;
                          }
                          else if((long long)nums[i] + nums[j] + nums[l] + nums[r] > target){
                              r--;
                          }
                          else if((long long)nums[i] + nums[j] + nums[l] + nums[r] < target){
                              l++;
                          }                
                      }
      
                  }
              }
              return ans;
          }
      };
      
    2. 一点剪枝优化(140ms->8ms)

      class Solution {
      public:
          vector<vector<int>> fourSum(vector<int>& nums, int target) {
              sort(nums.begin(), nums.end());
              vector<vector<int>> ans;
              int size = nums.size();
              for(int i = 0; i < size - 3; i++){
                  //剪枝
                  if ((long long)nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) break;
                  if ((long long)nums[i] + nums[size - 3] + nums[size - 2] + nums[size - 1] < target) continue;
                  //去重
                  if(i > 0 && nums[i] == nums[i-1]) continue;
                  for(int j = i + 1; j < size - 2; j++){
                      //剪枝
                      if ((long long)nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) break;
                      if ((long long)nums[i] + nums[j] + nums[size - 2] + nums[size - 1] < target) continue;
                      //去重
                      if(j > i + 1 && nums[j] == nums[j-1]) continue;
                      int l = j + 1, r = size - 1;
                      while(l < r){
                          //合超int范围
                          if((long long)nums[i] + nums[j] + nums[l] + nums[r] == target){
                              ans.push_back({nums[i], nums[j], nums[l], nums[r]});
                              l++;
                              r--;
                              while(l < r && nums[l] == nums[l - 1]) l++;
                              while(l < r && nums[r] == nums[r + 1]) r--;
                          }
                          else if((long long)nums[i] + nums[j] + nums[l] + nums[r] > target){
                              r--;
                          }
                          else if((long long)nums[i] + nums[j] + nums[l] + nums[r] < target){
                              l++;
                          }                
                      }
      
                  }
              }
              return ans;
          }
      };
      

454. 四数相加 II

  • 思路+解法

    1. 哈希表 O(n^2)

      class Solution {
      public:
          int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
              int ans = 0;
              unordered_map<int, int> m;
              for(auto x : nums1){
                  for(auto y : nums2){
                      m[x+y]++;
                  }
              }
              for(auto x : nums3){
                  for(auto y : nums4){
                      if(m.find(-(x + y)) != m.end()) ans += m[-(x + y)];
                  }
              }
              return ans;
          }
      };
      

16. 最接近的三数之和

  • 思路+解法

    1. 排序+双指针 O(n^2)

      class Solution {
      public:
          int threeSumClosest(vector<int>& nums, int target) {
              sort(nums.begin(), nums.end());
              int size = nums.size();
              int ans = 100000;
              for(int i = 0; i < size; i++){
                  //剪枝
                  if(i > 0 && nums[i] == nums[i - 1]) continue;
                  int l = i + 1, r = size - 1;
                  while(l < r){
                      if(nums[i] + nums[l] + nums[r] == target){
                          return target;
                      }
                      else{
                          //计算当前值是否靠近
                          if(abs(nums[i] + nums[l] + nums[r] - target) < abs(ans - target)){
                              ans = nums[i] + nums[l] + nums[r];
                          }
                          //双指针移动
                          if(nums[i] + nums[l] + nums[r] > target) {
                              r--;
                              //剪枝
                              while(l < r && nums[r] == nums[r + 1]) r--;
                          }
                          else if(nums[i] + nums[l] + nums[r] < target){
                              l++;
                              //剪枝
                              while(l < r && nums[l] == nums[l-1]) l++;
                          } 
                      }
                  }
              }
              return ans;
          }
      };
      
  • 一点收获:

    1. 和三数之和稍有不同,计算最小差值是在不等于target时计算,而不是括号外,不然会导致双指针移动不明

53. 最大子序和(*分治法)

  • 思路+解法

    1. 双指针滑动窗口 O(n)

      class Solution {
      public:
          int maxSubArray(vector<int>& nums) {
              int ans = nums[0];
              int l = 0, r = 1;
              int now = nums[0];
              while(r < nums.size()){
                  now += nums[r];
                  
                  while(l < r && now < nums[r]){
                      now -= nums[l];
                      l++;
                  }
                  r++;
                  ans = max(now, ans);
              }
              return ans;
          }
      };
      
    2. 动态规划 O(n)

      class Solution {
      public:
          int maxSubArray(vector<int>& nums) {
              int ans = nums[0];
              vector<int> dp(nums.size(), 0);
              dp[0] = nums[0];
              for(int i = 1; i < nums.size(); i++){
                  dp[i] = max(nums[i], dp[i - 1] + nums[i]);
                  ans = max(dp[i], ans);
              }
              return ans;
          }
      };
      
    3. 分治法 O(nlogn)

    4. 前缀和 O(n)

      class Solution {
      public:
          int maxSubArray(vector<int>& nums) {
              int ans = nums[0];
              int minPrefix = 0;
              int sum = 0;
              for(int i = 0; i < nums.size(); i++){
                  sum += nums[i];
                  ans = max(ans, sum - minPrefix);
                  minPrefix = min(minPrefix, sum);
              }
              return ans;
          }
      };
      
  • 一点收获

    1. 一题多解,多思考拓展思维

179. 最大数

  • 思路+解法

    1. 排序 O*(*nlognlogm)

      class Solution {
      public:
          static bool cmp(string a, string b){
              return a + b > b + a;
              
          }
          string largestNumber(vector<int>& nums) {
              vector<string> strs;
              for(int i = 0; i < nums.size(); i++){
                  strs.push_back(to_string(nums[i]));
              }
              sort(strs.begin(), strs.end(), cmp);
              string ans = "";
              for(string s : strs){
                  ans += s;
              }
              if(ans[0] == '0') return "0";
              return ans;
          }
      };
      
  • 一点收获

    1. 前导0

166. 分数到小数

  • 思路+解法:

    1. 比较余数 O(l) l是答案的长度

      class Solution {
      public:
          string fractionToDecimal(int numerator, int denominator) {
              string ans ="";
      
              if((long long)numerator * denominator < 0) ans += '-';
              long long a = abs(numerator);
              long long b = abs(denominator);
              ans += to_string(a / b);
              
              a = a % b;
              if(a != 0) ans += "."; 
      
              unordered_map<long long, int> m;
              int loc = ans.size(); //小数的第几位
              m[a] = loc++;
      
              while(a > 0){
                  a *= 10;
                  //得到的数
                  long long x = a / b;
                  ans += to_string(x);
                  //这一步的余数
                  a -= x * b;
                  if(m.find(a) != m.end()){
                      ans.insert(ans.begin() + m[a], '(');
                      ans += ')';
                      return ans;
                  }
                  m[a] = loc++;
                 
              }
              return ans;
          }
      };
      
  • 一点收获

    1. 负数转绝对值越界问题

368. 最大整除子集

  • 思路+解法

    1. 动态规划 O(n^2)

      class Solution {
      public:
          vector<int> largestDivisibleSubset(vector<int>& nums) {
              sort(nums.begin(), nums.end());
              int size = nums.size();
              vector<int> ans;
              vector<int> dp(size, 1);
              //当前位置的前一个元素
              vector<int> pre(size, -1);
              //整除子集的最大长度
              int maxlen = 1;
              int maxloc = 0;
              for(int i = 0; i < nums.size(); i++){
                  for(int j = 0; j < i; j++){
                      if(nums[i] % nums[j] == 0){
                          if(dp[j] + 1 > dp[i]){
                              dp[i] = dp[j] + 1;
                              pre[i] = j;
                          }
                      }
                  }
                  if(dp[i] > maxlen){
                      maxlen = dp[i];
                      maxloc = i;
                  }
              }
              ans.push_back(nums[maxloc]);
              int loc = pre[maxloc];
              //回溯找答案
              while(loc != -1){
                  ans.push_back(nums[loc]);
                  loc = pre[loc];
              }
              return ans;
          }
      };
      

1175. 质数排列

  • 思路+解法

    1. 埃氏筛法 O(nloglogn)

      class Solution {
      private:
          vector<int> is_prime = vector<int>(101, true);
      public:
          
          int sieve(int n){
              int p = 0;
              is_prime[0] = is_prime[1] = false;
              for(int i = 2; i <= n; i++){
                  if(is_prime[i]){
                      p++;
                      for(int j = i * 2; j <= n; j += i){
                          is_prime[j] = false;
                      }
                  }
              }
              return p;
          }
          int numPrimeArrangements(int n) {
              int k1 = sieve(n);
              int k2 = n - k1;
              long ans = 1;
              for(int i = 2; i <= k1; i++){
                  ans = (ans * i) % 1000000007;
              }
              for(int i = 2; i <= k2; i++){
                  ans = (ans * i) % 1000000007;
              }
              return ans;
          }
      };
      

第3章 回文的艺术

680. 验证回文字符串 Ⅱ

  • 思路+解法:

    1. 双指针 O(n)

      class Solution {
      public:
          bool isPalindrome(string & s, int l, int r){
              while(l < r){
                  if(s[l] != s[r]) return false;
                  l++ ,r--;
              };
              return true;
          }
          bool validPalindrome(string s) {
              int size = s.size();
              int l = 0, r = size - 1;
              int cnt = 0;
              while(l < r){
                  if(s[l] != s[r]){
                      return isPalindrome(s, l + 1, r) || isPalindrome(s, l, r - 1);
                  }
                  l++ ,r--;
              }
              return true;
          }
      };
      
  • 一点收获:

    1. 认真思考时间复杂度

234. 回文链表(*)

  • 思路+解法:

    1. 快慢指针翻转后半部分后比较 O(n)

      /**
       * Definition for singly-linked list.
       * struct ListNode {
       *     int val;
       *     ListNode *next;
       *     ListNode() : val(0), next(nullptr) {}
       *     ListNode(int x) : val(x), next(nullptr) {}
       *     ListNode(int x, ListNode *next) : val(x), next(next) {}
       * };
       */
      class Solution {
      public:
          bool isPalindrome(ListNode* head) {
              if(head->next == nullptr) return true;
      
              //快慢指针找到前半部分的结尾
              ListNode* slow = head;
              ListNode* fast = head;
              while(fast->next != nullptr && fast->next->next != nullptr){
                  fast = fast->next->next;
                  slow = slow->next;
              }
              
              //翻转后半部分
              ListNode* pre = slow->next;
              ListNode* cur = slow->next->next;
              pre->next = nullptr; //!
              while(cur != nullptr){
                  ListNode* temp = cur->next;
                  cur->next = pre;
                  pre = cur;
                  cur = temp;
              }
      
              //回文比较
              ListNode* firstHalfHead = head;
              ListNode* secondHalfHead = pre;
              while(secondHalfHead != nullptr){
                  if(firstHalfHead->val != secondHalfHead->val) return false;
                  firstHalfHead = firstHalfHead->next;
                  secondHalfHead = secondHalfHead->next;
              }
              return true;
          }
      };
      
    2. 翻转前半部分* O(n)

  • 一点收获:

    1. 链表操作

9. 回文数

  • 思路+解法:

    1. 只翻转一半数字 O(logn)

      class Solution {
      public:
          bool isPalindrome(int x) {
              if(x < 0) return false;
              if(x == 0) return true;
              if(x % 10 == 0) return false;
      
              int rev_num = 0;
              while(x > rev_num){
                  rev_num = rev_num * 10 + x % 10;
                  x /= 10; 
              }
              return x == rev_num || x == rev_num / 10;
          }
      };
      

5. 最长回文子串(*)

  • 思路+解法

    1. 动态规划 O(n^2)

      class Solution {
      public:
          string longestPalindrome(string s) {
              int n = s.size();
              //dp数组含义:从i到j的字符串是否为回文子串
              vector<vector<bool>> dp(n, vector<bool>(n, false));
              for(int i = 0; i < n; i++){
                  dp[i][i] = 0;
              }
              int max_len = 1;
              int begin = 0;
              //画表看遍历顺序
              for(int i = n - 2; i >= 0; i--){
                  for(int j = i + 1; j < n; j++){
                      if(s[i] == s[j]){
                          if(j - i <= 2){
                              dp[i][j] = true;
                          }
                          else{
                              if(dp[i + 1][j - 1]) dp[i][j] = true;
                          }
                      }
                      if(dp[i][j]){
                          if(j - i + 1 > max_len){
                              max_len = j - i + 1;
                              begin = i;
                          }
                      }
                  }
              }
              return s.substr(begin, max_len);
          }
      };
      
    2. 中心扩展 O(n^2)

      class Solution {
      public:
          int n = 0;
          int extend(string& s, int left, int right){
              while(right < n && left >= 0 && s[left] == s[right]){
                  left--;
                  right++;
              }
              return right - left - 1;
          }
          string longestPalindrome(string s) {
              n = s.size();
              int max_len = 1;
              int start = 0;
              for(int i = 0; i < n; i++){
                  int len1 ;
                  len1 = extend(s, i, i);
                  int len2;
                  len2 = extend(s, i, i + 1);
                  int len = max(len1, len2);
                  if(len > max_len){
                      max_len = len;
                      start = i - (len - 1) / 2;
                  }
              }
              return s.substr(start, max_len);
          }
      };
      
    3. 马拉车

516. 最长回文子序列

  • 思路+解法

    1. 动态规划 O(n^2)

      class Solution {
      public:
          int longestPalindromeSubseq(string s) {
              int n = s.size();
              int ans = 1;
              vector<vector<int>> dp(n, vector<int>(n, 0));
              for(int i = 0; i < n; i++) dp[i][i] = 1;
              for(int i = n - 2; i >= 0; i--){
                  for(int j = i + 1; j < n; j++){
                      if(s[i] == s[j]){
                          dp[i][j] = dp[i + 1][j - 1] + 2;
                      }
                      else{
                          dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
                      }
                  }
              }
              return dp[0][n - 1];
          }
      };
      

906. 超级回文数

  • 思路+解法

    1. 构造回文数 O(W1/4∗logW)

      class Solution {
      public:
          bool isPalindrome(long long x){
              if(x % 10 == 0) return false;
              long long rev_num = 0;
      
              while(x > rev_num){
                  rev_num = rev_num * 10 + x % 10;
                  x /= 10;
              }
              return x == rev_num || x == rev_num / 10;
          }
          int superpalindromesInRange(string left, string right) {
              int ans = 0;
              long long l = stol(left);
              long long r = stol(right);
              //ABBA
              for(int i = 1; i < 100000; i++){
                  string s1 = to_string(i);
                  string s2 = s1;
                  reverse(s2.begin(), s2.end());
                  string s = s1 + s2;
                  long m = stol(s);
                  m *= m;
                  if(m > r) break;
      
                  if(m >= l && isPalindrome(m)){
                      ans++;
                  }
      
              }
              //ABA
              for(int i = 1; i < 100000; i++){
                  string s1 = to_string(i);
                  string s2 = s1;
                  reverse(s2.begin(), s2.end());
                  string s = s1.append(s2.begin() + 1, s2.end());
                  long m = stol(s);
                  m *= m;
                  if(m > r) break;
      
                  if(m >= l && isPalindrome(m)){
                      ans++;
                  }
              }
              return ans;
          }
      };
      

第5章 深度优先遍历和广度优先遍历

112. 路径总和

  • 思路+解法:

    1. DFS递归 O(n)

      /**
       * Definition for a binary tree node.
       * struct TreeNode {
       *     int val;
       *     TreeNode *left;
       *     TreeNode *right;
       *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
       *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
       *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
       * };
       */
      class Solution {
      public:
          bool dfs(TreeNode* node, int sum, int tar){
               if(!node) return false;
      
               sum += node->val;
               if(!node->left && !node->right){
                   if(sum == tar) return true;
                   else return false; 
               }
      
              return dfs(node->left, sum, tar) || dfs(node->right, sum, tar);
           }
          bool hasPathSum(TreeNode* root, int targetSum) {
              return dfs(root, 0, targetSum);
          }
      };
      

113. 路径总和 II

  • 思路+解法:

    1. DFS O(n)

      /**
       * Definition for a binary tree node.
       * struct TreeNode {
       *     int val;
       *     TreeNode *left;
       *     TreeNode *right;
       *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
       *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
       *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
       * };
       */
      class Solution {
      public:
          int target;
          vector<vector<int>> ans;
          vector<int> path;
      
          void dfs(TreeNode* node, int sum){
              if(!node->left && !node->right && sum == target){
                  ans.push_back(path);
                  return;
              }
              
              if(node->left){
                  path.push_back(node->left->val);
                  dfs(node->left, sum + node->left->val);
                  path.pop_back();
              }
              
              if(node->right){
                  path.push_back(node->right->val);
                  dfs(node->right, sum + node->right->val);
                  path.pop_back();
              }
              
          }
      
          vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
              if(!root) return ans;
              target = targetSum;
              ans.clear();
              path.clear();
              path.push_back(root->val);
              dfs(root, root->val);
              return ans;
          }
      };
      

124. 二叉树中的最大路径和

  • 思路+解法:

    DFS后序遍历 O(n)

    /**
     * Definition for a binary tree node.
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
     *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
     *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
     * };
     */
    class Solution {
    public:
        int ans = INT_MIN;
        //后序遍历
        //每个节点拥有自己的最大值,可以同时拥有左右节点
        //但是给父节点返回的只能为一条路径
        int dfs(TreeNode* node){
            if(!node) return 0;
    
            int left_sum = dfs(node->left);
            int right_sum = dfs(node->right);
    
            int node_max = node->val;
            node_max += left_sum > 0? left_sum : 0;
            node_max += right_sum > 0? right_sum : 0;
            ans = max(node_max, ans);
            return max(node->val, max(node->val + left_sum, node->val + right_sum));
        }
        int maxPathSum(TreeNode* root) {
            dfs(root);
            return ans;
        }
    };
    

岛屿问题

第6章 二分法

704. 二分查找

  • 思路+解法:

    1. 二分模板 O(logn)

      class Solution {
      public:
          int search(vector<int>& nums, int target) {
              int l = 0, r = nums.size() - 1;
              while(l <= r){
                  int mid = (r - l) / 2 + l;
                  if(nums[mid] == target) return mid;
                  else if(nums[mid] > target){
                      r = mid - 1;
                  }else if(nums[mid] < target){
                      l = mid + 1;
                  }
              }
              return -1;
          }
      };
      

153. 寻找旋转排序数组中的最小值

  • 解法+思路:

    1. 二分查找 O(logn)

      class Solution {
      public:
          int findMin(vector<int>& nums) {
              int l = 0, r = nums.size() - 1;
              while(l <= r){
                  int mid = (r - l) / 2 + l;
                  if((mid == 0 || nums[mid] < nums[mid - 1]) &&(mid == nums.size() - 1 || nums[mid] < nums[mid + 1])) return nums[mid];
                  else if(nums[mid] > nums[r]){
                      l = mid + 1;
                  }
                  else {
                      r = mid - 1;
                  }
              }
              return -1;
          }
      };
      

875. 爱吃香蕉的珂珂

思路+解法:

  1. 二分答案 O(logn)

    class Solution {
    public:
        int minEatingSpeed(vector<int>& piles, int h) {
            int l = 1, r = pow(10 ,9);
            int ans = 1;
            while(l <= r){
                int mid = (r - l)/2 + l;
                int sum = 0;
                for(int i = 0; i < piles.size(); i++){
                    sum += piles[i] / mid;
                    if(piles[i] % mid != 0) sum++;
                }
                if(sum > h){
                    l = mid + 1;
                    
                }
                else if(sum <= h){
                    ans = mid;
                    r = mid  - 1;
                }
            }
            return ans;
        }
    };
    

69. Sqrt(x)

  • 思路+解法:

    1. 二分答案 O(logn)

      class Solution {
      public:
          int mySqrt(int x) {
              int l = 0, r = x;
              int ans = 0;
              while(l <= r){
                  int mid = (r - l) / 2 + l;
                  if((long long)mid * mid <= x){
                      l = mid + 1;
                      ans = mid;
                  }
                  else{
                      r = mid - 1;
                  }
              }
              return ans;
          }
      };
      

162. 寻找峰值

  • 思路+解法:

    1. 二分查找 O(logn)

      class Solution {
      public:
          int findPeakElement(vector<int>& nums) {
              int l = 0, r = nums.size() - 1;
              while(l <= r){
                  int mid = (r - l) / 2 + l;
                  if((mid == 0 || nums[mid] > nums[mid - 1]) &&(mid == nums.size() - 1 || nums[mid] > nums[mid + 1])) return mid;
                  if(mid == 0 || nums[mid] > nums[mid - 1] ){
                      l = mid + 1;
                  }
                  else if(nums[mid] < nums[mid - 1]){
                      r = mid - 1;
                  }
              }
              return -1;
          }
      };
      

410. 分割数组的最大值

  • 思路+解法

    1. 二分答案 O(nlog(sum - max_num))

      class Solution {
      public:
          bool cal(vector<int>& nums, int max_sum, int m){
              int _m = 0;
              int temp_sum = 0;
              for(int x : nums){
                  if(temp_sum + x > max_sum){
                      _m++;
                      temp_sum = x;
                  }
                  else temp_sum += x;
              }
              if(temp_sum > 0) _m++;
              return _m <= m;
          }
          int splitArray(vector<int>& nums, int m) {
              int sum = 0;
              int max_num = INT_MIN;
              for(int x : nums) {
                  sum += x;
                  max_num = max(max_num, x);
              }
              //二分区间为最大的元素到所有元素的和
              int l = max_num, r = sum;
              int ans = 0;
              while(l <= r){
                  int mid = (r - l) / 2 + l;
                  if(cal(nums, mid, m)){
                      ans = mid;
                      r = mid - 1;
                  }
                  else{
                      l = mid + 1;
                  }
              }
              return ans;
          }
      };
      

第7章 位运算

191. 位1的个数

  • 思路+解法:

    1. O()

      
      

371. 两整数之和

  • 思路+解法:

    1. O()

      
      

397. 整数替换

  • 思路+解法:

    1. O()

      
      

136. 只出现一次的数字

  • 思路+解法:

    1. 异或 O(n)

      class Solution {
      public:
          int singleNumber(vector<int>& nums) {
              int ans = 0;
              for(int& x : nums) ans ^= x;
              return ans;
          }
      };
      

第8章 设计

155. 最小栈

  • 思路+解法:

    1. 辅助栈 O(1) O(n)

      class MinStack {
      public:
          stack<int> s;
          stack<int> assist_s;
          MinStack() {
      
          }
          
          void push(int val) {
              s.push(val);
              if(assist_s.empty() || assist_s.top() > val) assist_s.push(val);
              else if(assist_s.top() <= val) assist_s.push(assist_s.top());
          }
          
          void pop() {
              s.pop();
              assist_s.pop();
          }
          
          int top() {
              return s.top();
          }
          
          int getMin() {
              return assist_s.top();
          }
      };
      
      /**
       * Your MinStack object will be instantiated and called as such:
       * MinStack* obj = new MinStack();
       * obj->push(val);
       * obj->pop();
       * int param_3 = obj->top();
       * int param_4 = obj->getMin();
       */
      
    2. 数组中存的是差值 O(1)空间复杂度

      class MinStack {
      public:
          stack<long long> s;
          long long min_value;
          MinStack() {
      
          }
          
          void push(int val) {
              if(s.empty()){
                  min_value = val;
                  s.push(0);
              }else{
                  s.push(val - min_value);
                  if(val - min_value < 0){
                      min_value = val;
                  }
              }
          }
          
          void pop() {
              if(s.top() > 0){
                  s.pop();
              }else{
                  min_value = min_value - s.top();
                  s.pop();
              }
          }
          
          int top() {
              if(s.top() > 0){
                  return min_value + s.top();
              }else{
                  return min_value;
              }
          }
          
          int getMin() {
              return min_value;
          }
      };
      
      /**
       * Your MinStack object will be instantiated and called as such:
       * MinStack* obj = new MinStack();
       * obj->push(val);
       * obj->pop();
       * int param_3 = obj->top();
       * int param_4 = obj->getMin();
       */
      

208. 实现 Trie (前缀树)

  • 思路+解法:

    1. Trie树

      const int N = 1e5+50;
      bool isEnd[N];
      int son[N][26];
      int idx = 0;
      class Trie {
      public:
          /** Initialize your data structure here. */
          Trie() {
              memset(isEnd,false, sizeof(isEnd));
              memset(son, 0, sizeof(son));
          }
          
          /** Inserts a word into the trie. */
          void insert(string word) {
              int p = 0;
              for(auto x: word){
                  int u = x - 'a';
                  if(!son[p][u]){
                      son[p][u] = ++idx; //默认节点是0,所以前置++
                  }
                  p = son[p][u]; //移动到下一个节点编号
              }
              isEnd[p] = true; //标记结尾
          }
          
          /** Returns if the word is in the trie. */
          bool search(string word) {
              int p = 0;
              for(auto x: word){
                  int u = x - 'a';
                  if(!son[p][u]){
                      return false;
                  }
                  p = son[p][u];
              }
              return isEnd[p];
          }
          
          /** Returns if there is any word in the trie that starts with the given prefix. */
          bool startsWith(string prefix) {
              int p = 0;
              for(auto x : prefix){
                  int u = x - 'a';
                  if(!son[p][u]){
                      return false;
                  }
                  p = son[p][u];
              }
              return true;
          }
      };
      
      /**
       * Your Trie object will be instantiated and called as such:
       * Trie* obj = new Trie();
       * obj->insert(word);
       * bool param_2 = obj->search(word);
       * bool param_3 = obj->startsWith(prefix);
       */
      
  • 亿点收获:

    1. 非静态私有数据成员不能初始化

146. LRU 缓存机制

  • 思路+解法:

    1. 哈希表 + 链表:O(1)

      struct Node{
          int key, val;
          Node* pre;
          Node* next;
          Node(int k, int v): key(k), val(v), pre(nullptr), next(nullptr) {}
          Node(): key(0), val(0), pre(nullptr), next(nullptr) {}
      };
      class LRUCache {
      private:
          unordered_map<int ,Node*> m;
          int size, capacity;
          Node* drummyHead;
          Node* drummyTail;
      
      public:
          LRUCache(int capacity) {
              size = 0;
              this -> capacity = capacity;
              drummyHead = new Node();
              drummyTail = new Node();
              drummyHead->pre = nullptr;
              drummyHead->next = drummyTail;
              drummyTail->pre = drummyHead;
              drummyTail->next = nullptr;
          }
          
          int get(int key) {
              //cout << "get:"<< key << endl;
              if(m.find(key) != m.end()){
                  Node* node = m[key];
      
                  move_to_head(node);
      
                  return node->val;
              }
              
              return -1;
          }
          
          void put(int key, int value) {
              //cout << "put:"<< key;
              
              if(m.find(key) != m.end()){
                  Node* node = m[key];
      
                  move_to_head(node);
      
                  node->val = value;
              }
              else{
                  Node* node = new Node(key, value);
                  m[key] = node;
                  if(size == capacity){
      
                      delete_tail();
                      add_to_head(node);
      
                  }else{
                      add_to_head(node);
                      size++;
                  }
              }
              /*for(Node* b = drummyHead->next; b != drummyTail; b = b->next) cout << b->val << " ";
              cout << m.size();
              cout << endl;*/
          }
          void move_to_head(Node* node){
              Node* prenode = node->pre;
              Node* nextnode = node->next;
      
              prenode->next = nextnode;
              nextnode->pre = prenode;
      
              Node* headnextnode = drummyHead->next;
      
              drummyHead->next = node;
              node->pre = drummyHead;
              node->next = headnextnode;
              headnextnode->pre = node;
          }
          void add_to_head(Node* node){
              Node* headnextnode = drummyHead->next;
      
              drummyHead->next = node;
              node->pre = drummyHead;
              node->next = headnextnode;
              headnextnode->pre = node;
          }
          void delete_tail(){
              Node* tailpreprenode = drummyTail->pre->pre;
              Node* tailprenode = drummyTail->pre;
      
              tailpreprenode->next = drummyTail;
              drummyTail->pre = tailpreprenode;
              
              m.erase(tailprenode->key);
              delete tailprenode;
          }
      };
      
      /**
       * Your LRUCache object will be instantiated and called as such:
       * LRUCache* obj = new LRUCache(capacity);
       * int param_1 = obj->get(key);
       * obj->put(key,value);
       */
      

460. LFU 缓存

  • 思路+解法:
    1. https://leetcode-cn.com/problems/lfu-cache/solution/ha-xi-biao-shuang-xiang-lian-biao-c-by-l-e5e0/

1206. 设计跳表

第13章 股票问题

121. 买卖股票的最佳时机

  • 思路+解法:

    1. 贪心 O(n)

      class Solution {
      public:
          int maxProfit(vector<int>& prices) {
              int buy = prices[0];
              int ans = 0;
              int n = prices.size();
              for(int i = 1; i < n; ++i){
                  if(prices[i] > buy) ans = max(ans, prices[i] - buy);
                  buy = min(buy, prices[i]);
              }
              return ans;
          }
      };
      
    2. 动态规划 O(n)

      class Solution {
      public:
          int maxProfit(vector<int>& prices) {
              int n = prices.size();
              //持有股票、不持有股票时手中的现金量
              vector<vector<int>> dp(n, vector<int>(2));
      
              dp[0][0] = -prices[0];
              dp[0][1] = 0; 
              for(int i = 1; i < n; i++){
                  dp[i][0] = max(dp[i - 1][0], -prices[i]);
                  dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
              }
              
              return dp[n - 1][1];
          }
      };
      

122. 买卖股票的最佳时机 II

  • 思路+解法:

    1. 动态规划 O(n)

      class Solution {
      public:
          int maxProfit(vector<int>& prices) {
              int n = prices.size();
              vector<vector<int>> dp(n, vector(2, 0));
              dp[0][0] = -prices[0];
              dp[0][1] = 0;
              for(int i = 1; i < n; i++){
                  dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
                  dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
              }
              return dp[n - 1][1];
          }
      };
      
    2. 贪心 O(n)

123. 买卖股票的最佳时机 III

  • 思路+解法:

    1. 动态规划 O(n)

      class Solution {
      public:
          int maxProfit(vector<int>& prices) {
              int n = prices.size();
              vector<vector<int>> dp(n, vector<int>(5, 0));
              dp[0][0] = 0;
              dp[0][1] = -prices[0];
              dp[0][2] = 0;
              dp[0][3] = -prices[0];
              dp[0][4] = 0;
              for(int i = 1; i < n; i++){
                  dp[i][0] = dp[i - 1][0];
                  dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
                  dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
                  dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
                  dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
              }
              return dp[n - 1][4];
          }
      };
      

188. 买卖股票的最佳时机 IV

  • 思路+解法:

    1. 动态规划 O(n * k)

      class Solution {
      public:
          int maxProfit(int k, vector<int>& prices) {
              if (prices.size() == 0) return 0;
              
              int n = prices.size();
              vector<vector<int>> dp(n, vector<int>(2 * k + 1, 0));
              dp[0][0] = 0;
              for(int i = 1; i < 2 * k + 1; i += 2){
                  dp[0][i] = -prices[0];
                  dp[0][i + 1] = 0;
              }
              for(int i = 1; i < n; i++){
                  dp[i][0] = dp[i - 1][0];
                  for(int j = 1; j < 2 * k + 1; j += 2){
                      dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]);
                      dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] + prices[i]);
                  }
              }
              return dp[n - 1][2 * k];
          }
      };
      

309. 最佳买卖股票时机含冷冻期

714. 买卖股票的最佳时机含手续费

  • 思路+解法:

    1. 动态规划 O(n)

      class Solution {
      public:
          int maxProfit(vector<int>& prices, int fee) {
              int n = prices.size();
              vector<vector<int>> dp(n, vector<int>(2, 0));
              dp[0][0] = -prices[0] - fee;
              dp[0][1] = 0;
              for(int i = 1; i < n; i++){
                  dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i] - fee);
                  dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]); 
              }
              return dp[n - 1][1];
          }
      };
      
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Prince_H_23

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值