第三章:数组中的问题最常见
3-2
Eg:283 移动零
练:
- 27. 移除元素
- 26.删除有序数组中的重复项
- 80.删除有序数组中的重复项 II
3-5
数据结构与算法C++精解
Eg:75 颜色分类
- 计数排序
- 三路快排
练:
- 88.合并两个有序数组(归并)、
- 215.数组中的第K个最大元素(第k大元素,利用快排)
3-6
Eg:167 两数之和 II - 输入有序数组
- 暴力解法,双层遍历O(n^2)
- 当看到有序的数组时,我们要想到用二分搜索提速
- 对撞指针
练:
- 125.验证回文串
- 344.反转字符串
- 345.反转字符串中的元音字母(a,e,i,o,u)
- 11.盛最多水的容器
3-7、3-8
Tip: 双索引技术的应用之滑动窗口
Eg:209 长度最小的子数组
- 暴力解:遍历所有的连续子数组 [i...j],计算其和sum,验证sum >= s, O(n^3)-> 如何优化到O(n^2)
- 滑动窗口
练:
- 438. 找到字符串中所有字母异位词 ❌
- 76. 最小覆盖子串 ❌
第四章:查找表相关问题
4-1、4-2
查找类问题:
查找有无?-某元素知否存在?Set
查找对应关系(键值对应)-某元素出现了几次?Map
Eg:
- 350. 两个数组的交集 II
思考🤔:对于👆🏻这两个问题,如果数组是有序的,用什么方法更简洁?
4-3、4-4、4-5、4-6
map和set的底层实现为平衡二叉树
哈希表查找的时间复杂度是O(1),但是没有顺序性
练:
- 242.有效的字母异位词
- 290.单词规律
- 205.同构字符串
- 451.根据字符出现频率排序
Eg:1 两数之和(一个使用查找表的经典问题)
- 暴力解法 O(n^2)
- 排序后,对撞指针O(nlogn) 📢:排序后如何保留原有索引值
- 查找表
练:
Eg:454 四数相加 II ❌
- 暴力解法 O(n^4) ❌
- 查找表 O(n^3) ❌
- 优化查找表到 O(n^2)
练:49 字母异位词分组
Eg:447 回旋镖的数量
练:149 直线上最多的点数
4-7、4-8
滑动窗口+查找表
Eg:219 存在重复元素 II
练:217 存在重复元素
Eg:220 在重复元素 III
第五章:在链表中穿针引线
5-1**、5-2、5-3**
Key:链表相关(穿针引线:操纵next指针的指向问题)
Eg:206 反转链表
改变一个链表节点的指针指向,需要三个指针(pre, cur, next)
练:
Key:设置链表的虚拟头节点(使用完记得删除释放)
Eg:203 移除链表元素(如果删除的节点是头节点,要特殊处理)
练:
- 82.删除排序链表中的重复元素 II
- 21.合并两个有序链表 (归并的merge)
5-4
- 24. 两两交换链表中的节点
- 25. K 个一组翻转链表❌
- 147. 对链表进行插入排序❌
- 148. 排序链表(自底向上的归并排序算法)❌
5-5 特殊的情况下,可以通过改变节点的值来处理链表问题
- 237. 删除链表中的节点 (题目给定的就是要删除的节点,我们无法知道要删除节点的前一个节点,所以不能通过改变前一个节点next指针的方式删除,我们就只能改变当前节点的值=当前节点的下一个节点值,再删除当前节点的下一个节点。注意⚠️:边界的处理:node = null; node.next = null即node是最后一个节点)
5-6 链表与双指针
- 19. 删除链表的倒数第 N 个结点
-
方法一:先遍历一边计算链表长度;再遍历一边删除倒数第n个节点
-
方法二:虚拟节点+双指针
第六章:栈,队列,优先队列
6-1 栈和队列的使用
**6-2 、**6-3 栈和递归的紧密关系
- 144.二叉树的前序遍历 (DLR)
-
- 二叉树的中序遍历 (LDR)
- 145.二叉树的后序遍历 (LRD)
- 341. 扁平化嵌套列表迭代器
6-4 队列 Queue
队列的基本应用:广度优先遍历
树: 层序遍历
图:无权图的最短路径
- 102. 二叉树的层序遍历
- 107. 二叉树的层序遍历 II
- 103.二叉树的锯齿形层序遍历
- 199.二叉树的右视图
6-5 BFS和图的最短路径
6-6 优先队列
****普通队列:先进先出,后进后出
****优先队列:寻找队列中的最大值或最小值优先出队
优先队列的底层实现:使用的是 堆 这种数据结构
对于堆的底层实现,要重视,做到白板编程的程度(使用一个数组来模拟一棵树)
6-7
第七章:二叉树和递归
7-1 二叉****树和递归
7-2
- 226 翻转二叉树
- 100 相同的树
- 101 对称二叉树
- 222 完全二叉树的节点个数
- 110 平衡二叉树
满二叉树:所有层的节点数达到最大
完全二叉树:除了最后一层,所有层的节点数达到最大,与此同时,最后一层的所有节点都在最左侧。(堆使用完全二叉树)
**平衡二叉树:**每一个节点的左右子树的高度差不超过1
7-3 递归的终止条件
7-4
- 257 二叉树的所有路径
- 113 路径总和 II
- 129 求根节点到叶节点数字之和
7-5
- 437 路径总和 III
7-6 二分搜索树
**二分搜索树:**每个节点的键值大于左孩子;每个节点的键值小于右孩子;以左右孩子为根的子树仍为二分搜索树
- 235 二叉搜索树的最近公共祖先
- 98 验证二叉搜索树
- 450 删除二叉搜索树中的节点
- 108 将有序数组转换为二叉搜索树 (是二分搜索树中序遍历的逆过程)
- 230 二叉搜索树中第K小的元素
- 236 二叉树的最近公共祖先 (LCA)
第八章:递归和回溯法
8-1 8-2 树形问题
- 17 电话号码的字母组合
回溯法是暴力解法的有个主要实现手段
8-3 回溯算法的应用
8-4 组合问题
- **77 组合
**
第九章:动态规划基础
9-1 斐波那契数列
// 递归
function fib (n) {
if(n == 0) return 0;
if(n == 1) return 1;
return fib(n-1) + fib(n-2);
}
时间复杂度:O(2^n)
// 记忆化搜索--自上而下的解决问题
// 优化:避免重复计算
var memo = []
function fib (n) {
if(n == 0) return 0;
if(n == 1) return 1;
if(memo[n]) {
memo[n] = fib(n-1) + fib(n-2)
}
return memo[n];
}
时间复杂度:O(n), fib函数会执行2n-1次
动态规划--自下而上的解决问题
function fib (n) {
let memo = [];
memo[0] = 0;
memo[1] = 1;
for(let i = 2; i <= n; i++) {
memo[i] = memo[i-1] + memo[i-2];
}
return memo[n]
}
时间复杂度:O(n),不存在递归调用
动态规划:将原问题拆解成若干子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案
9-2
9-3
通过求子问题的最优解,可以得到原问题的最优解
9-4 状态和状态转移
9-5 01背包问题