labuladong算法笔记

时间:2021-08-11
本文章向大家介绍labuladong算法笔记,主要包括labuladong算法笔记使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

动态规划解题套路框架

学习计划:

最长回文子序列

〇、必读文章

1、数据结构和算法学习指南(学习算法和刷题的框架思维)

  • 了解数据结构的操作和遍历(迭代or递归)
  • 从树刷起,结合框架思维,有利于理解(回溯、动态规划、分治等)

 2、动态规划详解(动态规划解题套路框架)

  • 过程:递归的暴力解法 -> 带备忘录的递归解法 -> 非递归的动态规划解法
  • 特征:重叠子问题-->使用备忘录&自底向上,最优子结构,状态转移方程
  • 例题:凑零钱(dp[i] = min(dp[i], 1 + dp[i - coin]))

3、回溯算法详解(修订版)=DFS-----做选择

  • 3个问题:参数记录路径、选择列表(做选择和撤销选择)、结束条件
result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return

    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择
  • 例题:八皇后问题、全排列问题

4、BFS 算法框架套路详解------求最短距离

  • 与DFS对比:使用队列,路径短,空间复杂度高
  • 问题本质:在图中找起点到终点的最近距离,队列入队访问邻接,记录访问过的
  • 例题:二叉树的最小高度(齐头并进,BFS时间复杂度低)、打开密码锁的最少次数(可以使用双向BFS,无需掌握)
  • 步骤:
// 计算从起点 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++;
    }
}

5、我作了首诗,保你闭着眼睛也能写对二分查找

  • 防止两数相加产生溢出:mid = left + (right - left) / 2;
  • while里面是小于等于
  • 寻找左侧边界的二分:相等时不直接返回
int left_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    // 搜索区间为 [left, right]
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            // 搜索区间变为 [mid+1, right]
            left = mid + 1;
        } else if (nums[mid] > target) {
            // 搜索区间变为 [left, mid-1]
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 收缩右侧边界
            right = mid - 1;
        }
    }
    // 检查出界情况
    if (left >= nums.length || nums[left] != target)
        return -1;
    return left;
}
  • 寻找右侧边界:left = mid + 1;「搜索区间」全都统一成两端都闭

原文地址:https://www.cnblogs.com/liujinhui/p/15130032.html