链表
常见的链表有单链表和双链表
单链表:每个结点有一个next指针指向下一个结点,data存放数据
双链表:每个结点有一个next指针指向下一个结点,prev指针指向上一个结点,data存放数据。
4的下一结点为-1,表示下一结点为空,不存在下一结点
p是指向上一结点,e是指向下一结点
单链表
实现单链表只需要两个数组int e[N]和int data [N],还需要tail和head来表示尾结点和头节点(头节点一般不存放数据),idx表示当前已用的内存空间,用于开店,我们用-1表示空。
e[i]表示地址为i的结点上的下一个结点的地址(即数组下标)。
data[i]表示地址为i的结点上的数据。
尾部插入结点 插入一个节点到尾部:e[tail]=++idx(++idx先把值给赋出来),e[idx]=-1(将新的伪结点设为空,表示后面没有结点),data[idx]=val(地址的值赋进来),tail=idx(伪结点变成这个新的结点);
中间插入结点 往地址adr的后面插入一个结点,e[++idx]=e[adr](插入结点的下一个指针指向)比如要在3,4中间插一个5,3指向4,让5也指向4,e[adr]=idx(比如让3指向5),data[idx]=val(赋值);
删除 将adr后面的结点删除(adr部位tail):e[adr]=e[e[adr]];(当前这个位置的指针指向下下个指针就可以了)
单链表只能处理”后面“的操作,不能找到前一个点。
双链表
单链表和双链表的区别在于,双链表不仅在next指针(e数组)来表示下一个结点,还有一个prev指针(p数组)来表示上一个结点的位置。
例题
给定按从小到大的顺序排列的数字1到n,随后对他们进行m次操作,每次将一个数字x移动到数字y之前或之后,请输出完成这m次操作后他们的顺序
输入格式
第一行为两个数字n,m表示初始状态为1到n的从小到大排序,后续有m次操作。
第二行到第m+1行,每行三个数x,y,z,当z=0是,将x移动到y之后,当z=1时,将x移动到y之前。
输出格式
一行,n个数字,中间用空格隔开,表示m次操作完成后的排列顺序。
样例输入
5 3
3 1 0
5 2 1
2 1 1
样例输出
2 1 3 5 4
思路
利用双链表去模拟
列表写成循环列表
package shuanlianbiao;
import java.util.*;
public class chapter1 {
public static int[]pre=new int [(int)1e4+1];
public static int[]next=new int [(int)1e4+1];
public static void res(int x) {
int a=pre[x];
int b=next[x];
next[a]=b;
pre[b]=a;
}//删除
public static void add(int x,int y) {
int z=next[y];
pre[z]=x;
pre[x]=y;
next[y]=x;
next[x]=z;
}
public static void main(String agrs[]) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
int m=scan.nextInt();
for(int i=1;i<n;i++) {
pre[i]=i-1;
next[i]=i+1;
}
next[0]=1;
pre[0]=0;
next[n]=0;
pre[n]=n-1;
for(int i=0;i<m;i++) {
int x=scan.nextInt();
int y=scan.nextInt();
int z=scan.nextInt();
if(z==0) {
y=pre[y];
}
res(x);
add(x,y);
}
for(int i=next[0];i>0;i=next[i]) {
System.out.print(i+" ");
}
}
}
栈
栈可以看作只能对顶部操作的数组,结构比较简单,只需要一个数组表示内存空间,一个数字表示栈顶即可
int stk[N],top;
往栈内新增元素:stk[++top]=x;(顶部操作)
删除栈顶元素:top--
判断栈是否为空:top==0
例题
小蓝有一个长度为 n 的括号串,括号串仅由字符 (
、 )
构成,请你帮他判断一下该括号串是否合法,合法请输出 Yes
,反之输出 No
。
合法括号序列:
-
空串是合法括号序列。
-
若 s 是合法括号序列,则
(
s)
也是合法括号序列。 -
若 s,t 都是合法括号序列,则 st 也是合法括号序列。
例如 ()()
, (())
, (())()
均为合法括号序列。
输入格式
第一行包含一个正整数 n ,表示括号串的长度。
第二行包含一个长度为 n 的括号串。
输出格式
输出共 11 行,若括号串合法请输出 Yes
,反之输出 No
代码
package zhan;
import java.util.*;
public class chapter1 {
public static void main(String args[]) {
Scanner scan=new Scanner(System.in);
char[] c=scan.next().toCharArray();
int top=0;
boolean f=true;
for(char x:c) {
if(x=='(') {
top++;
}else {
top--;
if(top<0) {
f=false;
}
}
}
if(top!=0) {
f=false;
}
if(f) {
System.out.print("YES");
}else {
System.out.print("No");
}
}
}
队列
队列是一个可以对头和尾操作的数组
需要一个数组,两个整形变量一个表示头head一个表示尾tail
int q[N],h=1,t=0;
有效元素的区间为[h,t]
入队:q[++t]=x;
出队:h++;
检查队列是否为空:t-h+1==0
例题
小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。
这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。
假设内存中有 M 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过 M−1,软件会将新单词存入一个未使用的内存单元;若内存中已存入 M 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。
假设一篇英语文章的长度为 N 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。
输入描述
输入共 2 行。每行中两个数之间用一个空格隔开。
第一行为两个正整数 M 和 N,代表内存容量和文章的长度。
第二行为 N 个非负整数,按照文章的顺序,每个数(大小不超过 1000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。
其中,0<M≤100,0<N≤1000。
输出描述
输出共 1 行,包含一个整数,为软件需要查词典的次数。
输入输出样例
示例 1
输入
3 7
1 2 1 5 4 4 1
输出
5
示例2
输入
2 10
8 824 11 78 11 78 11 78 8 264
输出
6
package duilie;
import java.util.*;
public class chapter1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scan=new Scanner(System.in);
int m=scan.nextInt();
int n=scan.nextInt();
int queue[]=new int[n+1];
int h=1,t=0,res=0;
for(int i=0;i<n;i++){
int x=scan.nextInt();
boolean f=true;
for(int j=h;j<=t;j++) {
if(queue[j]==x) {
f=false;
}
}
if(f) {
queue[++t]=x;
res++;
}
if(t-h+1>m) {
++h;
}
}
System.out.println(res);
}
}