单链表.
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位数后面插入一个数x
public 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位数后面插入一个数x
public 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]+" ");
//否则就是栈是空的,输出-1
else 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-25
int 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
//如果这个点上没有标记,就说明没有存入过这个字符,所以返回0
if(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的值谁大,然后将最大值重新赋值给res
res = 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+d
return 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次方=1
for(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");
}
}
}