数组&排序

88、合并两个有序数组

image-20220402132218661

1
2
3
4
5
+ 双指针 + 倒排
+ while循环(条件:下面退出)
+ 上指针未结束,且大于下指针
+ 上指针结束(提前走完)或者小于下面指针
+ 其他方法

image-20220402132335462

75、颜色分类

image-20220402132349617

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ 对数组排序(只包含0、1、2),原地排序(常数空间),一趟扫描
+ sort ×
+ 排序算法 ×
+ 双、三指针(扫描一趟常用解法)
+ 左边两个指针(放0、遍历扫描),右边一个指针(放2),判断左边元素
+ 遍历(条件:扫描退出)
+ 扫描到1跳过
+ 扫描移动
+ 扫描到0与左指针交换
+ 左指针移动
+ 扫描移动(交换来的值已经被扫描过,无需判断)
+ 扫描到2与右指针交换
+ 右指针移动
+ 扫描再次判断(交换来的值未被判断过)

image-20220402132456943

16、部分排序

image-20220402132511409

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ 找到中间未排序的区间(两边已排序)
+ 寻找逆序对
+ 找到最后一个逆序对

+ 花时间思考值得
+ 类似的问题就容易解决了
+ 总结、思考

+ 双指针
+ 两边同时扫描
+ 记录扫描过程的最大值
+ 若比最大值小,则记录位置(逆序)
+ 统计最大逆序位置
+ 记录最左和最右的最大逆序位置

image-20220402132528787

image-20220402132725332

练习

977、164-hard

总结

双指针(常用)

左右扫描

链表

203、移除链表元素

image-20220402133042094

1
2
3
4
5
6
7
8
9
10
11
+ 删除节点
+ 想象成一个全新的链表
+ 如果是要删除的,跳过
+ 如果不是,连接节点
+ 双指针 + 遍历一遍
+ 一指针遍历,一指针指向尾节点
+ 若为删除节点,尾节点不动(非尾部)
+ 若为正常节点,尾节点指向当前
+ 虚拟头结点
+ 方便删除第一个节点
+ 遍历将next作为头

image-20220402133048665

2、两数相加

image-20220402133107021

1
2
+ 虚拟头结点
+ 遍历 + 进位

image-20220402133120030

160、相交链表

image-20220402133130614

1
2
3
4
5
6
7
8
9
10
+ 快慢指针?
+ 拼接链表
+ 方法
+ 下一个为null时移动到另一个头结点
+ 原理
+ 两个链表长度相同
+ 最后的节点必然相同——相交之处(开始相同的地方)
+ 遍历(相同时结束)
+ 走完时到另一个节点头部
+ 结束时指向第一个相同位置

image-20220402133157555

image-20220402133206604

86、分隔链表

image-20220402133222815

1
2
3
4
5
6
7
8
9
10
+ 两个虚拟头结点lhead、rhead
+ lhead
+ 小于的串进来
+ ltai指向末尾
+ rhead
+ 大于的串进来
+ rtail指向末尾
+ 配合last指针记录每条链表尾部
+ 遍历链表head指针
+ 判断值进行链表串接

image-20220402133249278

image-20220402133300887

image-20220402133346850

234、回文联表

image-20220402133354186

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+ 1、链表反转
+ 1、创建新链表翻转
+ 2、后半部分原地翻转
+ 两个链表头
+ 双指针比较
+ 移动到null结束
+ 实现
+ 找到中间节点(常用操作)
+ 奇数-中间的
+ 偶数-左边的
+ 快慢指针
+ 快指针一次两步,慢指针一次一步
+ 快指针到头,慢指针到中间节点
+ 翻转链表(常用操作)
+ 指针逆序
+ 双指针遍历比较
+ 若需不改变原结构
+ 再次翻转
+ 这些基本操作100%快速手写

image-20220402133431358

image-20220402133439442

练习

138、708、237、141、206、21、23

总结

1
2
3
4
5
6
7
+ 99%题目画图
+ 虚拟头结点
+ 快慢指针
+ 求中心节点
+ 判断环
+ 多指针
+ 链表翻转

栈&队列

概念

image-20220402133800668

1
2
3
4
5
6
7
8
9
10
11
+ 栈
+ 解决问题:对称
+ 回文链表
+ 前面半部分入栈
+ 后面半部分出栈判断是否相等
+ 有效的括号
+ 左右括号对称
+ 队列
+ 解决问题:顺序
+ 双端队列
+ 头尾都可添加、删除元素

image-20220402133845463

155、最小栈

image-20220402133857251

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+ 获取栈中最小元素
+ 要求常数时间
+ 空间换时间
+ 1、两个栈
+ 正常栈、最小栈(只存放当前及以下层最小数据)
+ 两个栈同步 入栈、出栈
+ 最小栈入栈只放最小值
+ 核心思想
+ push的时候知道当前栈的最小值
+ 维护一个最小值
+ 优化
+ 链表
+ head节点始终保存最小值
+ 头插法
+ 实现
+ 创建node类
+ 保存当前值和最小值
+ 初始化创建虚拟头结点
+ 值为最大
+ push
+ 维护当前值和最小值(比较head)

image-20220402133938307

image-20220402134001226

239、滑动窗口最大值

image-20220402134021286

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
+ 为什么想不到方法?
+ 做的题太少!!!
+ 刷题
+ 套路总结

+ 1:暴力
+ 遍历,每次往后找k个元素
+ 2:动态规划

+ 3:双端队列
+ 准备
+ i指向滑动窗口最后一个元素
+ w指向第一个元素
+ node
+ 索引和对应的值
+ 实现
+ 从队尾入队,前面小的都移除
+ 队列中头元素最大
+ 当w合法时,获取队列头元素(滑动窗口最大值)
+ 移动后,判断队列头部元素索引是否有效
+ 队列按索引、从大到小排列
+ 只需遍历一遍O(n)
+ 原理
+ 索引判断节点位置,是否在滑动窗口
+ 值进行大小比较,前面永远是滑动窗口最大值
+ 思考每一步都是为了什么?
+ 为什么这么做
+ 必须要弄明白原理
+ 双端队列(linkedlist双向链表)
+ 遍历数组
+ while判断队头元素和当前元素
+ 删除全部比当前元素小的
+ 将i加入队尾
+ 检查窗口的索引是否合法
+ 最后k-1个元素结束遍历
+ 检查队头索引是否合法
+ 不合法移除
+ 最多只需删掉一个(每次移动都会删除)
+ 设置窗口的最大值
+ 队头元素设置到最大值数组中
+ 为什么使用双端队列?
+ 单调队列
+ 递减
+ 整体目的
+ 一次遍历找到滑动窗口最值
+ 找到最值:保证对头元素是当前滑动窗口的最大值
+ 大的元素往前靠
+ 小的元素没有意义
+ 队列单调

+ 4:双指针
+ 实现
+ 先扫描前k个元素找到最值的索引
+ 遍历数组
+ 每次移动,维护最大值索引
+ 若最值索引在滑动窗口内
+ 若最后元素大于最大值索引,设为最值
+ 若最后元素不大于,继续
+ 若不在窗口内,则重新扫描k个窗口元素,找到最值,更新最大值索引
+ 设置最大值索引为窗口最值
+ 性能
+ 对于递减数列,需要不断重新扫描最值

image-20220402134322988

image-20220402134342003

image-20220402134406537

654、最大二叉树

image-20220402134420556

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
+ 先找最值
+ 左边元素:左子树构建最大二叉树
+ 右边元素:右子树构件最大二叉树
+ 递归
+ 找到指定范围中的根节点(最大值node)
+ 结束条件:范围左右重合,直接返回
+ 创建节点
+ 遍历扫描
+ 找到最值的索引i
+ 左子树递归找到根节点(left,i)
+ 右子树递归找到根节点(i,right)

+ 变种题目
+ 返回父节点索引数组
+ 每个元素,两边找(都有可能)
+ 往右找第一个比当前元素大的为父节点
+ 当前为左子树
+ 往左找第一个比当前元素大的为父节点
+ 当前为右子树
+ 取小的那个父节点
+ 大的是上层的

+ 单调栈
+ 暴力,每个位置扫两遍
+ 栈,扫描一遍
+ 用栈求左、右边第一个比它大的数
+ 实现
+ 栈从下往上,维护单调递减
+ 扫描元素
+ 判断栈顶元素是否大于当前
+ 小于栈顶,入栈
+ 栈顶元素为当前元素的左边第一个比它大的数
+ 大于栈顶,弹出栈顶元素
+ 当前元素设置为栈顶元素的右边第一个比它大的数
+ 小的元素全部出栈
+ 保证大的入栈
+ 原理
+ 能push进去,下面的元素比它大
+ 能pop出去,即将push的元素比它大

+ 反过来求?
+ 左小,右小

+ 实现
+ 准备
+ 栈、两个数组(左大右大)
+ 扫描元素,维护两个数组
+ 栈顶小,循环弹出
+ 设置栈顶右大为当前元素
+ 栈顶大,入栈
+ 设置栈顶为当前元素左大
+ 若栈顶为空,不设置
+ 设置父节点索引
+ 比较两个数组,找出最小的作为父节点
+ 两个for循环跟一个一样
+ 执行的东西一样

image-20220402134743050

image-20220402134753678

739、每日温度

image-20220402134834567

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
+ 找到下次升温天数(和右大索引距离)
+ 直接复制过来,单调栈
+ 只记录右大
+ 设置结果为 右大索引-当前索引
+ 倒推法
+ 思路
+ 从右往左遍历元素
+ 类似动态规划
+ 如果j>i的值
+ j是右大
+ 如果j<i的值
+ 寻找j的右大继续判断
+ 找到j的右大作为新值
+ 找到j对应升温的天数
+ 如果仍然小,则继续移动,重新进入判断
+ 若找不到,则设为0
+ 如果j=i的值
+ 同样寻找j的右大
+ 实现
+ 倒序遍历数组
+ 循环(true):必然会结束
+ 如果j比当前元素大的
+ j设为当前元素右大
+ 结束循环
+ 如果不存在右大
+ 设为0
+ 结束循环
+ 如果j与当前元素相等
+ 找到j的右大就是当前元素的右大
+ 结束循环
+ 如果j小于当前元素
+ j的右大作为新j
+ 继续循环
+ 原理
+ 动态规划的思想
+ 从后往前推
+ 前面的值依靠后面的值进行计算
+ 代码简化
+ 等于情况和小于情况合并
+ 等于也要找后面的元素,小于也要找后面的元素

image-20220402135055695

练习

42接雨水、232…

总结

1
2
3
4
+ 单调栈
+ 求左大右大
+ 双端队列、单调队列
+ 滑动窗口最值