Files
LeetCode-Go/ctl/template/template.markdown
2021-03-15 22:56:38 +08:00

29 KiB
Raw Permalink Blame History

LeetCode in Go

LeetCode Online Judge is a website containing many algorithm questions. Most of them are real interview questions of Google, Facebook, LinkedIn, Apple, etc. and it always help to sharp our algorithm Skills. Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview. This repo shows my solutions in Go with the code style strictly follows the Google Golang Style Guide. Please feel free to reference and STAR to support this repo, thank you!

GitHub All Releases Support Go version

GitHub

支持 Progressive Web Apps 和 Dark Mode 的题解电子书《LeetCode Cookbook》 Online Reading

离线版本的电子书《LeetCode Cookbook》PDF Download here

通过 iOS / Android 浏览器安装 PWA 版《LeetCode Cookbook》至设备桌面随时学习

Data Structures

标识了 的专题是完成所有题目了的,没有标识的是还没有做完所有题目的

logo



数据结构 变种 相关题目 讲解文章
顺序线性表:向量
单链表 1. 双向链表
2. 静态链表
3. 对称矩阵
4. 稀疏矩阵
哈希表 1. 散列函数
2. 解决碰撞/填充因子
栈和队列 1. 广义栈
2. 双端队列
队列 1. 链表实现
2. 循环数组实现
3. 双端队列
字符串 1. KMP算法
2. 有限状态自动机
3. 模式匹配有限状态自动机
4. BM 模式匹配算法
5. BM-KMP 算法
6. BF 算法
1. 二叉树
2. 并查集
3. Huffman 树
数组实现的堆 1. 极大堆和极小堆
2. 极大极小堆
3. 双端堆
4. d 叉堆
树实现的堆 1. 左堆
2. 扁堆
3. 二项式堆
4. 斐波那契堆
5. 配对堆
查找 1. 哈希表
2. 跳跃表
3. 排序二叉树
4. AVL 树
5. B 树 / B+ 树 / B* 树
6. AA 树
7. 红黑树
8. 排序二叉堆
9. Splay 树
10. 双链树
11. Trie 树
12. R 树
-------------------------------------------- -------------------------------------------------------------------------------------------- --------------------------- -----------------------------------

Algorithm

算法 具体类型 相关题目 讲解文章
排序算法 1. 冒泡排序
2. 插入排序
3. 选择排序
4. 希尔 Shell 排序
5. 快速排序
6. 归并排序
7. 堆排序
8. 线性排序算法
9. 自省排序
10. 间接排序
11. 计数排序
12. 基数排序
13. 桶排序
14. 外部排序 - k 路归并败者树
15. 外部排序 - 最佳归并树
递归与分治 1. 二分搜索/查找
2. 大整数的乘法
3. Strassen 矩阵乘法
4. 棋盘覆盖
5. 合并排序
6. 快速排序
7. 线性时间选择
8. 最接近点对问题
9. 循环赛日程表
动态规划 1. 矩阵连乘问题
2. 最长公共子序列
3. 最大子段和
4. 凸多边形最优三角剖分
5. 多边形游戏
6. 图像压缩
7. 电路布线
8. 流水作业调度
9. 0-1 背包问题/背包九讲
10. 最优二叉搜索树
11. 动态规划加速原理
12. 树型 DP
贪心 1. 活动安排问题
2. 最优装载
3. 哈夫曼编码
4. 单源最短路径
5. 最小生成树
6. 多机调度问题
回溯法 1. 装载问题
2. 批处理作业调度
3. 符号三角形问题
4. n 后问题
5. 0-1 背包问题
6. 最大团问题
7. 图的 m 着色问题
8. 旅行售货员问题
9. 圆排列问题
10. 电路板排列问题
11. 连续邮资问题
搜索 1. 枚举
2. DFS
3. BFS
4. 启发式搜索
随机化 1. 随机数
2. 数值随机化算法
3. Sherwood 舍伍德算法
4. Las Vegas 拉斯维加斯算法
5. Monte Carlo 蒙特卡罗算法
1. 计算 π 值
2. 计算定积分
3. 解非线性方程组
4. 线性时间选择算法
5. 跳跃表
6. n 后问题
7. 整数因子分解
8. 主元素问题
9. 素数测试
图论 1. 遍历 DFS / BFS
2. AOV / AOE 网络
3. Kruskal 算法(最小生成树)
4. Prim 算法(最小生成树)
5. Boruvka 算法(最小生成树)
6. Dijkstra 算法(单源最短路径)
7. Bellman-Ford 算法(单源最短路径)
8. SPFA 算法(单源最短路径)
9. Floyd 算法(多源最短路径)
10. Johnson 算法(多源最短路径)
11. Fleury 算法(欧拉回路)
12. Ford-Fulkerson 算法(最大网络流增广路)
13. Edmonds-Karp 算法(最大网络流)
14. Dinic 算法(最大网络流)
15. 一般预流推进算法
16. 最高标号预流推进 HLPP 算法
17. Primal-Dual 原始对偶算法(最小费用流)18. Kosaraju 算法(有向图强连通分量)
19. Tarjan 算法(有向图强连通分量)
20. Gabow 算法(有向图强连通分量)
21. 匈牙利算法(二分图匹配)
22. HopcroftKarp 算法(二分图匹配)
23. kuhn munkras 算法(二分图最佳匹配)
24. Edmonds Blossom-Contraction 算法(一般图匹配)
1. 图遍历
2. 有向图和无向图的强弱连通性
3. 割点/割边
3. AOV 网络和拓扑排序
4. AOE 网络和关键路径
5. 最小代价生成树/次小生成树
6. 最短路径问题/第 K 短路问题
7. 最大网络流问题
8. 最小费用流问题
9. 图着色问题
10. 差分约束系统
11. 欧拉回路
12. 中国邮递员问题
13. 汉密尔顿回路
14. 最佳边割集/最佳点割集/最小边割集/最小点割集/最小路径覆盖/最小点集覆盖
15. 边覆盖集
16. 二分图完美匹配和最大匹配问题
17. 仙人掌图
18. 弦图
19. 稳定婚姻问题
20. 最大团问题
数论 1. 最大公约数
2. 最小公倍数
3. 分解质因数
4. 素数判定
5. 进制转换
6. 高精度计算
7. 整除问题
8. 同余问题
9. 欧拉函数
10. 扩展欧几里得
11. 置换群
12. 母函数
13. 离散变换
14. 康托展开
15. 矩阵
16. 向量
17. 线性方程组
18. 线性规划
几何 1. 凸包 - Gift wrapping
2. 凸包 - Graham scan
3. 线段问题
4. 多边形和多面体相关问题
NP 完全 1. 计算模型
2. P 类与 NP 类问题
3. NP 完全问题
4. NP 完全问题的近似算法
1. 随机存取机 RAM
2. 随机存取存储程序机 RASP
3. 图灵机
4. 非确定性图灵机
5. P 类与 NP 类语言
6. 多项式时间验证
7. 多项式时间变换
8. Cook定理
9. 合取范式的可满足性问题 CNF-SAT
10. 3 元合取范式的可满足性问题 3-SAT
11. 团问题 CLIQUE
12. 顶点覆盖问题 VERTEX-COVER
13. 子集和问题 SUBSET-SUM
14. 哈密顿回路问题 HAM-CYCLE
15. 旅行售货员问题 TSP
16. 顶点覆盖问题的近似算法
17. 旅行售货员问题近似算法
18. 具有三角不等式性质的旅行售货员问题
19. 一般的旅行售货员问题
20. 集合覆盖问题的近似算法
21. 子集和问题的近似算法
22. 子集和问题的指数时间算法
23. 子集和问题的多项式时间近似格式
------------ ------------------------------------------------------------------ ----------------------------------------------------------------- --------------------

LeetCode Problems

一. 个人数据

{{.PersonalData}}

二. 目录

{{.TotalNum}}

{{.AvailableTable}}


下面这些是免费的算法题,但是暂时还不能使用 Go 解答的:

暂无


三.分类

Array

Problems List in there

String

Problems List in there

Two Pointers

  • 双指针滑动窗口的经典写法。右指针不断往右移,移动到不能往右移动为止(具体条件根据题目而定)。当右指针到最右边以后,开始挪动左指针,释放窗口左边界。第 3 题,第 76 题,第 209 题,第 424 题,第 438 题,第 567 题,第 713 题,第 763 题,第 845 题,第 881 题,第 904 题,第 978 题,第 992 题,第 1004 题,第 1040 题,第 1052 题。
	left, right := 0, -1

	for left < len(s) {
		if right+1 < len(s) && freq[s[right+1]-'a'] == 0 {
			freq[s[right+1]-'a']++
			right++
		} else {
			freq[s[left]-'a']--
			left++
		}
		result = max(result, right-left+1)
	}
  • 快慢指针可以查找重复数字,时间复杂度 O(n),第 287 题。
  • 替换字母以后,相同字母能出现连续最长的长度。第 424 题。
  • SUM 问题集。第 1 题,第 15 题,第 16 题,第 18 题,第 167 题,第 923 题,第 1074 题。

Problems List in there

Linked List

  • 巧妙的构造虚拟头结点。可以使遍历处理逻辑更加统一。
  • 灵活使用递归。构造递归条件,使用递归可以巧妙的解题。不过需要注意有些题目不能使用递归,因为递归深度太深会导致超时和栈溢出。
  • 链表区间逆序。第 92 题。
  • 链表寻找中间节点。第 876 题。链表寻找倒数第 n 个节点。第 19 题。只需要一次遍历就可以得到答案。
  • 合并 K 个有序链表。第 21 题,第 23 题。
  • 链表归类。第 86 题,第 328 题。
  • 链表排序,时间复杂度要求 O(n * log n),空间复杂度 O(1)。只有一种做法,归并排序,至顶向下归并。第 148 题。
  • 判断链表是否存在环,如果有环,输出环的交叉点的下标;判断 2 个链表是否有交叉点,如果有交叉点,输出交叉点。第 141 题,第 142 题,第 160 题。

Problems List in there

Stack

  • 括号匹配问题及类似问题。第 20 题,第 921 题,第 1021 题。
  • 栈的基本 pop 和 push 操作。第 71 题,第 150 题,第 155 题,第 224 题,第 225 题,第 232 题,第 946 题,第 1047 题。
  • 利用栈进行编码问题。第 394 题,第 682 题,第 856 题,第 880 题。
  • 单调栈利用栈维护一个单调递增或者递减的下标数组。第 84 题,第 456 题,第 496 题,第 503 题,第 739 题,第 901 题,第 907 题,第 1019 题。

Problems List in there

Tree

Problems List in there

Dynamic Programming

Problems List in there

Backtracking

  • 排列问题 Permutations。第 46 题,第 47 题。第 60 题,第 526 题,第 996 题。
  • 组合问题 Combination。第 39 题,第 40 题,第 77 题,第 216 题。
  • 排列和组合杂交问题。第 1079 题。
  • N 皇后终极解法(二进制解法)。第 51 题,第 52 题。
  • 数独问题。第 37 题。
  • 四个方向搜索。第 79 题,第 212 题,第 980 题。
  • 子集合问题。第 78 题,第 90 题。
  • Trie。第 208 题,第 211 题。
  • BFS 优化。第 126 题,第 127 题。
  • DFS 模板。(只是一个例子,不对应任何题)
func combinationSum2(candidates []int, target int) [][]int {
	if len(candidates) == 0 {
		return [][]int{}
	}
	c, res := []int{}, [][]int{}
	sort.Ints(candidates)
	findcombinationSum2(candidates, target, 0, c, &res)
	return res
}

func findcombinationSum2(nums []int, target, index int, c []int, res *[][]int) {
	if target == 0 {
		b := make([]int, len(c))
		copy(b, c)
		*res = append(*res, b)
		return
	}
	for i := index; i < len(nums); i++ {
		if i > index && nums[i] == nums[i-1] { // 这里是去重的关键逻辑
			continue
		}
		if target >= nums[i] {
			c = append(c, nums[i])
			findcombinationSum2(nums, target-nums[i], i+1, c, res)
			c = c[:len(c)-1]
		}
	}
}
  • BFS 模板。(只是一个例子,不对应任何题)
func updateMatrix_BFS(matrix [][]int) [][]int {
	res := make([][]int, len(matrix))
	if len(matrix) == 0 || len(matrix[0]) == 0 {
		return res
	}
	queue := make([][]int, 0)
	for i, _ := range matrix {
		res[i] = make([]int, len(matrix[0]))
		for j, _ := range res[i] {
			if matrix[i][j] == 0 {
				res[i][j] = -1
				queue = append(queue, []int{i, j})
			}
		}
	}
	level := 1
	for len(queue) > 0 {
		size := len(queue)
		for size > 0 {
			size -= 1
			node := queue[0]
			queue = queue[1:]
			i, j := node[0], node[1]
			for _, direction := range [][]int{{-1, 0}, {1, 0}, {0, 1}, {0, -1}} {
				x := i + direction[0]
				y := j + direction[1]
				if x < 0 || x >= len(matrix) || y < 0 || y >= len(matrix[0]) || res[x][y] < 0 || res[x][y] > 0 {
					continue
				}
				res[x][y] = level
				queue = append(queue, []int{x, y})
			}
		}
		level++
	}
	for i, row := range res {
		for j, cell := range row {
			if cell == -1 {
				res[i][j] = 0
			}
		}
	}
	return res
}

Problems List in there

Problems List in there

Problems List in there

  • 二分搜索的经典写法。需要注意的三点:
    1. 循环退出条件,注意是 low <= high而不是 low < high。
    2. mid 的取值mid := low + (high-low)>>1
    3. low 和 high 的更新。low = mid + 1high = mid - 1。
func binarySearchMatrix(nums []int, target int) int {
	low, high := 0, len(nums)-1
	for low <= high {
		mid := low + (high-low)>>1
		if nums[mid] == target {
			return mid
		} else if nums[mid] > target {
			high = mid - 1
		} else {
			low = mid + 1
		}
	}
	return -1
}
  • 二分搜索的变种写法。有 4 个基本变种:
    1. 查找第一个与 target 相等的元素,时间复杂度 O(logn)
    2. 查找最后一个与 target 相等的元素,时间复杂度 O(logn)
    3. 查找第一个大于等于 target 的元素,时间复杂度 O(logn)
    4. 查找最后一个小于等于 target 的元素,时间复杂度 O(logn)
// 二分查找第一个与 target 相等的元素,时间复杂度 O(logn)
func searchFirstEqualElement(nums []int, target int) int {
	low, high := 0, len(nums)-1
	for low <= high {
		mid := low + ((high - low) >> 1)
		if nums[mid] > target {
			high = mid - 1
		} else if nums[mid] < target {
			low = mid + 1
		} else {
			if (mid == 0) || (nums[mid-1] != target) { // 找到第一个与 target 相等的元素
				return mid
			}
			high = mid - 1
		}
	}
	return -1
}

// 二分查找最后一个与 target 相等的元素,时间复杂度 O(logn)
func searchLastEqualElement(nums []int, target int) int {
	low, high := 0, len(nums)-1
	for low <= high {
		mid := low + ((high - low) >> 1)
		if nums[mid] > target {
			high = mid - 1
		} else if nums[mid] < target {
			low = mid + 1
		} else {
			if (mid == len(nums)-1) || (nums[mid+1] != target) { // 找到最后一个与 target 相等的元素
				return mid
			}
			low = mid + 1
		}
	}
	return -1
}

// 二分查找第一个大于等于 target 的元素,时间复杂度 O(logn)
func searchFirstGreaterElement(nums []int, target int) int {
	low, high := 0, len(nums)-1
	for low <= high {
		mid := low + ((high - low) >> 1)
		if nums[mid] >= target {
			if (mid == 0) || (nums[mid-1] < target) { // 找到第一个大于等于 target 的元素
				return mid
			}
			high = mid - 1
		} else {
			low = mid + 1
		}
	}
	return -1
}

// 二分查找最后一个小于等于 target 的元素,时间复杂度 O(logn)
func searchLastLessElement(nums []int, target int) int {
	low, high := 0, len(nums)-1
	for low <= high {
		mid := low + ((high - low) >> 1)
		if nums[mid] <= target {
			if (mid == len(nums)-1) || (nums[mid+1] > target) { // 找到最后一个小于等于 target 的元素
				return mid
			}
			low = mid + 1
		} else {
			high = mid - 1
		}
	}
	return -1
}
  • 在基本有序的数组中用二分搜索。经典解法可以解,变种写法也可以写,常见的题型,在山峰数组中找山峰,在旋转有序数组中找分界点。第 33 题,第 81 题,第 153 题,第 154 题,第 162 题,第 852 题
func peakIndexInMountainArray(A []int) int {
	low, high := 0, len(A)-1
	for low < high {
		mid := low + (high-low)>>1
		// 如果 mid 较大则左侧存在峰值high = m如果 mid + 1 较大则右侧存在峰值low = mid + 1
		if A[mid] > A[mid+1] {
			high = mid
		} else {
			low = mid + 1
		}
	}
	return low
}
  • max-min 最大值最小化问题。求在最小满足条件的情况下的最大值。第 410 题,第 875 题,第 1011 题,第 1283 题。

Problems List in there

Math

Problems List in there

Hash Table

Problems List in there

Sort

  • 深刻的理解多路快排。第 75 题。
  • 链表的排序,插入排序(第 147 题)和归并排序(第 148 题)
  • 桶排序和基数排序。第 164 题。
  • "摆动排序"。第 324 题。
  • 两两不相邻的排序。第 767 题,第 1054 题。
  • "饼子排序"。第 969 题。

Problems List in there

Bit Manipulation

  • 异或的特性。第 136 题,第 268 题,第 389 题,第 421 题,
x ^ 0 = x
x ^ 11111……1111 = ~x
x ^ (~x) = 11111……1111
x ^ x = 0
a ^ b = c  => a ^ c = b  => b ^ c = a (交换律)
a ^ b ^ c = a ^ (b ^ c) = (a ^ b^ c (结合律)
  • 构造特殊 Mask将特殊位置放 0 或 1。
 x 最右边的 n 位清零 x & ( ~0 << n )
获取 x 的第 n 位值(0 或者 1)(x >> n) & 1
获取 x 的第 n 位的幂值x & (1 << (n - 1))
仅将第 n 位置为 1x | (1 << n)
仅将第 n 位置为 0x & (~(1 << n))
 x 最高位至第 n ()清零x & ((1 << n) - 1)
将第 n 位至第 0 ()清零x & (~((1 << (n + 1)) - 1)
  • 有特殊意义的 & 位操作运算。第 260 题,第 201 题,第 318 题,第 371 题,第 397 题,第 461 题,第 693 题,
X & 1 == 1 判断是否是奇数(偶数)
X & = (X - 1) 将最低位(LSB) 1 清零
X & -X 得到最低位(LSB) 1
X & ~X = 0

Problems List in there

Union Find

  • 灵活使用并查集的思想,熟练掌握并查集的模板,模板中有两种并查集的实现方式,一种是路径压缩 + 秩优化的版本,另外一种是计算每个集合中元素的个数 + 最大集合元素个数的版本,这两种版本都有各自使用的地方。能使用第一类并查集模板的题目有:第 128 题,第 130 题,第 547 题,第 684 题,第 721 题,第 765 题,第 778 题,第 839 题,第 924 题,第 928 题,第 947 题,第 952 题,第 959 题,第 990 题。能使用第二类并查集模板的题目有:第 803 题,第 952 题。第 803 题秩优化和统计集合个数这些地方会卡时间,如果不优化,会 TLE。
  • 并查集是一种思想,有些题需要灵活使用这种思想,而不是死套模板,如第 399 题,这一题是 stringUnionFind利用并查集思想实现的。这里每个节点是基于字符串和 map 的,而不是单纯的用 int 节点编号实现的。
  • 有些题死套模板反而做不出来,比如第 685 题,这一题不能路径压缩和秩优化,因为题目中涉及到有向图,需要知道节点的前驱节点,如果路径压缩了,这一题就没法做了。这一题不需要路径压缩和秩优化。
  • 灵活的抽象题目给的信息,将给定的信息合理的编号,使用并查集解题,并用 map 降低时间复杂度,如第 721 题,第 959 题。
  • 关于地图,砖块,网格的题目,可以新建一个特殊节点,将四周边缘的砖块或者网格都 union() 到这个特殊节点上。第 130 题,第 803 题。
  • 能用并查集的题目,一般也可以用 DFS 和 BFS 解答,只不过时间复杂度会高一点。

Problems List in there

Sliding Window

  • 双指针滑动窗口的经典写法。右指针不断往右移,移动到不能往右移动为止(具体条件根据题目而定)。当右指针到最右边以后,开始挪动左指针,释放窗口左边界。第 3 题,第 76 题,第 209 题,第 424 题,第 438 题,第 567 题,第 713 题,第 763 题,第 845 题,第 881 题,第 904 题,第 978 题,第 992 题,第 1004 题,第 1040 题,第 1052 题。
	left, right := 0, -1

	for left < len(s) {
		if right+1 < len(s) && freq[s[right+1]-'a'] == 0 {
			freq[s[right+1]-'a']++
			right++
		} else {
			freq[s[left]-'a']--
			left++
		}
		result = max(result, right-left+1)
	}
  • 滑动窗口经典题。第 239 题,第 480 题。

Problems List in there

Segment Tree

  • 线段树的经典数组实现写法。将合并两个节点 pushUp 逻辑抽象出来了,可以实现任意操作(常见的操作有:加法,取 maxmin 等等)。第 218 题,第 303 题,第 307 题,第 699 题。
  • 计数线段树的经典写法。第 315 题,第 327 题,第 493 题。
  • 线段树的树的实现写法。第 715 题,第 732 题。
  • 区间懒惰更新。第 218 题,第 699 题。
  • 离散化。离散化需要注意一个特殊情况:假如三个区间为 [1,10] [1,4] [6,10],离散化后 x[1]=1,x[2]=4,x[3]=6,x[4]=10。第一个区间为 [1,4],第二个区间为 [1,2],第三个区间为 [3,4],这样一来,区间一 = 区间二 + 区间三,这和离散前的模型不符,离散前,很明显,区间一 > 区间二 + 区间三。正确的做法是:在相差大于 1 的数间加一个数,例如在上面 1 4 6 10 中间加 5即可 x[1]=1,x[2]=4,x[3]=5,x[4]=6,x[5]=10。这样处理之后区间一是 1-5 ,区间二是 1-2 ,区间三是 4-5 。
  • 灵活构建线段树。线段树节点可以存储多条信息,合并两个节点的 pushUp 操作也可以是多样的。第 850 题,第 1157 题。

线段树题型从简单到困难:

  1. 单点更新:
    HDU 1166 敌兵布阵 update:单点增减 query:区间求和
    HDU 1754 I Hate It update:单点替换 query:区间最值
    HDU 1394 Minimum Inversion Number update:单点增减 query:区间求和
    HDU 2795 Billboard query:区间求最大值的位子(直接把update的操作在query里做了)
  2. 区间更新:
    HDU 1698 Just a Hook update:成段替换 (由于只query一次总区间,所以可以直接输出 1 结点的信息)
    POJ 3468 A Simple Problem with Integers update:成段增减 query:区间求和
    POJ 2528 Mayors posters 离散化 + update:成段替换 query:简单hash
    POJ 3225 Help with Intervals update:成段替换,区间异或 query:简单hash
  3. 区间合并(这类题目会询问区间中满足条件的连续最长区间,所以PushUp的时候需要对左右儿子的区间进行合并):
    POJ 3667 Hotel update:区间替换 query:询问满足条件的最左端点
  4. 扫描线(这类题目需要将一些操作排序,然后从左到右用一根扫描线扫过去最典型的就是矩形面积并,周长并等题):
    HDU 1542 Atlantis update:区间增减 query:直接取根节点的值
    HDU 1828 Picture update:区间增减 query:直接取根节点的值

Problems List in there

Binary Indexed Tree

Problems List in there


Thank you for reading here. This is bonus. You can download my 《ACM-ICPC Algorithm Template》

♥️ Thanks

Thanks for your Star

Stargazers over time