数据结构:栈和队列详解

栈的概念

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守先进后出LIFO(Last In First Out)的原则。大家可以理解为给弹匣压子弹,先压的子弹在最后才能打出,概念图如下

栈的简单实用

public class Main {
    public static void main(String[] args) {
        Stack<Integer> s = new Stack();    //<Integer>表示栈中全是整形数据
        s.push(1);     //向栈中压入1,2, 3, 4
        s.push(2);
        s.push(3);
        s.push(4);
        System.out.println(s.size()); // 获取栈中有效元素个数---> 4
        System.out.println(s.peek()); // 获取栈顶元素---> 4       peek只获取栈顶元素,并不出栈
        s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3                pop出栈,栈顶由原来的4,变为3
        System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
        if(s.empty()){
            System.out.println("栈空");
        }else{
            System.out.println(s.size());
        }
    }
}

栈的模拟实现

import java.util.Arrays;

public class MyStack {

    int[] arr;      //  使用数组来模拟栈的实现
    int size=0;     //用size来记录入栈时的下标

    public MyStack() {
        arr=new int[3];     //初试化,开始数组的长度为3

    }

    public void push(int val){
        expansion();        //定义一个函数,来判断数组是否满了,如果满了,就为数组进行扩容
        arr[size]=val;          
        size++;             //为数组赋值后,下标后移

    }

    public int peek(){
        if(isempty()){
            System.out.println("栈为空");
        }
        return arr[size-1];     //在push中,将size++了,要获取栈顶元素,将下标前移一位
    }
    public int pop(){
        if(isempty()){
            System.out.println("栈为空");
        }
        int s = arr[size-1];
        size--;             //弹出一个数据后,有效位前移一位
        return s;
    }

    public boolean isempty(){
        if(size==0){
            return true;
        }else return false;
    }

    public  void expansion(){
        if(size==arr.length){       //有效位和数组长度相等表面数组不够用了,需要扩容
            arr= Arrays.copyOf(arr,size*2);
        }
    }

}

概念区分(栈、虚拟机栈、栈帧有什么区别呢?)

栈:一般数据结构概念,用于实现LIFO(先进后出)行为。

虚拟机栈:JVM为每个线程分配的私有栈,用于存储方法调用状态。

栈帧:虚拟机栈中的基本单元,代表单个方法调用的执行状态。

队列(Queue)

概念

只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,和栈正好相反,栈是先进后出,队列是先进先出,进行插入插入操作的一端称为队尾,进行删除操作的一段称为对头

队列的简单使用

public static void main(String[] args) {
Queue<Integer> q = new LinkedList<>();
q.offer(1);
q.offer(2);
q.offer(3);
q.offer(4);
q.offer(5); // 从队尾入队列
System.out.println(q.size());
System.out.println(q.peek()); // 获取队头元素
q.poll();
System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
if(q.isEmpty()){
System.out.println("队列空");
}else{
System.out.println(q.size());
}
}

循环队列的实现

一个循环队列的实现,需要实现一下功能

class MyCircularQueue {

    //构造器,设置队列长度为 k
    public MyCircularQueue(int k) {

    }


    //向循环队列插入一个元素。如果成功插入则返回真。
    public boolean enQueue(int value) {

    }

    //从循环队列中删除一个元素。如果成功删除则返回真
    public boolean deQueue() {

    }

    //从队首获取元素。如果队列为空,返回 -1
    public int Front() {

    }

    //获取队尾元素。如果队列为空,返回 -1
    public int Rear() {

    }

    //检查循环队列是否为空
    public boolean isEmpty() {

    }

    //检查循环队列是否已满
    public boolean isFull() {

    }
}

我们使用数组来一步一步的实现,在下图中,红色字体代表循环队列的下标,蓝色字体代表下标对应的值,front代表啊队头,rear代表队尾

循环队列的满与空

有上图可以得出,当对头和队尾相遇的时候,说明整个队列满了,这时就又有一个问题,如果rear==front表示队列满了,当队列为空时,rear也是等于front的,这样就不好区分了,所以我们可以浪费一个空间,当rear走到下标为6的位置就表明整个队列满了,下标为7的位置浪费掉,以此来区分 ‘满’ 与 ‘空’ 的区别,代码如下

class MyCircularQueue {
    public int front;
    public int rear;
    int [] elem;

    //构造器,设置队列长度为 k
    public MyCircularQueue(int k) {
        elem=new int[k+1];    //因为浪费了一个空间,所以在初试化的时候要求给k个空间
                              //实际给了k+1个空间
    }


//向循环队列插入一个元素。如果成功插入则返回真。
    public boolean enQueue(int value) {

    }

    //从循环队列中删除一个元素。如果成功删除则返回真
    public boolean deQueue() {

    }

//从队首获取元素。如果队列为空,返回 -1
    public int Front() {

    }

    //获取队尾元素。如果队列为空,返回 -1
    public int Rear() {

    }

    //检查循环队列是否为空
    public boolean isEmpty() {
        return front==rear;

    }

    //检查循环队列是否已满
    public boolean isFull() {
        return (rear+1)%elem.length==front;
        //这里大家可以当一个公式记住,当前下标加1 % 整个数组的长度,得到下一个下标的位
        //下一个下标的位置和对头相等代表数组满了

    }
}
入队操作

当队列不满的时候才能进行入队操作,所以要先判断队列是否满了,当队列不满的时候直接在队尾(rear)的位置上赋值,rear再前移一个位置,代码如下

public boolean enQueue(int value) {
        if(isFull()){
            return false;    //判断队列是否满了
        }
        elem[rear]=value;   //给队尾赋值
        rear = (rear+1)%elem.length;    //rear位置前移;
        return true;

    }
删除对头操作

删除对头的时候要先判断队列是否为空,当队列不为空的时候,front前移一个位置,得到对头元素同理

//从循环队列中删除一个元素。如果成功删除则返回真
public boolean deQueue() {
        if(isEmpty()){
            return false;
        }
        front=(front+1)%elem.length;
        return true;       
        
    }

//从队首获取元素。如果队列为空,返回 -1
    public int Front() {
        if(isEmpty()){
            return -1;
        }
        return elem[front];
    }
获得队尾元素

队尾的元素为下标rear前一个位置的元素,所以当rear为0时,队尾的下标不可能时-1,应为队列长度-1,完整代码如下

class MyCircularQueue {
    public int front;
    public int rear;
    int [] elem;

    //构造器,设置队列长度为 k
    public MyCircularQueue(int k) {
        elem=new int[k+1];
    }


//向循环队列插入一个元素。如果成功插入则返回真。
    public boolean enQueue(int value) {
        if(isFull()){
            return false;
        }
        elem[rear]=value;   //给队尾赋值
        rear = (rear+1)%elem.length;    //rear位置后移;
        return true;

    }

    //从循环队列中删除一个元素。如果成功删除则返回真
    public boolean deQueue() {
        if(isEmpty()){
            return false;
        }
        front=(front+1)%elem.length;
        return true;

    }

//从队首获取元素。如果队列为空,返回 -1
    public int Front() {
        if(isEmpty()){
            return -1;
        }
        return elem[front];
    }

    //获取队尾元素。如果队列为空,返回 -1
    public int Rear() {
        if(isEmpty()){
            return -1;
        }
        int index = (rear==0)?elem.length-1:rear-1;
        return elem[index];

    }

    //检查循环队列是否为空
    public boolean isEmpty() {
        return front==rear;

    }

    //检查循环队列是否已满
    public boolean isFull() {
        return (rear+1)%elem.length==front;

    }
}

有帮助麻烦点个支持哈

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值