模板
# 快排
1. 找到中间的那个数字x,定义左右端点
2. 递归处理要求x的左边都比x小,右边都比x大
3. 找到x左边第一个大于他的和右边第一个小于他的然后交换
4. 递归剩下两部分
void quickSort(int[] q, int l, int r) {
if(l >= r) return ;
int x = q[l + r >> 1], i = l - 1, j = r + 1;
while(i < j) {
while(q[ ++ i] < x);
while(q[ -- j] > x);
if (i < j) {
swap(q, i, j);
}
}
quickSort(q, l, j);
quickSrot(q, j + 1, r);
}
双指针
for (int i = 0, j = 0; i < n; i ++) {
while(j < n && check(i,j)) j ++;
}
二分
从左边靠近寻找的x
int l = 0, r = n - 1;
while(l < r) {
int mid = l + r >> 1;
if (nums[mid] < x) l = mid + 1;
else r = mid;
}
从右边靠近寻找的x
int l = 0, r = n - 1;
while(l < r) {
int mid = l + r + 1;
if (nums[mid] > x) r = mid - 1;
else l = mid;
}
单调栈
求x左边或者右边最靠近他的最大/小的值或者序号
for (int i = 0; i < n; i ++) {
while(!stk.isEmpty() && stk.peek() >= x) stk.pop();
if (!stk.isEmpty()) sout(stk.peek());
else sout(-1);
stk.add(nums[i]);
}
单调队列
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++) {
if (hh <= tt && i - k + 1 > q[hh]) hh ++;
while(hh <= tt && a[q[hh]] >= a[i]) tt --;
q[++ tt] = i;
if (i - k + 1 >= 0) sout(q[hh]);
}
Trie树
把一个单词插入到树中,一共2个操作insert/query
1. 定义自己和儿子的关系 son[N][26]; // idx ~ 26
2. 定义全局指针idx
3. 定义出现次数或者is_end结束表示
void insert(String s) {
char[] c = s.toCharArray();
int p = 0;
for (int i = 0; i < c.length; i ++) {
int u = c[i] - 'a';
if (son[p][i] == 0) son[p][u] = ++ idx;
p = son[p][u];
}
cnt[p] ++;
}
# 出现多少次
int query(String s) {
char[] c = s.toCharArray();
int p = 0;
for (int i = 0; i < c.length; i ++) {
int u = c[i] - 'a';
if (son[p][u] == 0) return 0;
p = son[p][u];
}
return cnt[p];
}
并查集
合并集合 int a = find(nums[i]); int b = find(nums[b]);
p[a] = b; // 把a加入b的集合
int[] p = new int[N];
// 初始化 p[x] = x;
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
树与图的存储
int h[N], e[N], ne[N], idx;
Arrays.fill(h, -1);
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
树与图的dfs
int dfs(int u) {
st[u] = true;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (!st[j]) dfs(j);
}
}
树与图的bfs
Queue<Integer> q;
st[1] = true; // 表示1号点已经遍历过
q.add(1);
while(!q.isEmpty()) {
int t = q.pop();
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (!st[j]) {
st[j] = true;
q.push(j);
}
}
}
拓扑排序
图中的每条表(x, y) x都出现在y之前
bool topsort() {
Queue<Integer> q = new LinkedList<>();
// 1. 入度为0的入队
for(int i = 1; i <= n; i ++) {
if(ru[i] == 0) q.add(i);
}
while(!q.isEmpty()) {
int t = q.poll();
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (-- d[j] == 0) {
q.add(j);
}
}
}
// 是否能成拓扑排序 ru[i]是不是点都存在了
}
单源最短路spfa算法
int n;
int h[N], w[N], e[N], ne[N], idx;
int dist[N]; // 每个点到1号点的最短举例
boolean[N] st; // 每个点是否在队列中
int spfa() {
Arrays.fill(dist, 0x3f3f3f);
dist[1] = 0;
Queue<Integer> q = new LinkedList<>();
q.add(1);
st[1] = true;
while(!q.isEmpty()) {
int t =q.poll();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[t] + w[i]) {
dist[j] = dist[t] + w[i];
if (!st[j]) {
q.add(j);
st[j] = true;
}
}
}
}
return dist[n];
}
多源最短路
1. 初始化
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
if (i == j) d[i][j] = 0;
else d[i][j] = INF;
}
}
void floyd() {
for (int k = 1; k <= n; k ++) {
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]);
}
}
}
}
最小生成树
图转成树 边权最小
1. 并查集 + 按照边权排序
2. 遍历每条小边然后合并到一个集合中
int n, m; // 点数和边数
int p[N];
Edge[] eges = new int[M];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int kruskal() {
Arrays.sort(edges);
for (int i = 1; i <= n; i ++) p[i] = i;
int res = 0; int cnt = 0;
for (int i = 0; i < m; i ++) {
int a = edg[i].a;
int b = edg[i].b;
int c = edge[i].c;
a = find(a);
b = find(b);
if (a != b) {
p[a] = b;
res += c;
cnt ++;
}
}
if (cnt < n - 1) return INF;
return res;
}
class Edge implemenets Comparable<Edge> {
int a, b, c;
public int compareTo(Edge o) {
reutrn this.c - o.c;
}
}
图的遍历
地图模型:
蛇梯图 稠密图最短路
dist[n][m]: 记录 由某个点转移到某个点的次数
最小基因: s--> t
Map<String, Integer> 由字符串s--->变化到t 的次数
for (int i = 0; i < t.length(); i ++ ) {
for (int j = 'a'; j <= 'z'; j ++) {
xxx
}
}
回溯: 组合
求一个数组里某些组合
n个里面选k个
void dfs(int u, int n, int k, List<Integer> path) {
if (path.size() > k ) return ;
if (path.size() == k _ {
ans.add(new ArrayList<>(path));
return ;
}
for (int i = u; i <= n; i ++) {
path.add(i);
dfs(i + 1, n, k, path); // 这里如果是i则可重复选,i + 1则不可重复选
path.remove(path.size() - 1);
}
}
排列
void dfs(int u, int[] nums, List<Integer> path) {
if (st[i] == false) {
st[i] = true;
path.add(nums[i]);
dfs(u + 1, nums, path);
path.remove(path.size() - 1);
st[i] = false;
}
}