栈和队列的知识点详解

目录

1. 栈(Stack)

1.1 概念

1.2 栈的使用

1.3 栈的模拟实现

1.4 栈的应用场景

1. 改变元素的序列

2. 将递归转化为循环

2. 队列(Queue)

2.1 概念

2.2 队列的使用

2.3 队列模拟实现

2.4 循环队列

3. 双端队列 (Deque)

1. 栈(Stack)

1.1 概念

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈 顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据在栈顶。

1.2 栈的使用

方法功能
Stack()构造一个空的栈
E push(E e)将e入栈,并返回e
E pop()将栈顶元素出栈并返回
E peek()获取栈顶元素
int size()获取栈中有效元素个数
boolean empty()检测栈是否为空
public class MyStack {
    public int [] elem;
    public int usedSize;
    public static final int DEFAULT_CAPACITY =10;
    public MyStack(){
        this.elem=new int[DEFAULT_CAPACITY];
    }
    //压栈
    public void push(int val){
        if(ifFull()){
            this.elem= Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize++]=val;
    }
    public boolean ifFull(){
        return usedSize == elem.length;
    }
    //出栈
    public int pop(){
        if(isEmpty()){
            throw new EmptyExpection("栈为空");
        }
        usedSize--;
       return elem[usedSize];
    }
    public boolean isEmpty(){
        return usedSize==0;
    }
    public int peek(){
        if(isEmpty()){
            throw new EmptyExpection("栈为空");
        }
        return elem[usedSize--];
    }

入栈出栈的时间复杂度都为O(1)。

通过链表实现栈,可以使用单链表和双链表。

不管使用哪种链表,你一定得保证,入栈,出栈等操作,时间复杂度均为O(1)。

1.3 栈的模拟实现

从上图中可以看到,Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的。

1.4 栈的应用场景

1. 改变元素的序列

2. 将递归转化为循环

比如:逆序打印链表

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;
    }
比特就业课
// 将栈中的元素出栈
while(!s.empty()){
 System.out.print(s.pop().val + " ");
 }
 }

2. 队列(Queue)

2.1 概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头 (Head/Front)

2.2 队列的使用

在Java中,Queue是个接口,底层是通过链表实现的。

方法功能
boolean offer(E e)入队列
E poll()出队列
peek()获取队头元素
int size()获取队列中有效元素个数
boolean isEmpty()检测队列是否为空

注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

2.3 队列模拟实现

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有 两种:顺序结构 和 链式结构。同学们思考下:队列的实现使用顺序结构还是链式结构好?

// 双向链表节点
public static class ListNode{
 ListNode next;
 ListNode prev;
 int value;
 ListNode(int value){
 this.value = value;
 }
 }
 ListNode first;   // 队头
ListNode last;    // 队尾
int size = 0;

2.4 循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。 环形队列通常使用数组实现。

数组下标循环的小技巧

1. 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length

2. 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) % array.length

如何区分空与满

1. 通过添加 size 属性记录

2. 保留一个位置

3. 使用标记

3. 双端队列 (Deque)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

Deque是一个接口,使用时必须创建LinkedList的对象。

在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

  • 10
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值