单链表.
AcWing. 826.单链表
import java.util.Scanner;
public class Main{static int[] e = new int[100010];//结点i的值static int[] ne = new int[100010];//结点i的next指针static int idx,head;//head是头结点,idx存当前已经用到了哪个点public static void init(){idx = 0;head = -1;}//H向链表头插入一个数x;public static void add_head(int x){e[idx] = x;ne[idx] = head;head = idx++;} //I在第k位数后面插入一个数xpublic static void add(int k,int x){e[idx] = x;ne[idx] = ne[k];ne[k] = idx++;}//D删除第k个数后面得数public static void remove(int k){ne[k] = ne[ne[k]];}public static void main(String[] args){Scanner scan = new Scanner(System.in);int m = scan.nextInt();init();while(m -- > 0){//因为java中没有输入一个字符,所以用字符串转字符String s = scan.next();char op = s.charAt(0);if(op == 'H'){int x = scan.nextInt();add_head(x);}else if(op == 'D'){int k = scan.nextInt();if(k == 0) head = ne[head];else remove(k-1);}else {int k = scan.nextInt();int x = scan.nextInt();add(k-1,x);}}for(int i = head;i != -1;i = ne[i] ){System.out.print(e[i] + " ");}}
}
双链表
AcWing 827.双链表
import java.util.Scanner;
public class Main{static int N = 100010;static int[] e = new int[N];//元素static int[] r = new int[N];//右指针static int[] l = new int[N];//左指针static int idx;//删除第k位插入的数public static void remove(int k){r[l[k]] = r[k];l[r[k]] = l[k];}//这是在第k位数后面插入一个数xpublic static void add_all(int k,int x){e[idx] = x;r[idx] = r[k];l[idx] = k;l[r[k]] = idx;r[k] = idx++;}public static void main(String[] args){Scanner scan = new Scanner(System.in);int m = scan.nextInt();r[0] = 1;l[1] = 0;idx = 2;while(m -- > 0){String s = scan.next();char op = s.charAt(0);if(op == 'L'){int x = scan.nextInt();add_all(0,x);}else if(op == 'R'){int x = scan.nextInt();add_all(l[1],x);}else if(op == 'D'){int k = scan.nextInt();remove(k + 1);}else if(s.equals("IR")){int k = scan.nextInt();int x = scan.nextInt();add_all(k + 1,x);}else {int k = scan.nextInt();int x = scan.nextInt();add_all(l[k+1],x);}}for(int i = r[0];i != 1; i= r[i]){System.out.print(e[i] + " ");}}
}
栈
AcWing 828.模拟栈
import java.util.Scanner;
public class Main{public static void main(String[] args){Scanner scan = new Scanner(System.in);int m = scan.nextInt();int[] stl = new int[100010];int tt = 0;while(m -- > 0){String s = scan.next();if(s.equals("push")){int x= scan.nextInt();stl[++tt] = x;}else if(s.equals("pop")){tt--;}else if(s.equals("empty")){if(tt > 0){System.out.println("NO");}else System.out.println("YES");}else{System.out.println(stl[tt]);}}}
}
AcWing 3302.表达式求值
import java.util.*;
public class Main{public static void main(String[] args){Scanner scan = new Scanner(System.in);//以字符串形式输入表达式String s = scan.next();//map来添加运算符号进去,定义优先级Map<Character,Integer> map = new HashMap<>();map.put('+',1);map.put('-',1);map.put('*',2);map.put('/',2);Stack<Character> op = new Stack<>();//存运算符号Stack<Integer> num = new Stack<>();//存数字for(int i = 0 ; i < s.length(); i ++ ){char c = s.charAt(i);//判断c字符是不是数字if(Character.isDigit(c)){int x = 0,j = i;//数字可能会是多位数,while(j < s.length() && Character.isDigit(s.charAt(j))){x = x * 10 + s.charAt(j) - '0';j++;}num.push(x);//将数字x存入数字栈栈顶i = j - 1;//重新赋值i}else if(c == '('){op.push(c); // 将左括号存入字符栈栈顶}else if(c == ')'){//如果栈顶不等于左括号,一直计算下去;while(op.peek() != '('){eval(op,num);}op.pop(); // 将左括号弹出栈顶}else { //如果是正常字符while(!op.empty() && op.peek() != '(' && map.get(op.peek()) >= map.get(c)){eval(op,num);}op.push(c);}}while(!op.empty()) eval(op,num);System.out.println(num.peek());}public static void eval(Stack<Character> op,Stack<Integer> num){int b = num.pop();int a = num.pop();char c = op.pop();if(c == '+'){num.push(a+b);}else if(c == '-'){num.push(a-b);}else if(c == '*'){num.push(a*b);}else {num.push(a/b);}}
}
队列
AcWing 829. 模拟队列
import java.util.Scanner;
public class Main{public static void main(String[] args){Scanner scan = new Scanner(System.in);int m = scan.nextInt();//队列是在tt队尾插入元素,队头hh弹出元素int[] dl = new int[100010];int hh = 0;int tt = -1;while(m -- > 0){String s = scan.next();if(s.equals("push")){int x = scan.nextInt();dl[++tt] = x;}else if(s.equals("pop")){hh++;}else if(s.equals("empty")){if(hh <= tt) System.out.println("NO");else System.out.println("YES");}else {System.out.println(dl[hh]);}}}
}
单调栈
AcWing 830.单调栈
import java.util.*;
public class Main{public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int[] stk = new int[100010];int tt = 0;for(int i = 0 ; i < n ; i++ ){int x = scan.nextInt();//如果栈非空 ,栈顶元素>= x,那么说明栈顶这个数不满足需求,//因为比较的是左边第一个最近最小的数,所以就把stk[tt]弹出了;while(tt!=0 && stk[tt] >= x){tt--;}//如果弹出操作完了之后,栈不是空的,就输出栈顶元素,if(tt != 0) System.out.print(stk[tt]+" ");//否则就是栈是空的,输出-1else System.out.print("-1" + " ");//最后将x插入栈顶;stk[++tt] = x;}}
}
单调队列
AcWing 154.滑动窗口
import java.io.*;
public class Main{static int N = 1000010;static int n,k;//数组长度 和 滑动窗口长度static int hh,tt;//队头队尾static int[] q = new int[N];//q是队列 存的是 下标static int[] a = new int[N];public static void main(String[] args)throws IOException{BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));PrintWriter wt = new PrintWriter(new OutputStreamWriter(System.out));String[] st = bf.readLine().split(" ");n = Integer.parseInt(st[0]);k = Integer.parseInt(st[1]);String[] str = bf.readLine().split(" ");for(int i = 0 ; i < n ; i ++ ) a[i] = Integer.parseInt(str[i]);hh = 0;tt = - 1;//队头队尾 感觉像双指针for(int i = 0 ; i < n ; i ++ ){//i是 滑动窗口的 终点//队列非空 hh <= tt且 队头在起点之前 就出栈if(hh <= tt && q[hh] < i - k + 1) hh ++;//起点:i-k+1//这个while 就是 控制 这个队列的单调性递增!//非空 & 队尾元素 >= 新加进来的元素,就将队尾删掉,因 队头存的是窗口中最小的while(hh <= tt && a[q[tt]] >= a[i]) tt --; //求最小值所以保留最小值q[++ tt] = i;//当前 位置插到 队尾//不足k 不用判断 if(i >= k - 1) wt.print(a[q[hh]] + " ");}wt.println();hh = 0;tt = -1;for(int i = 0 ; i < n ; i ++){if(hh <= tt && q[hh] < i - k + 1) hh ++;while(hh <= tt && a[q[tt]] <= a[i]) tt --;//求最大值所以保留最大值q[++ tt] = i;if(i >= k - 1) wt.print(a[q[hh]] + " ");}wt.flush();//记得刷新流}
}
KMP
AcWing 831. KMP字符串
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Scanner;
public class Main{static int N = 100010;static int ne[] = new int[N];//kmp核心数组next[];public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));Integer n = Integer.parseInt(br.readLine());//输入p字符的长度String s1 = " " + br.readLine();//输入p字符串Integer m = Integer.parseInt(br.readLine());String s2 = " " + br.readLine();char[] a1 = s1.toCharArray();//创建p数组 存字符char[] a2 = s2.toCharArray();/*** ne[]:存储一个字符串以每个位置为结尾的‘可匹配最长前后缀’的长度。* next[i]=j 等价 p[1,j]=p[i-j+1,i]* 构建ne[]数组:* 1,初始化ne[1] = 0,i从2开始。当第一个数时就是0所以不用参与。默认是0;* 2,若匹配,s[i]=s[j+1]说明1~j+1是i的可匹配最长后缀,ne[i] = ++j;* 3,若不匹配,则从j的最长前缀位置+1的位置继续与i比较* (因为i-1和j拥有相同的最长前后缀,我们拿j的前缀去对齐i-1的后缀),* 即令j = ne[j],继续比较j+1与i,若匹配转->>2* 4,若一直得不到匹配j最终会降到0,也就是i的‘可匹配最长前后缀’的长度* 要从零开始重新计算*/for(int i = 2,j = 0;i <= n ;i++) {//这里判断从j是不是>=0,如果< 0,表示还没有前缀和后缀相等while(j!=0&&a1[i]!=a1[j+1]) j = ne[j]; //不相等就让j等于上一个数的next[];if(a1[i]==a1[j+1]) j++;//匹配 有一个相等,前缀和后缀相等长度为1.ne[i] = j;}/*** 匹配两个字符串:* 1,从i=1的位置开始逐个匹配,利用ne[]数组减少比较次数* 2,若i与j+1的位置不匹配(已知1~j匹配i-j~i-1),* j跳回ne[j]继续比较(因为1~j匹配i-j~i-1,所以1~ne[j]也能匹配到i-ne[j]~i-1)* 3,若匹配则j++,直到j==n能确定匹配成功* 4,成功后依然j = ne[j],就是把这次成功当成失败,继续匹配下一个位置*/for(int i = 1,j = 0; i <= m;i++) {while(j!=0&&a2[i]!=a1[j+1]) j = ne[j];if(a2[i]==a1[j+1]) j++;if(j==n) {//相等的数= 短的数组的长度,就说明该输出了j = ne[j];//输出之后,要继续往下面遍历对比,所以让j=上一个数的缀长度,bw.write(i-n+" ");}}/*** 时间复杂度:* 因为:j最多加m次,再加之前j每次都会减少且最少减一,j>0* 所以:while循环最多执行m次,若大于m次,j<0矛盾* 最终答案:O(2m)*///所有write下的内容,会先存在writers中,当启用flush以后,会输出存在其中的内容。//如果没有调用flush,则不会将writer中的内容进行输出。bw.flush();}
}
Trie:高效地存储和查找字符串集合的数据结构
AcWing 835. Trie字符串统计
import java.util.Scanner;
public class Main{static int N = 100010,idx = 0;//idx是当前用到的下标static int[][] song = new int[N][26];//每个点的儿子集合最多26个字母儿子static int[] cnt = new int[N];//存当前结尾的单词有多少个static char[] str = new char[N];//插入字符串public static void insert(char[] str){int p = 0; //下标0表示头结点,根节点for(int i = 0 ; i < str.length; i ++ ){// 将字符串每个字符a-z 映射成数字;0-25int u = str[i] - 'a'; //如果这个儿子分支没有字符,说明这条分支还没有这个字符插入过//就创建出来 把【idx】下标赋值上去,作为每个分支的专属坐标if(song[p][u] == 0) song[p][u] = ++idx;//然后将p往下前进一层p = song[p][u];}//最后停在那一层的那个数字就做标记,说明这是要找的字符串的结尾cnt[p]++;}public static int query(char[] str){int p = 0;//从根节点开始,下标是0表示根节点,头结点for(int i = 0 ; i < str.length; i ++){int u = str[i] - 'a'; // 将字符串每个字符都转化成数字0-25//如果这个点上没有标记,就说明没有存入过这个字符,所以返回0if(song[p][u] == 0) return 0;//如果这个点上面能寻找到这个字符,就让他往下一层继续寻找;p = song[p][u];}//最后查找完之后输出最后一个做标记的点为下标的cnt数组的值。return cnt[p];}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();String sss = scan.nextLine();while(n -- > 0){String s = scan.nextLine();String[] st = s.split(" ");String s1 = st[0];String s2 = st[1];if(s1.equals("I")){insert(s2.toCharArray());}else{System.out.println(query(s2.toCharArray()));}}}
}
AcWing 143. 最大异或对
import java.io.*;
public class Main{static int N = 3100010,idx = 0;static int[][] song = new int[N][2];//插入public static void add(int x){int p = 0;//从头结点开始for(int i = 30 ; i >= 0 ; i -- ){ //从第31位开始遍历int u = x >> i & 1;//取出第i位的二进制数//判断这一层是空的,就创建,然后赋值下标if(song[p][u] == 0) song[p][u] = ++idx;//我觉得这个idx不是代表1或0,二维数组的u才代表这一条路的节点数字p = song[p][u];//然后让往下前进一层}}//查询public static int query(int x){int p = 0,res = 0;//从根节点0开始;res存的是这条路径表示的数for(int i = 30; i>= 0 ; i --){int u = x >> i & 1; //取出第i位的二进制数
//如果该节点的u是0,则判断一下在这一层有没有跟他相反的0,这样方便找最大异或值if(song[p][1-u] != 0){ res += (1 << i);//res就将该二进制位对应异或之后的最优解1每一位顺次加起来。因为是异或相反数就是1,这是最优解p = song[p][1-u];//然后往最优解那边前进一层。}else{//否则就没有 最优解的0匹配1,1匹配0,所以就异或之后的值是0//res += (0 << i);因为是0所以可以省略,p = song[p][u];//然后让他往不优解那边前进一层。}}return res;//最后返回异或之后的最大值res}public static void main(String[] args)throws IOException{BufferedReader re = new BufferedReader(new InputStreamReader(System.in));BufferedWriter wt = new BufferedWriter(new OutputStreamWriter(System.out));int n = Integer.parseInt(re.readLine());String[] s = re.readLine().split(" ");for(int i = 0 ; i < n ; i ++ ){add(Integer.parseInt(s[i]));}int res = 0;for(int i = 0 ; i < n ; i ++ ){//因为输入的是字符串所以需要转成整形。然后每一次比较res的值谁大,然后将最大值重新赋值给resres = Math.max(res,query(Integer.parseInt(s[i])));}wt.write(res +" ");//最后输出res,因为快输出输出的是字符串,所以需要在后面加上“ ”;wt.close();}
}
并查集
AcWing 836.合并集合
import java.io.*;public class Main {private static int[] p = new int[(int) 10e5];private 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 {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));String[] s = reader.readLine().split(" ");int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);for (int i = 0; i < n; i++) {p[i] = i;}while (m-- > 0) {s = reader.readLine().split(" ");if ("Q".equals(s[0])) {int x = Integer.parseInt(s[1]);int y = Integer.parseInt(s[2]);writer.write(find(x) == find(y) ? "Yes\n" : "No\n");} else {int x = Integer.parseInt(s[1]);int y = Integer.parseInt(s[2]);p[find(x)] = find(y);}}writer.flush();}
}
AcWing 837. 连通块中点的数量
import java.io.*;public class Main {private static int[] p = new int[(int) 10e5];private 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 {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));String[] s = reader.readLine().split(" ");int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);int[] size = new int[n];for (int i = 0; i < n; i++) {p[i] = i;size[i]=1;}while (m-- > 0) {s = reader.readLine().split(" ");if ("Q1".equals(s[0])) {//是否在同一块内int x = Integer.parseInt(s[1]);int y = Integer.parseInt(s[2]);writer.write(find(x) == find(y) ? "Yes\n" : "No\n");} else if ("Q2".equals(s[0])) {//同一块内有多少节点int x = Integer.parseInt(s[1]);writer.write(size[find(x)]+"\n");} else {int x = Integer.parseInt(s[1]);int y = Integer.parseInt(s[2]);if(find(x) == find(y) ) continue;size[find(y)]+=size[find(x)];//别加反了p[find(x)] = find(y);}}writer.flush();}
}
AcWing 240.食物链
堆
AcWing 838.堆排序
import java.io.*;
public class Main{static int[] h=new int[100010];static int size;public static void main(String[] args) throws IOException {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));String[] s = reader.readLine().split(" ");int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);s = reader.readLine().split(" ");for (int i = 1; i <= n; i++) {h[i]=Integer.parseInt(s[i-1]);// writer.write(h[i]+"\n");} size=n;for(int i=n/2;i>0;i--){down(i);}while(m-->0) {writer.write(h[1]+" ");h[1]=h[size];size--;down(1);} writer.flush();}public static void down(int u){int t=u;if(u*2<=size&&h[u*2]<h[t]) t=u*2;if(u*2+1<=size&&h[u*2+1]<h[t]) t=u*2+1;if(t!=u){int tem=h[t];h[t]=h[u];h[u]=tem;down(t);}}
}
AcWing 839.模拟堆
哈希表
数据范围是1e9,所以需要先% 再加 再%
AcWing 840.模拟散列表
拉链法(数组+单链表)
import java.io.*;
import java.util.Arrays;
public class Main{static int N=100003,idx=0;static int[] h=new int[N];static int[] e=new int[N];static int[] ne=new int[N];public static void main(String[] args) throws IOException {BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));String[] s = reader.readLine().split(" ");int n = Integer.parseInt(s[0]);//数组赋初始值Arrays.fill(h, -1);while(n-->0){s = reader.readLine().split(" ");int x=Integer.parseInt(s[1]);if("I".equals(s[0])) {//插入insert(x);}else{if( find(x) )writer.write("Yes\n");else writer.write("No\n");}} writer.flush();}public static void insert (int x){int k=(x%N+N)%N;e[idx]=x;ne[idx]=h[k];h[k]=idx++;}public static boolean find (int x){int k=(x%N+N)%N;for(int i=h[k];i!=-1;i=ne[i]){if(e[i]==x){return true; }}return false;}
}
开放寻址法(蹲坑法)直接一个数组模拟蹲坑
AcWing 841.字符串哈希 :快速判断两个字符串是否相等
unsigned long long 是2^64次 代替了%Q,溢出时相当于 自动取模
import java.util.Scanner ;
public class Main{//开long型数组,本来是 前缀hash求完之后 % 2的64次方来避免 冲突,可能超过我们给的数组大小static int N = 100010,P = 131;//p是进制数,经验值static long[] h = new long[N];// 存放hash前缀值static long[] p = new long[N];// 存放p的n次方public static long get(int l,int r){//求l-r区间的hash值,要用h[r] - h[l-1] 位数不同需要让h[l-1]向左边移到跟h[r]对齐//如求1234的3-4区间位,1234 - 12,12左移然后就让12*10^(4-3+1)=1200, 相减= 34//本题是p进制,需要将上行公式中的10换成p就行//h[0] = 0//h[1] = h[i-1] * P + str[1] = 0*P+a = a//h[2] = a * P + b//h[3] = (a*P+b)*P+c = a*p[2]+b*P+c//h[4] = (a*p[2]+b*P+c)*P+d = a*p[3]+b*p[2]+c*P+d//比如abcd求3-4区间位,就是让h[d]-h[b],h[b]位数不用需要向左移对齐h[d],//h[2]*P^(4-3+1)=(a*P+b)*P^2 = a*P^3+b*P^2//然后 h[d] - h[b] 求34区间值,(a*p[3]+b*p[2]+c*P+d) - (a*P^3+b*P^2) = c*P+dreturn h[r] - h[l-1]*p[r-l+1];}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();String s = scan.next();p[0] = 1;//p的0次方=1for(int i = 1 ; i <= n ; i++ ){p[i] = p[i-1] * P;//每一个下标对应P的多少次方//预处理公式 前缀哈希的值,P进制,中间*P h[i] = h[i-1] * P + s.charAt(i-1);}while(m -- > 0){int l1 = scan.nextInt();int r1 = scan.nextInt();int l2 = scan.nextInt();int r2 = scan.nextInt();//判断两个区间是不是相同,用get的方法返回值一样说明区间的hash值是一样的if(get(l1,r1) == get(l2,r2)) System.out.println("Yes");else System.out.println("No");}}
}