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