labuladong的算法小抄學習筆記


本文主要目的是精簡原文,吸其精華,簡單整理方便自己。
有很多結論,是原文的話,請帶着自己的思想觀看這篇筆記。

labuladong的原文地址

一、基礎:

  1. 數據結構的存儲方式只有兩種:數組(順序存儲)和鏈表(鏈式存儲)。
     用數組實現,就要處理擴容縮容的問題;
     用鏈表實現,沒有這個問題,但需要更多的內存空間存儲節點指針。

  2. 數據結構的基本操作:遍歷+訪問(增刪改查)
     形式:線性:for/while 迭代為代表,非線性:遞歸為代表

  3. 刷題先刷二叉樹二叉樹遍歷(前序、中序、后序、層次、深度優先、廣度優先遍歷
     前序:根左右
     中序:左根右
     后序:左右根
    二叉樹套路框架?:

    不要小看這幾行破代碼,幾乎所有二叉樹的題目都是一套這個框架就出來了

    function traverse(root: TreeNode) {
        // 前序遍歷代碼位置
        traverse(root.left);
        // 中序遍歷代碼位置
        traverse(root.right);
        // 后序遍歷代碼位置
    }
    

    PS:因為二叉樹是最容易培養框架思維的,而且大部分算法技巧,本質上都是樹的遍歷問題。
      刷完整個專題,再去做什么回溯動規分治專題,你就會發現只要涉及遞歸的問題,都是樹的問題

  4. 常見算法技巧
    單鏈表:雙指針
    數組:很大部分也是雙指針

  5. 動態規划解題套路框架:存在【重疊子】問題,一定具備【最優子】結構,需要列出正確的【狀態轉移方程】(三要素)。
     一般是用來求最值,只有列出正確的「狀態轉移方程」,才能正確地窮舉

    框架:明確 base case -> 明確「狀態」-> 明確「選擇」 -> 定義 dp 數組/函數的含義
    題解思路:遞歸偽代碼,暴力遞歸,優化遞歸,反向for(因為遞歸是自頂向下,動態規划一般是自底向上)

    // 初始化 base case
    dp[0][0][...] = base
    // 進行狀態轉移
    for 狀態1 in 狀態1的所有取值:
        for 狀態2 in 狀態2的所有取值:
            for ...
                dp[狀態1][狀態2][...] = 求最值(選擇1,選擇2...)
    

    PS:下面通過斐波那契數列問題和湊零錢問題來詳解動態規划的基本原理。前者主要是讓你明白什么是重疊子問題(斐波那契數列沒有求最值,所以嚴格來說不是動態規划問題),后者主要舉集中於如何列出狀態轉移方程。

  6. 回溯算法解題套路框架:是我們常說的 DFS(深度優先) 算法,本質上就是一種暴力窮舉算法
    解決一個回溯問題,實際上就是一個決策樹的遍歷過程。

    站在回溯樹的一個節點上,你只需要思考 3 個問題:
    1)路徑:也就是已經做出的選擇。
    2)選擇列表:也就是你當前可以做的選擇。
    3)結束條件:也就是到達決策樹底層,無法再做選擇的條件。

    其核心就是 for 循環里面的遞歸,在遞歸調用之前「做選擇」,在遞歸調用之后「撤銷選擇」

    result = []
    def backtrack(路徑, 選擇列表):
        if 滿足結束條件:
            result.add(路徑)
            return
        for 選擇 in 選擇列表:
          # 做選擇
          將該選擇從選擇列表移除
          路徑.add(選擇)
          backtrack(路徑, 選擇列表)
          # 撤銷選擇
          路徑.remove(選擇)
          將該選擇再加入選擇列表
    

    例題:全排列N皇后

  7. BFS算法解題套路框架:(廣度優先),一般用隊列數據結構,問題的本質就是:讓你在一幅「圖」中找到從起點 start 到終點 target 的最近距離

    // 計算從起點 start 到終點 target 的最近距離
    int BFS(Node start, Node target) {
        Queue<Node> q; // 核心數據結構
        Set<Node> visited; // 避免走回頭路
        
        q.offer(start); // 將起點加入隊列
        visited.add(start);
        int step = 0; // 記錄擴散的步數
    
        while (q not empty) {
            int sz = q.size();
            /* 將當前隊列中的所有節點向四周擴散 */
            for (int i = 0; i < sz; i++) {
                Node cur = q.poll();
                /* 划重點:這里判斷是否到達終點 */
                if (cur is target)
                    return step;
                /* 將 cur 的相鄰節點加入隊列 */
                for (Node x : cur.adj()) {
                    if (x not in visited) {
                        q.offer(x);
                        visited.add(x);
                    }
                }
            }
            /* 划重點:更新步數在這里 */
            step++;
        }
    }
    

============= 分割線 ==================

今天偶然百度了一下labuladuo,發現這教程有着讓我難以接受的歷史(不能接受抄襲去變現)這是鏈接
如果想短時間內提升解題能力,我覺得這個小抄還是不錯的,只要內容正確(有人說有些題是錯的,沒去驗證過),可以白嫖。
算法靠自己總結,才會更加印象深刻吧,我先另謀生路。

本文結束,沒想到第一遍博客止步於此。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM