链表
为什么要用链表
数组作为一个顺序储存方式的数据结构,可是有大作为的,它的灵活使用为我们的程序设计带来了大量的便利;但是,数组最大的缺点就是我们的插入和删除时需要移动大量的元素,所以呢,大量的消耗时间,以及冗余度难以接收。
链表可以灵活地去解决这个问题,插入删除操作只需要修改指向的对象就可以了,不需要进行大量的数据移动操作。
单链表
初始化
static class Node{//定义结点类
int value;//本身的值
Node next;//指向下一个结点
public Node(int value, Node next) {
this.value = value;
this.next = next;
}
}
Node head = new Node(-1,null);//头结点
Node end = new Node(-1, null);//尾结点
Node per = head;
for(int i=1;i<=10;i++) {
per.next = new Node(i, null);
per = per.next;
}
per.next = end;
插入
插入前:
插入后:
Node now;//待插入结点
now.next = head.next;//此节点的next为插入位置上一个结点的下一个结点
head.next = now;//此节点位置的上一个结点的下一个结点为now
删除
删除前:
删除后:
Node now;//待删除结点
head.next = now.next;
双链表
初始化
static class N{
N last;
int value;
N next;
public N(N last, int value, N next) {
this.last = last;
this.value = value;
this.next = next;
}
}
Node first = new Node(null,-1,null);//头结点
Node end = new Node(null,-1, null);//尾节点
Node per = first;
for(int i=1;i<=10;i++) {
per.next = new N(per,i, null);
per = per.next;
}
end.last = per;
per.next = end;
插入
插入前:
插入后:
Node now;//待插入结点
now.next = first.next;
first.next.last = now;
first.next = now;
now.last = first;
删除
删除前:
删除后:
Node now;//待删除结点
now.last.next = now.next;
now.next.last = now.last;
例题:左移右移(双链表解法)
思路:
1.创建双链表并完成初始化,初始元素为 1 1 1 ~ n n n;
2.无论 x x x 左移或右移,都要先将 x x x 从原位置删除,为了便于获取 x x x 对应的 N o d e Node Node 结点,用 M a p Map Map 存储 x x x 和 v a l u e value value 为 x x x 的结点;
3.如果 x x x 为左移,就将 x x x 对应的 N o d e Node Node 结点插入到头结点后;
4.如果 x x x 为右移,就将 x x x 对应的 N o d e Node Node 结点插入到尾节点前;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
public class 左移右移_双链表 {
static class Node{
Node up;
int value;
Node down;
public Node(Node up, int value, Node down) {
this.up = up;
this.value = value;
this.down = down;
}
}
public static void main(String[] args) throws IOException{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
String[] s = in.readLine().split(" ");
int n = Integer.parseInt(s[0]);
int m = Integer.parseInt(s[1]);
Map<Integer, Node> map = new HashMap<>();
Node first = new Node(null, -1, null);
Node last = new Node(null, -1, null);
Node no = first;
for(int i=1;i<=n;i++) {
no.down = new Node(no, i, null);
no = no.down;
map.put(i, no);
}
last.up = no;
no.down = last;
for(int i=0;i<m;i++) {
s = in.readLine().split(" ");
char ch = s[0].charAt(0);
int x = Integer.parseInt(s[1]);
Node node = map.get(x);
node.up.down = node.down;
node.down.up = node.up;
if(ch=='L') {
node.down = first.down;
first.down.up = node;
first.down = node;
node.up = first;
}else {
node.up = last.up;
last.up.down = node;
node.down = last;
last.up = node;
}
}
no = first.down;
while(no!=last) {
System.out.print(no.value+" ");
no = no.down;
}
}
}
栈
栈
栈( S t a c k Stack Stack):是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。
栈顶( T o p Top Top):线性表允许进行插入删除的那一端。
栈底( B o t t o m Bottom Bottom):固定的,不允许进行插入和删除的另一端。
常用方法
Stack<Integer> stack = new Stack();
boolean is = stack.isEmpty();//判断此栈是否为空
int n = stack.peek();//获取栈顶的元素,但不删除
int m = stacl.pop();//获取并删除栈顶的元素
stack.push(10);//将10压入栈中
stack.clear();//清空栈
判断括号序列是否合法
public static boolean check(String s){
Stack<Character> stack = new Stack();
char[] ch = s.toCharArray();
for(int i=0;i<ch.length;i++){
if(ch[i]=='(')
stack.push(ch[i]);
else if(stack.isEmpty())
return false;
else
stack.pop();
}
return stack.isEmpty();
}
队列
队列
队列( q u e u e queue queue)是一种先进先出的、操作受限的线性表。
队列这种数据结构非常容易理解,就像我们平时去超市买东西,在收银台结账的时候需要排队,先去排队的就先结账出去,排在后面的就后结账,有其他人再要过来结账,必须排在队尾不能在队中间插队。
常用方法
Queue<Integer> queue = new LinkedList<>();
queue.peek();//获取队头元素,但不删除
queue.poll();//获取并删除队头元素
queue.clear();//清空队列
queue.push(11);//将11存放到队列中
例题:左移右移(栈 + 队列解法)
思路:
1.如果一个数先移动到最左边,再移动到最右边,那么最后输出的时候这个数一定是在最右边,也就是一个数最终出现在哪里,以他最后一次出现为准;
2.为了避免一个数重复判断,而且要以他最后一次出现时的 L L L 和 R R R 操作为最终操作,所以可以先将全部输入分别存放到 c h a r char char 类型数组和 i n t int int 类型数组中,然后逆序判断,并且用一个数组来表示这个 x x x 有没有出现过;
3.因为要对输入做逆序操作,所以,逆序时最后出现的 L L L 对应的 x x x 在输出的最前面,然后之后出现的 L L L 对应的 x x x 依次输出,即先入先出,可以用队列来存储进行 L L L 操作的 x x x ;
4.逆序时最后出现的 R R R 对应的 x x x 在输出的最后面,然后之后出现的 R R R 对应的 x x x 依次在前,即后入先出,可以用栈来存储进行 R R R 操作的 x x x ;
5.输出时,先输出队列中的元素,然后将 1 1 1 ~ n n n 中没有出现过的值按序输出,最后输出栈中的元素;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class 左移右移_栈_队列 {
public static void main(String[] args) throws IOException{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
String[] s = in.readLine().split(" ");
int n = Integer.parseInt(s[0]);
int m = Integer.parseInt(s[1]);
int[] a = new int[n+1];
char[] c = new char[m];
int[] x = new int[m];
for(int i=0;i<m;i++) {
s = in.readLine().split(" ");
c[i] = s[0].charAt(0);
x[i] = Integer.parseInt(s[1]);
}
Stack<Integer> r = new Stack<>();
Queue<Integer> l = new LinkedList<>();
for(int i=m-1;i>=0;i--) {
if(a[x[i]]==0) {//判断x[i]是否出现过
a[x[i]] = 1;//若x[i]没有出现过
if(c[i]=='L')
l.add(x[i]);
else
r.push(x[i]);
}
}
while(l.size()!=0) //输出队列中元素
System.out.print(l.poll()+" ");
for(int i=1;i<=n;i++)
if(a[i]==0) //a[i]为0,表示i没有出现过
System.out.print(i+" ");
while(r.size()!=0) //输出栈中元素
System.out.print(r.pop()+" ");
}
}