一、栈
1.1.栈的概念
只允许在一端进行插入或删除操作的线性表。也称为先进后出的线性表;
栈顶元素:即最先出栈的元素
栈底元素:即最先入栈的元素
出栈:即栈的删除操作
压栈:即栈的插入操作,也叫入栈
1.2.栈的使用
通过代码来演示:
public class Test{
public static void main(String[] args) {
//创建一个栈
Stack<Integer> stack=new Stack<>();
//入栈
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
stack.push(5);
//出栈
int p=stack.pop();
//获取栈顶元素
int val=stack.peek();
//计算栈的元素个数
int count=stack.size();
//判断栈是否为空
boolean b= stack.empty();
System.out.println("出栈元素为"+p+" 栈顶元素为"+val+" 栈的元素个数为"+count+" 栈是否为空:"+b);
}
}
可以看到,5是最先出栈的元素,出栈后,栈顶元素为4,栈的大小为4个元素,栈不为空;
运行结果:
1.3.栈的模拟实现
这里我们用数组来模拟实现一个栈
public class MyStack {
public int[] elem;
public int usedSize;
public MyStack(int[] elem) {
this.elem = elem;
}
public boolean isFull() {
//用来判断栈是否满了
return elem.length==usedSize;
}
//栈的插入
public void push(int val){
if(isFull()){
//说明栈满了,进行扩容
elem= Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize]=val;
usedSize++;
}
//出栈
public int pop() throws MyException{
if(isFull()){
//栈为空,错误
throw new MyException("栈为空,错误");
}
return elem[usedSize--];
}
//获取栈顶元素,但是不删除
public int peek() throws MyException{
if(isFull()){
//栈为空,错误
throw new MyException("栈为空,错误");
}
return elem[usedSize];
}
//判断栈是否为空
public boolean empty(){
return usedSize==0;
}
class MyException extends Exception{
MyException(String string){
super();
}
}
}
1.4.栈的应用场景
1.4.1改变元素序列
正确答案为:C
可以看到,想要出1就必须先出2,故C错误。
正确答案:B
1.4.2将递归转化为循环
以将链表逆序打印为例
public class Test{
public static void main(String[] args) {
// 递归方式
void printList(Node head){
if(null != head){
printList(head.next);
System.out.print(head.val + " ");
}
}
// 循环方式
void printList(Node head){
if(null == head){
return;
}
Stack<Node> s = new Stack<>();
// 将链表中的结点保存在栈中
Node cur = head;
while(null != cur){
s.push(cur);
cur = cur.next;
}
}
}
1.5.栈的其它实现方式
对于栈的其它实现方式,我们可以想到的无非只有一个链表,但并不是采取任何一种插入方法都能实现,因为对于栈来说,不管是出栈还是入栈,它的时间复杂度都为O(1),因此我们模拟实现出来的栈时间复杂度也必须为O(1)
1.5.1.单链表
因此,如果要用单链表来实现栈, 需要使用标记尾节点,并且采用头插法插入数据。
1.5.2.双向链表
由于双向链表不管是头和尾开始都能遍历链表,因此不管是头插法还是尾插法都能实现栈。
!!!因此,LinkedList可以当作栈来使用
可以看到,LinkedList中确实含有Stack的方法。
1.6.栈、虚拟机栈、栈帧的区别
栈:是一种数据结构
虚拟机栈:是jvm中的一块内存
栈帧:当运行一个方法时,给方法开辟的内存
二、队列
2.1.队列的概念
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。
2.2.队列的使用
!!!队列是一个接口,使用时必须实例化LinkedList对象。
队列的方法有:
代码演示:
public class Test{
public static void main(String[] args) {
//实例化时实例LinkedList对象
Queue<Integer> queue=new LinkedList<>();
//入队
queue.offer(1);
queue.offer(2);
queue.offer(3);
queue.offer(4);
queue.offer(5);
System.out.println(queue);
//出队
queue.poll();
System.out.println(queue);
//获取队列的元素个数
System.out.println("队列的元素个数:"+queue.size());
//判断队列是否为空
System.out.println(queue.isEmpty());
}
}
执行结果:
可以看到,队列不同与栈,是一种先进先出的数据结构。
2.3.队列的模拟实现
我们使用链表来实现队列,下面我们分析单链表和双向链表实现对列的区别
代码实现:
public class MyQueue {
static class ListNode{
public int val;
public ListNode prev;
public ListNode next;
public ListNode(int val){
this.val=val;
}
}
public ListNode first=null;
public ListNode last=null;
public void offer(int val){
if(isEmpty()){
ListNode node=new ListNode(val);
first=last=node;
}
ListNode node=new ListNode(val);
last.next=node;
node.prev=last;
last=node;
}
public int poll(){
if(isEmpty()){
return -1;
}
int val=first.val;
first=first.next;
if(first!=null) {
//当只有一个节点时不用置空
first.prev = null;
}
return val;
}
public int peek(){
if(isEmpty()){
return -1;
}
int val=first.val;
return val;
}
public boolean isEmpty(){
return first==null;
}
}
2.4.双端队列(Deque)
![](https://i-blog.csdnimg.cn/direct/28be1c076cdf439b8034c946bc41e29c.png)