复杂度
字符串
正则表达式
搜索
BM
模式匹配
暴力匹配
KMP
思想:每当一趟匹配过程中出现字符比较不等,不需要回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后,继续进行比较。时间复杂度O(n+m)
// 获得字符串t的next函数值public int[] next(char[] t) {int[] next = new int[t.length];next[0] = -1;int i = 0;int j = -1;while (i < t.length - 1) {if (j == -1 || t[i] == t[j]) {i++;j++;if (t[i] != t[j]) {next[i] = j;} else {next[i] = next[j];}} else {j = next[j];}}return next;}// s主串,t模式串,若匹配成功,返回下标,否则返回-1public int kmp(char[] s, char[] t) {int[] next = next(t);int i = 0;int j = 0;while (i <= s.length - 1 && j <= t.length - 1) {if (j == -1 || s[i] == t[j]) {i++;j++;} else {j = next[j];}}if (j < t.length) {return -1;} elsereturn i - t.length; // 返回模式串在主串中的头下标}
多模式匹配
-
Trie树★
又称字典树、前缀树、单词查找树、键树,多叉哈希树 Trie树典型应用是用于快速检索(最长前缀匹配),统计,排序和保存大量的字符串,所以经常被搜索引擎系统用于文本词频统计,搜索提示等场景。它的优点是最大限度地减少无谓的字符串比较,查询效率比较高。
前缀树 ```java public void add(TrieNode root, String word) { TrieNode cur = root; for (int i = 0; i < word.length(); ++i) {
char ch = word.charAt(i);if (cur.child[ch - 'a'] == null) {cur.child[ch - 'a'] = new TrieNode();}cur = cur.child[ch - 'a'];
} ++cur.frequency; }
public void find(TrieNode cur) { // 常用树的回溯或者DFS进行搜索 }
class TrieNode { int frequency; // 频数统计(可选) TrieNode[] child;
public TrieNode() {frequency = 0;child = new TrieNode[26];}
}
- 后缀树<a name="sgQrE"></a>## 线性表<a name="0R9wV"></a>### 数组<a name="zQq0e"></a>#### 树状数组<a name="m5mWc"></a>#### 矩阵<a name="xBPMJ"></a>#### 轮转数组★<a name="ATnYL"></a>### 列表(动态数组)<a name="rzhA2"></a>### 链表★<a name="4afe0b3f"></a>#### 单(双)向链表<a name="yTDqS"></a>#### 跳舞链<a name="cYIHW"></a>#### 快慢指针<a name="dzF16"></a>#### 链表反转★<a name="EY9ra"></a>#### 链表成环> 求是否成环、环节点<a name="cP8tO"></a>### 跳表<a name="kux0m"></a>### 队列<a name="7iXEg"></a>#### 普通队列<a name="15ec8cc5"></a>#### 阻塞队列<a name="3WfgB"></a>#### 并发队列<a name="e91ccb7d"></a>#### 双端队列<a name="H3aVG"></a>#### 优先队列<a name="sjvvl"></a>#### 多级反馈队列<a name="IEIjJ"></a>### 栈★<a name="Zy64J"></a>#### 单调栈> MonotonousStack<a name="09sot"></a>##### 思路适用于求解第一个大/小于其本身的位置的题目(常用在数组中求大小/最值问题,或者这些问题的隐含问题中)<br />常用技巧:哨兵法(简化代码)| 求解的问题 | 遍历方向 | 维护单调性(栈底->栈顶) || --- | --- | --- || 左侧第一个更大 | 从右到左 | 单调递减 || 左侧第一个更小 | 从右到左 | 单调递增 || 右侧第一个更大 | 从左到右 | 单调递减 || 右侧第一个更小 | 从左到右 | 单调递增 |<a name="hgz9L"></a>##### 求解每个位置的模板```java// 问题一:给定一个数组,请你返回每个元素右边第一个大于它的元素,如果不存在则返回-1public static int[] getFirstRightMax(int[] arr) {if(arr == null || arr.length == 0) return null;Deque<Integer> stack = new LinkedList<Integer>();int[] res = new int[arr.length];int i = 0;while(i < arr.length) {if(stack.isEmpty() || arr[stack.peek()] >= arr[i]) {stack.push(i);i++;} else {res[stack.pop()] = arr[i];}}// 遍历走完之后,栈中可能还剩余数组while(!stack.isEmpty()){res[stack.pop()] = -1;}return res;}// 问题二:给定一个数组,请你返回每个元素左边第一个大于它的元素,如果不存在则返回-1public static int[] getFirstLeftMax(int[] arr) {if(arr == null || arr.length == 0) return null;Deque<Integer> stack = new LinkedList<Integer>();int[] res = new int[arr.length];int i = 0;while(i < arr.length) {if(stack.isEmpty() || arr[stack.peek()] >= arr[i]) {stack.push(i);i++;} else {res[stack.pop()] = stack.isEmpty() ? -1 : arr[stack.peek()];}}while(!stack.isEmpty()){res[stack.pop()] = stack.isEmpty() ? -1 : arr[stack.peek()];}return res;}
简单维护单调栈模板
int[] nums = {3, 4, 2, 5, 6, 0, 1};Deque<Integer> stack = new LinkedList<>();int tmp = -1;for (int i = 0; i < n; ++i) {while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {tmp = stack.pop(); // 可以记录下次大值的位置}stack.push(i);}
练习
- 接雨水(困难)
- 每日温度(中等)
- 下一个更大元素 I(简单)
- 去除重复字母(困难)
- 股票价格跨度(中等)
- 移掉K位数字
不用计算连通量
private class UnionFind {private int[] parents;UnionFind(int n) {parents = new int[n];for (int i = 0; i < n; ++i) {parents[i] = i;}}public int find(int x) {return parents[x] == x ? x : parents[x] = find(parents[x]);}public void union(int a, int b) {parents[find(a)] = find(b);}}
计算连通量
private class UnionFind {private int[] parents;private int count;UnionFind(int n) {parents = new int[n];for (int i = 0; i < n; ++i) {parents[i] = i;}count = n;}public int find(int x) {return parents[x] == x ? x : parents[x] = find(parents[x]);}public void union(int a, int b) {int pa = find(a);int pb = find(b);if (pa != pb) {count--;}parents[pa] = pb;}}
散列表/哈希表★
散列函数
直接定址法
- 数字分析法
- 除留余数法
- 分段叠加法
- 平方取中法
-
冲突解决
开放寻址法
- 链地址法
- 再哈希法
-
过滤器
布隆过滤器
-
位图
动态扩容
树
二叉树
二叉树递归
private static List<Integer> result = new ArrayList<>();public static List<Integer> DFS(TreeNode root) {if (root == null) {return null;}result.add(root.val);if (root.left != null) {DFS(root.left);}if (root.right != null) {DFS(root.right);}return result;}
二叉树非递归 LinkedList
public static List<Integer> DFS(TreeNode root) {if (root == null) {return null;}Stack<TreeNode> stack = new Stack<>();stack.push(root);List<Integer> result = new ArrayList<>();while (!stack.isEmpty()) {TreeNode treeNode = stack.pop();result.add(treeNode.val);if (treeNode.right != null) {stack.push(treeNode.right);}if (treeNode.left != null) {stack.push(treeNode.left);}}return result;}
二叉树的最近公共祖先★
二叉查找树★
前序遍历
public static void preOrder(TreeNode root){if(root != null){System.out.print(root.val + " ");preOrder(root.left);preOrder(root.right);}}
public static ArrayList preOrder1(TreeNode root){Stack<TreeNode> stack = new Stack<TreeNode>();ArrayList alist = new ArrayList();TreeNode p = root;while(p != null || !stack.empty()){while(p != null){alist.add(p.val);stack.push(p);p = p.left;}if(!stack.empty()){TreeNode temp = stack.pop();p = temp.right;}}return alist;}
中序遍历
public static void inOrder(TreeNode root){if(root != null){inOrder(root.left);System.out.print(root.val + " ");inOrder(root.right);}}
public static ArrayList inOrder1(TreeNode root){ArrayList alist = new ArrayList();Stack<TreeNode> stack = new Stack<TreeNode>();TreeNode p = root;while(p != null || !stack.empty()){while(p != null){stack.push(p);p = p.left;}if(!stack.empty()){TreeNode temp = stack.pop();alist.add(temp.val);p = temp.right;}}return alist;}
后序遍历
public static void postOrder(TreeNode root){if(root != null){postOrder(root.left);postOrder(root.right);System.out.print(root.val + " ");}}
public static ArrayList postOrder1(TreeNode root){ArrayList alist = new ArrayList();Stack<TreeNode> stack = new Stack<TreeNode>();if(root == null)return alist;TreeNode cur,pre = null;stack.push(root);while(!stack.empty()){cur = stack.peek();if((cur.left == null && cur.right == null) || (pre != null && (cur.left == pre || cur.right == pre))){TreeNode temp = stack.pop();alist.add(temp.val);pre = temp;}else{if(cur.right != null)stack.push(cur.right);if(cur.left != null)stack.push(cur.left);}}return alist;}
层序遍历
private static void levelOrder(TreeNode root) {Queue<TreeNode> queue = new LinkedList<TreeNode>();if(root == null)return;queue.offer(root);while(!queue.isEmpty()){TreeNode temp = queue.poll();System.out.print(temp.val + " ");if(temp.left != null)queue.offer(temp.left);if(temp.right != null)queue.offer(temp.right);}}
Morris遍历
完全二叉树
平衡二叉树
平衡二叉查找树
AVL树
红黑树
线段树★
https://www.cnblogs.com/AC-King/p/7789013.html
- 主席树
多路查找树
B-树
B+树
B*树
2-3树/2-3-4树
哈夫曼树与编码
堆★
大(小)顶堆
二项堆
优先队列
斐波那契堆
可并堆
最近公共祖先
图
存储
邻接矩阵
邻接表
最短路径
单源最短路
Dijkstra算法★
T-O(n^2+e)/S-O(n+e)
Floyd-Warshall算法
T-O(n3)/S-O(n2)
Bellman-Ford算法★
T-O(ne)/S-O(n)
