Skip to content

Latest commit

 

History

History
447 lines (374 loc) · 14.9 KB

0_要点总结.md

File metadata and controls

447 lines (374 loc) · 14.9 KB

要点总结

3 入门模拟

图形输出 (P89)

  • 通过行数和列数的规律,进行输出
  • 定义一个二维数组,通过规律填充,然后输出二维数组
  • eg: 3_B1036.cpp

日期差 (P91)

  • 思路: 小日期不断增加,直到等于大日期
  • 定义一个二维数组 month[13][2]
    • 第一维表示 12 个月份
    • 第二维为 0 表示平年天数,为 1 表示闰年天数

进制转换 (P93)

  • 需要通过 10 进制中转

4 算法初步

选择排序 (P99)

插入排序 (P100)

递归 (P112)

  • 递归边界、递归式

全排列 (P115)

(未学) 回溯 (P117)

贪心 (P118)

  • 考虑当前状态下的局部最优策略
  • eg: 4_B1023.cpp

快速幂 (P134)

  • 定理:
    • b 为奇数,ab = a * ab-1
    • b 为偶数,ab = ab/2 * ab/2
  • eg: 4_FastPower.cpp

双指针 (P137)

  • 头、尾分别设置指针,然后向中间靠近,直到头指针 > 尾指针

归并排序 (P139)

快速排序 (P142)

随机选择算法 (P149)

5 数学问题

最大公约数、最小公倍数

  • 辗转相除法: gcd(a, b) = gcd(b, a % b),递归实现

素数判断 (P160)

  • for 循环的范围 [2 ~ sqrt(n)]

素数获取 (P162)

  • Eratosthenes 筛法
  • 筛选掉所有倍数
  • 设置辅助数组,记录某个数是否被筛选
  • eg: 5_Eratosthenes.cpp

6 STL

STL

7 数据结构专题 1

静态链表 (P260)

  • 定义静态链表的节点时,通常除了数据域和指针域外,还会设置一个节点性质的标记
    struct Node {
        int data;
        int next;
        // 节点某一个性质,根据题目而不同
        bool isInList;
    };
  • 记得要初始化!
  • 可能需要把有效节点全部移至数组左端
  • eg: 7_A1032.cpp, 7_A1052.cpp

8 搜索专题

DFS (P269)

  • 通过递归 (也就是栈) 可以很好地实现 DFS
  • 注意 "死胡同" 和 "岔道口",也就是递归的 边界条件递归表达式
  • "剪枝",可以减少时间复杂度
  • eg: 8_DFS.cpp

BFS (P274)

  • 通过队列,while 循环迭代进行实现
    void BFS(...) 
    {
        queue<T> q;
        q.push(s);
        while (!q.empty())
        {
            ...
        }
    }
  • 要点
    • 全局定义一些变量
    • 定义结构体(类)
    • 可能需要在结构体(类)中设置一些具有标记属性的性质
    • 访问的条件判断
  • eg: 8_BFS_1.cpp, 8_BFS_2.cpp

9 数据结构专题 2

二叉树的递归定义 (P284)

  • 要么没有根节点,是空树
  • 要么由根节点、左子树、右子树组成,且左右子树都是二叉树

二叉树节点的插入 (P286)

  • 二叉树节点的插入位置,就是 查找失败的位置
  • 根据题目二叉树的性质,从左右子树中的一棵进行递归,最后到达空树的位置

时刻要注意判断左、右子树的判空

给定遍历序列重建二叉树 (P294)

带权值的普通树的 DFS (P307)

  • eg: 9_A1053.cpp
  • 该题注意点
    • 普通树,用 vector 存子树的 index
    • 最后按权值的大小输出,可以在读入某节点的子树下标后,对 vector 进行 sort,这样可以优先访问大权值
    • 用 vector 保存 path 时,注意 push_back() 后别忘了 pop_back()

二叉查找树的插入 (P311)

  • 插入某个值,如果这个值查找失败,则查找失败的地方就是这个节点的插入位置
  • eg: 9_BST模板.cpp

二叉查找树的删除 (P313)

  • 递归思想
  • 对于某个节点N,用其前驱(后继)节点P去替换节点N,然后转化为去左子树(右子树)中去删除节点P,然后一直递归,直到递归到一个叶子节点,直接删除
  • eg: 9_BST模板.cpp

二叉查找树

AVL平衡二叉树的插入 (P321)

  • 注意要理解 LL、LR、RR、RL 四种情况,搞清楚左旋、右旋

  • 作题之前最好画个图

  • 插入情况汇总 (P326)

    树形 判定条件 调整方法
    LL BF(root)=2, BF(root->lchild)=1 对 root 右旋
    LR BF(root)=2, BF(root->lchild)=-1 对 root->lchild 左旋,再对 root 右旋
    RR BF(root)=2, BF(root->rchild)=-1 对 root 左旋
    RL BF(root)=2, BF(root->rchild)=1 对 root->rchild 右旋,在对 root 左旋

并查集 (P328)

  • 合并、查找、集合
  • 合并两个集合、判断两个元素是否在同一个集合

并查集的查找 (P329)

  • 迭代或者递归,寻找所属集合的根节点

并查集的路径压缩 (P330)

  • 使原本路径上的节点的父节点都是 集合的根节点

并查集例子 (P332)

堆 (P335)

哈夫曼树 (P342)

  • 带权路径长度最小 (叶子节点的带权路径长度的和)
  • 构建思想
    • 反复选择权值最小的两个元素,不断合并,最后只剩下一个元素
    • 想法是参照下面的模板,设置 priority_queue 的类型为 Node*,重写 greater 函数
  • eg: 9_MinWeightPathLen.cpp
  • 哈夫曼编码
    • 针对一个确定的字符串
    • 将字符串中每个字符出现的频率,作为节点的权值,构造哈夫曼树
    • 二叉树左孩子为 0, 右孩子为 1,进行编码

10 图算法专题

邻接矩阵,邻接表

  • 邻接矩阵适合顶点数量较少情况
  • 邻接表适合顶点数量较多情况

DFS 遍历图 (P350)

  • 模板
    // pseudo code
    void DFS(u)
    {
        visit[u] = true;
        for (u 的所有邻接点 v)
        {
            if (visit[v] == false)
            {
                DFS(v);
            }
        }
    }
    
    void DFSTraverse(G)
    {
        for (G 的所有连通图顶点 u)
        {
            if (visit[u] == false)
            {
                DFS(u);
            }
        }
    }
  • eg: 10_A1034.cpp

BFS 遍历图 (P359)

  • 模板
    // pseudo code
    void BFS(u)
    {
        queue q;
        q.push(u);
        visit[u] = true;
        while (!q.empty())
        {
            top = q.top();q.pop();
            for (u 邻接顶点 v)
            {
                if (visit[v] == false)
                {
                    q.push(v);
                    visit[v] = true;
                }
            }
        }
    }
    
    void BFSTraverse(G)
    {
        for (G 的所有连通图顶点 u)
        {
            if (visit[u] == false)
            {
                BFS(u);
            }
        }
    }
  • eg: 10_A1076.cpp

Dijkstra 算法 (P367)

  • 单源最短路径
  • 当存在比当前的路径更短的路径时,进行更新
  • pseudo code (P370)
  • eg: 10_Dijkstra模板.cpp

11 动态规划专题

  • 解决最优化问题的算法
  • 一个复杂问题分解成若干子问题,综合子问题的最优解
  • 记录子问题的解,避免下次遇到相同的问题而导致重复计算
  • 通过递归 (记忆化搜索) 或者迭代
  • 必须拥有重叠子问题和最优子结构,才能使用动态规划求解
  • 核心问题
    • 设计 状态 和 状态转移方程
    • 还有边界值

非布莱契数列 (P426)

  • 重叠子问题
    • 被分成多个子问题,且子问题会重复出现

数塔问题 (P426)

  • 状态转移方程 (P427)
  • 第 i 层的状态只与第 i+1 层的状态有关
  • 最优子结构
    • 一个问题的最优解由子问题的最优解有效地构造出来
    • 如数塔问题中,每个 dp 都可以由 2 个子问题推导得到

分治,动态规划 (P429)

  • 分治: 子问题不重叠,不一定解决最优化问题
    • 归并排序,快速排序
  • 动态规划: 重叠子问题,一定是最优化问题

贪心,动态规划 (P429)

  • 都必须有最优子结构
  • 贪心: 没有被选择的子问题不会去求解,而是直接抛弃
  • 动态规划: 总是考虑所有子问题结果,选取其中的最优

最大连续子序列和 (P430)

  • 不需要枚举左端点,只需要右端点,记录以当前点为结尾的连续序列的最大和
  • dp[i] = max(dp[i], dp[i - 1] + A[i])
  • eg: 11_MaxConstantSqSum.cpp

最长不下降子序列 (P432)

  • 同样不需要枚举左端点,只需要右端点,记录以当前点为结尾的不降序列的最大长度
  • dp[i] = {1 || dp[j] + 1}
  • eg: 11_LIS.cpp

最长公共子序列长度 (P434)

  • 不需要枚举左端点,记录当前点为结尾的最长公共子序列长度
  • dp[i][j] = {dp[i - 1][j - 1] + 1 || max(dp[i - 1][j], dp[i][j - 1])}
  • eg: 11_LCS.cpp

最长回文子串 (P436)

  • 普通方法枚举端点,会出现有的无法状态转移的情况
  • 字符串的长度字符串的初始位置 进行枚举
  • eg: 11_Palindromic.cpp

01 背包问题 (P443)

  • 考虑第 i 个物品的选择策略
  • dp[i][v] = max{dp[i - 1][v], dp[i - 1][v - w[i]] + c[i]}
  • 空间复杂度上可以进行优化
    • 滚动数组 (一维数组)
  • dp[v] = max{dp[v], dp[v - w[i]] + c[i]}
  • 注意一维数组时,v 需要逆向枚举
    • 如果正向枚举,dp[i - 1][v - w[i]] 会被覆盖
    • dp[v - w[i]] 就会使用左边已经更新的值 (第 i 个)
  • eg: 11_01Bag.cpp

完全背包问题 (P446)

  • 方法同 01 背包问题
  • dp[i][v] = max{dp[i - 1][v], dp[i][v - w[i]] + c[i]}
  • dp[v] = max{dp[v], dp[v - w[i]] + c[i]}
  • 这时的一维数组 v 需要正向枚举
    • 保证 dp[v - w[i]] 是已经更新后的值 (第 i 个)
  • eg: 11_CompleteBag.cpp

总结 (P447)

12 字符串专题

(未学) 字符串 hash 进阶

KMP (P455)

next 数组 (P456)

  • next[i] 是子串 subStr[0...i] 的最长相等前后缀的前缀最后一位的下标
  • pattern[i] != pattern[j + 1],j pointer 不断回溯 j == nextArray[j]
  • 其实也就是 j pointer 回溯到上一个以相同字母开头的前缀的下标

KMP 算法 (P458)

  • 代码与求解 next 数组几乎一致
  • 可以将求 next 数组的过程看作是 pattern模式串自匹配
  • eg: 11_KMP.cpp

有限状态自动机 (P463)

  • 回退的箭头就是 next 数组代表的位置

13 专题扩展

分块思想 (P465)

  • 有序元素划分为若干块,分成 sqrt(N)(取上界) 块,每块中有 sqrt(N)(取下界) 个元素
  • block[sqrt(N)]table[N] 数组
    • block[sqrt(N)] 用来记录每个块中有多少个元素
    • table[N] 用来记录每个元素的个数
  • eg: 13_A1057.cpp

树状数组 BIT (P470)