链表
单链表
使用数组来进行单链表的操作、
模板:
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
| static int N = 100010; static int e[] = new int[N]; static int ne[] = new int[N]; static int idx; static int head;
public static void init(){ head = -1; idx = 0; }
public static void addHead(int a){ e[idx] = a; ne[idx] = head; head = idx; idx++; }
public static void add(int k,int a){ e[idx] = a; ne[idx] = ne[k]; ne[k] = idx; idx++; }
public static void remove(int k){ ne[k] = ne[ne[k]]; }
|
例题:
代码为:
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 63
| import java.util.*; import java.io.*; public class Main{ static int N = 100010; static int e[] = new int[N]; static int ne[] = new int[N]; static int idx; static int head;
public static void init(){ head = -1; idx = 0; }
public static void addHead(int a){ e[idx] = a; ne[idx] = head; head = idx; idx++; }
public static void add(int k,int a){ e[idx] = a; ne[idx] = ne[k]; ne[k] = idx; idx++; }
public static void remove(int k){ ne[k] = ne[ne[k]]; }
public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); int n = Integer.parseInt(br.readLine()) ; init(); while (n-->0){ String s[] = br.readLine().split(" "); char p = s[0].charAt(0); if (p=='H'){ int a = Integer.parseInt(s[1]); addHead(a); }else if (p=='D'){ int k = Integer.parseInt(s[1]); if (k==0) head = ne[head]; else remove(k-1); }else { int k = Integer.parseInt(s[1]); int a = Integer.parseInt(s[2]); add(k-1,a); } } for (int i = head;i!=-1;i=ne[i]){ pw.print(e[i]+" "); } pw.flush(); } }
|
双链表
模板;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public static void init(){ r[0] = 1; l[1] = 0; idx = 2; } public static void add(int k,int a){ e[idx] = a; l[idx] = k; r[idx] = r[k]; l[r[k]] = idx; r[k] = idx; idx++; } public static void delete(int k){ l[r[k]] = l[k]; r[l[k]] = r[k]; }
|
例题:
代码为:
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 63
| import java.io.*; import java.util.*; public class Main{ static int N = 100010; static int e[] = new int[N]; static int l[] = new int[N]; static int r[] = new int[N]; static int idx; public static void init(){ r[0] = 1; l[1] = 0; idx = 2; } public static void add(int k,int a){ e[idx] = a; l[idx] = k; r[idx] = r[k]; l[r[k]] = idx; r[k] = idx; idx++; } public static void delete(int k){ l[r[k]] = l[k]; r[l[k]] = r[k]; }
public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); init(); int n = Integer.parseInt(br.readLine()); while (n-->0){ String s[] = br.readLine().split(" "); String op = s[0]; if (op.equals("D")){ int k = Integer.parseInt(s[1]); delete(k+1); }else if (op.equals("IL")){ int k = Integer.parseInt(s[1]); int a = Integer.parseInt(s[2]); add(l[k+1],a); }else if (op.equals("IR")){ int k = Integer.parseInt(s[1]); int a = Integer.parseInt(s[2]); add(k+1,a); }else if (op.equals("L")){ int a = Integer.parseInt(s[1]); add(0,a); }else if (op.equals("R")){ int a = Integer.parseInt(s[1]); add(l[1],a); } } for (int i = r[0];i!=1;i=r[i]){ System.out.print(e[i]+" "); } } }
|
栈
数组模拟栈的插入,弹出,是否为空,查询栈顶元素、
模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| static int N = 100010; static int a[] = new int[N]; static int t = 0;
public static void push(int x){ a[++t] = x; }
public static void pop(){ if (t>0) { --t; } }
public static String isEmpty(){ if (t==0) return "YES"; else return "NO"; }
public static int query(){ return a[t]; }
|
表达式求值也使用了栈的数据结构,就是给出一个字符串的表达式,求出计算结果,详细的请见,表达式求值那个文章。
单调栈
运用的场景:
- 给定一个序列,求在这个序列中,每个数的左边(或右边),离它最近的(比它大或小)的数在什么地方。
模板为“:
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
| for (int i = 0;i<n;i++){ int t = scanner.nextInt(); while (tt!=0 && a[tt]>=t) tt--; if (tt!=0) System.out.print(a[tt]+" "); else System.out.print(-1+" "); a[++tt] = t; }
Map<Integer,Integer> map = new HashMap<>(); Deque<Integer> deque = new ArrayDeque<>(); int len1 = nums1.length; int len2 = nums2.length; for (int i = len2-1;i>=0;i--){ int t = nums2[i]; while (!deque.isEmpty() && deque.peek()<=t) deque.pop(); if (!deque.isEmpty()) { map.put(t,deque.peek()); }else { map.put(t,-1); } deque.push(t); }
|
例题:
代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import java.util.*; public class Main{ static int N = 100010; static int a[] = new int[N]; public static void main(String[] args) { int tt = 0; Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); for (int i = 0;i<n;i++){ int t = scanner.nextInt(); while (tt!=0 && a[tt]>=t){ tt--; } if (tt!=0) System.out.print(a[tt]+" "); else System.out.print(-1+" "); a[++tt] = t; } } }
|
队列
数组模拟队列
模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int hh = 0,tt = -1;
public static void add(int a){ q[++tt] = a; } public static void pop(){ hh++; } public static String isEmpty(){ if (hh<=tt) return "NO"; else return "YES"; } public static int query(){ return q[hh]; }
|
单调队列
应用场景
最大值和最小值分开来做,两个for循环搞定。步骤为:
- 解决队首已经出窗口的问题;
- 解决队尾与当前元素a[i]不满足单调性的问题;
- 将当前元素下标加入队尾;
- 如果满足条件则输出结果;
模板”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| int hh = 0;int tt = -1;
for (int i = 0;i<n;i++){ if (hh<=tt && i-q+1>queue[hh]) hh++; while (hh<=tt && a[queue[tt]]>=a[i]) tt--; queue[++tt] = i; if (i>=q-1) pw.print(a[queue[hh]]+" "); } pw.println();
hh = 0;tt = -1; for (int i = 0;i<n;i++){ if (hh<=tt && i-q+1>queue[hh]) hh++; while (hh<=tt && a[queue[tt]] <= a[i]) tt--; queue[++tt] = i; if (i>=q-1) pw.print(a[queue[hh]]+" "); }
|
例题:
给定一个大小为 n≤10的6次幂的数组。有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。你只能在窗口中看到 k 个数字。每次滑动窗口向右移动一个位置。求滑动窗口中的最小值和最大值:
输入样例:
输出样例:
1 2
| -1 -3 -3 -3 3 3 3 3 5 5 6 7
|
代码为:
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
| import java.util.*; import java.io.*; public class Main{ static int N = 1000010; static int a[] = new int[N]; static int queue[] = new int[N]; public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); String s[] = br.readLine().split(" "); int n = Integer.parseInt(s[0]); int q = Integer.parseInt(s[1]); String ss[] = br.readLine().split(" "); for (int i = 0;i<n;i++){ a[i] = Integer.parseInt(ss[i]); } int hh = 0;int tt = -1; for (int i = 0;i<n;i++){ if (hh<=tt && i-q+1>queue[hh]){ hh++; } while (hh<=tt && a[queue[tt]]>=a[i]) tt--; queue[++tt] = i; if (i>=q-1) pw.print(a[queue[hh]]+" "); } pw.println(); hh = 0;tt = -1; for (int i = 0;i<n;i++){ if (hh<=tt && i-q+1>queue[hh]){ hh++; } while (hh<=tt && a[queue[tt]] <= a[i]) tt--; queue[++tt] = i; if (i>=q-1) pw.print(a[queue[hh]]+" "); } pw.flush(); } }
|
KMP算法
没弄懂,,背就完了
模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
for(int i = 2 , j = 0; i <= pn ; i ++ ){ while(j > 0 && p[i] != p[j+1]) j = ne[j]; if(p[i] == p[j+1]) j ++ ; ne[i] = j; }
for(int i = 1 ,j = 0 ; i <= sm ; i ++ ){ while(j > 0 && s[i] != p[j+1]) j = ne[j]; if(s[i] == p[j+1]) j++; if(j == pn){ pw.write((i - pn) + " "); j = ne[j]; } }
|
例题:
代码为:
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
| import java.io.*; import java.util.*; public class Main{ static int N = 100010; static int M = 1000010; static int ne[] = new int[N]; static char s[] = new char[M]; static char p[] = new char[N]; public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); int pn = Integer.parseInt(br.readLine()); String pp = br.readLine(); for (int i = 1;i<=pn;i++){ p[i] = pp.charAt(i-1); } int sm = Integer.parseInt(br.readLine()); String ss = br.readLine(); for (int i = 1;i<=sm;i++){ s[i] = ss.charAt(i-1); } for(int i = 2 , j = 0; i <= pn ; i ++ ){ while(j > 0 && p[i] != p[j+1]) j = ne[j]; if(p[i] == p[j+1]) j ++ ; ne[i] = j; }
for(int i = 1 ,j = 0 ; i <= sm ; i ++ ){ while(j > 0 && s[i] != p[j+1]) j = ne[j]; if(s[i] == p[j+1]) j++; if(j == pn){ pw.write((i - pn) + " "); j = ne[j]; } } pw.flush(); } }
|
Trie树
Trie树又称字典树、单词查找树。是一种能够高效存储和查找字符串集合的数据结构。
存储字符串:
Trie树的代码具体分析:
模板代码“:
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
| static int N = 10010; static int son[][] = new int[N][26]; static int cnt[] = new int[N]; static int idx = 0; public static void insert(String s){ int len = s.length(); int p = 0; for (int i = 0;i<len;i++){ int u = s.charAt(i)-'a'; if (son[p][u]==0) son[p][u] = ++idx; p = son[p][u]; } cnt[p]++; } public static void query(String s){ int len = s.length(); int p = 0; for (int i = 0;i<len;i++){ int u = s.charAt(i)-'a'; if (son[p][u]==0){ System.out.println(0); return; } p = son[p][u]; } System.out.println(cnt[p]); }
|
例题;
代码为:
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
| import java.util.*; import java.io.*; public class Main{ static int N = 10010; static int son[][] = new int[N][26]; static int cnt[] = new int[N]; static int idx = 0; public static void insert(String s){ int len = s.length(); int p = 0; for (int i = 0;i<len;i++){ int u = s.charAt(i)-'a'; if (son[p][u]==0) son[p][u] = ++idx; p = son[p][u]; } cnt[p]++; } public static void query(String s){ int len = s.length(); int p = 0; for (int i = 0;i<len;i++){ int u = s.charAt(i)-'a'; if (son[p][u]==0){ System.out.println(0); return; } p = son[p][u]; } System.out.println(cnt[p]); }
public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); while (n-->0){ String p[] = br.readLine().split(" "); if (p[0].equals("I")) insert(p[1]); else if (p[0].equals("Q")) { query(p[1]); } } } }
|
并查集
在上述的问题中,问题2的时间复杂度较高,因为遍历集合编号,只要是和数的高度成正比的,因此每次遍历需要很多的时间复杂度,因此可以进行优化。
路径压缩优化:
代码模板:
一:朴素并查集
1 2 3 4 5 6 7 8 9 10 11 12 13
| static int p[] = new int[N];
public static int find(int x){ if (p[x]!=x) p[x] = find(p[x]); return p[x]; }
for (int i =0;i<n;i++){ p[i] = i;}
p[find(a)] = find(b);
|
二:维护集合里面元素的个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static int p[] = new int[N]; static int size[] = new int[N];
public static int find(int x){ if (p[x]!=x) p[x] = find(p[x]); return p[x]; }
for (int i =0;i<n;i++){ p[i] = i; size[i] = 1; }
if(find(a)!=find(b)) size[find(b)] += size[find(a)]; p[find(a)] = find(b);
|
连通块:如果可以从A走到B。并且B也可以走到A,就说明A和B在同一个连通块当中的。
三:维护到根节点距离的并查集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| int p[N], d[N];
int find(int x) { if (p[x] != x) { int u = find(p[x]); d[x] += d[p[x]]; p[x] = u; } return p[x]; }
for (int i = 1; i <= n; i ++ ) { p[i] = i; }
p[find(a)] = find(b);
|
例题:
代码为:
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
| import java.util.*; import java.io.*; public class Main{ static int N = 100010; static int p[] = new int[N]; public static int find(int x){ if (p[x]!=x) p[x] = find(p[x]); return p[x]; } public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); String s[] = br.readLine().split(" "); int n = Integer.parseInt(s[0]); int m = Integer.parseInt(s[1]); for (int i =0;i<n;i++){ p[i] = i; } while (m-->0){ String ss[] = br.readLine().split(" "); if (ss[0].equals("M")){ int a = Integer.parseInt(ss[1]); int b = Integer.parseInt(ss[2]); p[find(a)] = find(b); }else { int a = Integer.parseInt(ss[1]); int b = Integer.parseInt(ss[2]); if (find(a)==find(b)) pw.println("Yes"); else pw.println("No"); } } pw.flush(); } }
|
例题2:连通块中点的数量
代码为:
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
| import java.util.*; import java.io.*; public class Main{ static int N = 100010; static int p[] = new int[N]; static int size[] = new int[N]; public static int find(int x){ if (p[x]!=x) p[x] = find(p[x]); return p[x]; } public static void main(String[] args) throws IOException { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int m = scanner.nextInt(); for (int i = 0;i<n;i++){ p[i] = i; size[i] = 1; } while (m-->0){ String ss = scanner.next(); if (ss.equals("C")) { int a = scanner.nextInt(); int b = scanner.nextInt(); if (find(a)==find(b)) continue; else { size[find(b)] = size[find(b)] + size[find(a)]; p[find(a)] = find(b); } }else if (ss.equals("Q1")){ int a = scanner.nextInt(); int b = scanner.nextInt(); if (find(a)==find(b)){ System.out.println("Yes"); }else { System.out.println("No"); } }else { int a = scanner.nextInt(); System.out.println(size[find(a)]); } } } }
|
例题3:
代码为:
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
| import java.util.*; import java.io.*; public class Main{ static int N = 50010; static int p[] = new int[N]; static int d[] = new int[N]; public static int find(int x){ if (p[x]!=x){ int t = find(p[x]); d[x] = d[x] + d[p[x]]; p[x] = t; } return p[x]; } public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); String s[] = br.readLine().split(" "); int n = Integer.parseInt(s[0]); int k = Integer.parseInt(s[1]); for (int i = 0;i<n;i++) p[i] = i; int res = 0; while (k-- >0){ String s2[] = br.readLine().split(" "); int t = Integer.parseInt(s2[0]); int a = Integer.parseInt(s2[1]); int b = Integer.parseInt(s2[2]); if (a>n || b>n) res++; else{ int pa = find(a); int pb = find(b); if (t==1){ if (pa==pb && (d[a]-d[b])%3!=0) res++; else if(pa!=pb){ p[pa] = pb; d[pa] = d[b] - d[a]; } }else { if (pa==pb && (d[a]-d[b]-1)%3!=0) res++; else if(pa!=pb){ p[pa] = pb; d[pa] = d[b] - d[a] + 1; } } } } pw.println(res); pw.flush(); } }
|
堆
其中堆中包含两个操作,一个是down,另一个是up。
down:当修改堆中的数字使其变大的时候,进行down操作,将该节点的根,左子树,右子树,进行比较,如果左右子节点比修改后的小,则与最小的数进行交换,。依次进行这个操作,直到又形成一个堆。
up:当变小一个堆中的数字。进行up操作,与它的根节点进行比较,如果小于根节点,则交换,依次比较。
在数组中,如果根的下标为x,则根的左儿子下标为2x,根的右儿子下标为2x+1。
ph[k] = u:第k个插入的数,在堆中的下标是u;
hp[u] = k:堆中下标为u的,是第k个插入的数;