算法總結

Array題目總結

1.題目分類

  • 雙指針
    • 同向雙指針java

    • 相向雙指針node

  • 滑動窗口
    • 使用hash map進行count
  • 前綴和、後綴和
    • 利用前綴和下降時間複雜度算法

    • 前綴和 + Hash Table
      • 這類題目其實都是2Sum的變種,利用hash table記錄下標,從而若是發現有合法的子數組後,可以直接求出子數組區間
      • 325.Maximum Size Subarray Sum Equals k、525.Contiguous Array
  • 區間問題
    • 掃描線算法
  • 排序算法的考察
    • 歸併排序
    • 快速排序
    • 快速選擇
  • 和各個數據結構結合
    • Hash Table
    • Stack
      • 單調棧
    • Heap
    • Queue

2.注意點


Tree題目總結

1.題目分類

  • 全局prev變量、全局sum變量數組

  • 樹的path sum相關數據結構

  • LCA(最近公共祖先)指針

  • BST相關(遇到BST的題必定要考慮它的性質)
    • 刪除BST的節點
  • 樹的遍歷
    • 非遞歸前序中序遍歷
    • 使用Stack實現樹的前序、中序遍歷
    • 樹的層序遍歷BFS (套模版較簡單)
      • 求樹的最大寬度
    • 樹的序列化
  • 遞歸左右子樹:分治法思想
    • 判斷鏡像樹
    • 旋轉左右節點
  • 樹中的距離
    • 求樹的最大直徑(最長的任意兩點間的距離=>轉化爲求樹高)
    • 求樹中最長路徑(轉化爲求左右子樹路徑,而後左右加起來和全局的比較)
  • 藉助Stack
    • 判斷某個序列是不是合法的遍歷序列
  • 樹中的路徑(樹的路徑定義爲樹中任意兩個節點間的距離)
    • 求最長的路徑 :
      先分治得出leftright,而後更新全局res的時候是left + right,可是本次遞歸返回的是max(left, right)
        1. Binary Tree Maximum Path Sum
        1. Diameter of Binary Tree
        1. Longest Univalue Path

2.補充知識點

1) BST的性質

二叉查找樹要麼是一棵空樹,要麼是一棵具備以下性質的非空二叉樹:code

  • 若左子樹非空,則左子樹上的全部結點的關鍵字值均小於根結點的關鍵字值
  • 若右子樹非空,則右子樹上的全部結點的關鍵字值均大於根結點的關鍵字值
  • 左、右子樹自己也分別是一棵二叉查找樹(二叉排序樹)

能夠看出,二叉查找樹是一個遞歸的數據結構,且對二叉查找樹進行中序遍歷,能夠獲得一個遞增的有序序列排序

好比lc 98題,不使用全局變量,採用遞歸左右子樹的形式,判斷BST :遞歸

class Solution {
    public boolean isValidBST(TreeNode root) {
        return helper(root, null, null);
    }
    
    private boolean helper(TreeNode node, Integer max, Integer min) {
        if (node == null) return true;
        if (min != null && node.val <= min) return false;
        if (max != null && node.val >= max) return false;
        return helper(node.left, node.val, min) && helper(node.right, max, node.val);
    }
}

3.注意點


DFS 總結

1.DFS問題核心思想

  • 將大問題分解爲小問題,小問題遞歸解決:
    • 從top-down分解下去,計算倒是bottom-up計算上來,最終將結果傳到頂層;
  • 每一步有多個路徑能夠走,則有的路徑走不通了後就須要回溯,回溯記得將原來改變的狀態恢復(擦屁股);

2.通常解題思路

  • 棋盤類的題目
    • 通常就是按照四個方向擴展,再加上一些條件看能不能繼續向某個方向擴展,遍歷的起始點多是內部某點,也多是邊界;
      • 具體可參考490. The Maze 、489. Robot Room Cleaner
  • 問題分解類的題目
    • 大問題分解爲小問題,可能涉及記憶化搜索 memo + dfs,好比字符串分割;:
      • 87.Scramble String、140. Word Break、329. Longest Increasing Path in a Matrix、 638. Shopping Offers、935.Knight Dialerip

      • 比較典型的dfs + memo: 329. Longest Increasing Path in a Matrix
      class Solution {
            int m, n;
            int[][] dirs = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
      
            public int longestIncreasingPath(int[][] matrix) {
                 if (matrix.length == 0 || matrix[0].length == 0) return 0;
                 m = matrix.length;
                 n = matrix[0].length;
                 int[][] memo = new int[m][n];
                 int res = 0;
                 for (int i = 0; i < m; i++) {
                      for (int j = 0; j < n; j++) {
                           int tmp = helper(matrix, i, j, memo);
                           res = Math.max(res, tmp);
                      }
                 }
                 return res;
            }
      
            private int helper(int[][] matrix, int x, int y, int[][] memo) {
                 if (memo[x][y] != 0) return memo[x][y];
                 int res = 1;
                 for (int[] d : dirs) {
                      int nx = x + d[0];
                      int ny = y + d[1];
                      if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
                      if (matrix[nx][ny] <= matrix[x][y]) continue;
                      int tmp = helper(matrix, nx, ny, memo) + 1;
                      res = Math.max(tmp, res);
                 }
                 memo[x][y] = res;
                 return res;
            } 
       }
      • 比較典型的dfs + memo: 935.Knight Dialer

        可是書已到這題的memo是二維的,也就是每一步的狀態,其實涉及到兩個變量,即每一步按到哪一個數,和當前是第幾個數,因此是memo[num][index],飯容易範這個錯誤,將memo寫成以維的memo[num]

      class Solution {
           int[][] neighbors = {{4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4}};
           int MOD = 1_000_000_007;
      
           public int knightDialer(int N) {
                int res = 0;
                int[][] memo = new int[10][N + 1];
                for (int i = 0; i < 10; i++) {
                     res += helper(i, N, memo);
                     res %= MOD;
                }
                return res;
           }
      
           private int helper(int num, int index, int[][] memo) {
                if (index == 1) return 1; 
                if (memo[num][index] != 0) return memo[num][index];
                int res = 0;
                for (int next : neighbors[num]) {
                     res += helper(next, index - 1, memo);
                     res %= MOD;
                }
                memo[num][index] = res;
                return res;
           }
      }
  • Combination類型及其變種:
      1. Matchsticks to Square、698.Partition to K Equal Sum Subsets

3.注意點

0.記得加上visited數組,防止重複訪問而產生overflow


BFS總結

1.題目分類

  • 樹、圖的層序遍歷

  • 拓撲排序
    • 判斷有向圖是否有環
  • 求最短距離
    • 棋盤上的最短路徑

2.通常解題思路

3.注意點


DP總結

1.通常解題思路

dynamic programming的適用狀況

  • 最優子結構:問題能夠被分解爲求解子問題的最優解,也就是如今的解依賴於子問題的左右解

  • 子問題重複計算 :子問題具備重複求解行,因此能夠事先存儲下來,以便於以後直接獲取,從而避免子問題對的重複求解

  • 無後效性:子問題的最優解是肯定的,且一旦子問題的最優解獲得後,原問題的最優解能夠用子問題的最優解求解

2.題目分類

  • Matrix DP

  • Sequence DP (單sequence) 一維的問題
    • 典型題:LIS (注意LIS的兩種姿式)

    • 最小調整代價

    • 2 sets of subproblems: 原問題的最優解依賴於兩個子問題的最優解 (一般從左向右掃描一次,而後再從右向左掃描一次,最後再合併左右的結果)

      for (int i = 1; i < A.length; i++) {
           if (A[i] > A[i - 1]) inc[i] = inc[i - 1] + 1;
      }
      for (int i = A.length - 2; i > 0; i--) {
           if (A[i] > A[i + 1]) dec[i] = dec[i + 1] + 1; 
      }
      //合併
      for (int i = 0; i < A.size(); i++) {
           if (inc[i] && dec[i]) {
                res = Math.max(res, inc[i] + dec[i] + 1);
           }
      }
      public int maxProduct(int[] nums) {
             if (nums == null || nums.length == 0) return 0;
             int n = nums.length;
             int[] mins = new int[n];
             int[] maxs = new int[n];
             int res = nums[0];
             mins[0] = nums[0];
             maxs[0] = nums[0];
             for (int i = 1; i < n; i++) {
                  maxs[i] = mins[i] = nums[i];
                  if (nums[i] > 0) {
                       maxs[i] = Math.max(maxs[i - 1] * nums[i], nums[i]);
                       mins[i] = Math.min(mins[i - 1] * nums[i], nums[i]);
                  } else if (nums[i] < 0) {
                       maxs[i] = Math.max(mins[i - 1] * nums[i], nums[i]);
                       mins[i] = Math.min(maxs[i - 1] * nums[i], nums[i]);
                  }
                  res = Math.max(res, maxs[i]);
             }
             return res;
      }
      /**
        題意:求乘積最大的子數組,關鍵是原數組中有正負數
        分析:那麼確定是用動態規劃了,本質是用兩個dp數組,一個維護至今的最大值,一個維護至今的最小值 ;
        想的簡單點,就是維護一個至今的最大值和最小值數組,max[i]表示到i爲止的最大的,min[i]表示迄今最小值
        固然簡化了也能夠用兩個變量max和min,也就是用來個狀態來維護,只須要當A[i] < 0的時候交換min和max就好了
        若是是負數,則就會使獲得當前爲止的最大值變爲最小值,當前爲止的最小值變爲最大值;
        應該維護兩個變量,一個是至今的最大值一個至今的最小值,而後還有一個全局的最大值;
        */
      
        //兩個dp數組版本:
        public int maxProduct(int[] nums) {
             if (nums == null || nums.length == 0) return 0;
             int n = nums.length;
             int[] mins = new int[n];
             int[] maxs = new int[n];
             int res = nums[0];
             mins[0] = nums[0];
             maxs[0] = nums[0];
             for (int i = 1; i < n; i++) {
                  maxs[i] = mins[i] = nums[i];
                  if (nums[i] > 0) {
                       maxs[i] = Math.max(maxs[i - 1] * nums[i], nums[i]);
                       mins[i] = Math.min(mins[i - 1] * nums[i], nums[i]);
                  } else if (nums[i] < 0) {
                       maxs[i] = Math.max(mins[i - 1] * nums[i], nums[i]);
                       mins[i] = Math.min(maxs[i - 1] * nums[i], nums[i]);
                  }
                  res = Math.max(res, maxs[i]);
             }
             return res;
        }
      
        //兩個狀態變量:
        public int maxProduct(int[] nums) {
             if (nums == null || nums.length == 0) return 0;
             int n = nums.length;
             int res = nums[0], tmpMin = res, tmpMax = res;
             for (int i = 1; i < n; i++) {
                  if (nums[i] < 0) {
                       int tmp = tmpMin;
                       tmpMin = tmpMax;
                       tmpMax = tmp;
                  }
                  tmpMax = Math.max(tmpMax * nums[i], nums[i]);
                  tmpMin = Math.min(tmpMin * nums[i], nums[i]);
                  res = Math.max(res, tmpMax);
             }
             return res;
        }
    • 具備多個狀態:dp[i][0]、dp[i][1] dp[i][2] ...i is problem size
  • Two Sequences Converging
    • 典型題:LCS
  • 揹包問題

3.注意點


數據結構總結

1.題目分類

  • Stack
    • 單調棧

    • 遞歸問題轉爲迭代形式(不少遞歸問題都也能夠用stack解決)

    • 用棧模擬:根據題目的性質,這時候分析幾個例子,查看是否具備棧的性質,好比和棧頂元素關係直接這種狀況

  • Hash Table
    • HashMap

    • TreeMap

      有序key-value,通常按照key有序組織,能夠找到第一個比當前key小的:floorKey()或者大的key:ceilingKey(),在有些題目中頗有用

  • Queue
  • Linked List (通常有幾個常考的套路)
    • 翻轉鏈表模版
    private ListNode reverse(ListNode head){
         ListNode newNode=null;
         while(head!=null){
             ListNode temp=head.next;
             head.next=newNode;
             newNode=head;
             head=temp;
         }
         return newNode;
     }
    • 幾個基本操做:相似題目題目23四、25能夠分解爲這幾個基本操做:求鏈表中點(求鏈表第n個點)、反轉鏈表

    • 相似題目24須要先後兩個指針prev、cur來交替操做

    • 相似題目8六、328屬於分割鏈表,藉助dummy node

  • Union Find
    • 並查集模版

      class UF {
           int[] parent;
           public UF(int N) {
                parent = new int[N];
                for (int i = 0; i < N; i++) parent[i] = i;
           }
           public int find(int x) {
                if (parent[x] != x) parent[x] = find(parent[x]);
                return parent[x];
           }
           public void union(int x, int y) {
                parent[find(x)] = find(y);
           }
      }
  • Trie

  • Graph

2.通常解題思路

3.注意點


其餘重要算法

1.題目分類

  • 貪心法

  • 分治法

  • Sort
    • 手撕快排(這個確定的)
    • 手撕歸併排序 (這個必須的)
  • 掃描線算法
    • Meeting rooms problem:
    • 252.Meeting-Rooms (M)
    • 253.Meeting-Rooms-II (M+)
    • 056.Merge-Intervals (M)
    • 057.Insert-Intervals (M)
    • 732.My-Calendar-III (M)
    • 759.Employee-Free-Time (M+)
    • 370.Range-Addition (M+)
  • 機率題
  • Segment Tree

相關文章
相關標籤/搜索