数据结构之栈和队列的实现

一、栈的基本概念与操作

栈是一种特殊的线性表,只允许在一端实现插入删除的操作,插入删除的一端称之为栈顶,另一端称为栈底。栈遵循后进先出的原则。

压栈:指栈的插入操作,入数据在栈顶。
出栈:又叫弹栈,指栈的出数据操作,出数据在栈顶。

基本操作如图:
在这里插入图片描述

二、栈的代码实现以及解释

在这里插入图片描述
如图,栈的存储方式和数组类似,所以较为简单。因此,我们只需用到数组就可以解决问题。

1.操作前准备
先定义一个数组并给定空间。

public class MyStack {
     int[] array;
     int size = 0;
     public MyStack(){
         this.array = new int[10];
     }
}

2.压栈操作
注意:压栈操作我们需要考虑一个问题,栈是否已满?

//定义一个方法判断栈是否已满
    public boolean isFull(){
         return size == array.length;
    }

之后就可以实现操作

    public int push (int val){
         //如果是满的要扩容
         if(isFull()){
             array = Arrays.copyOf(array,2*array.length);
         }
         this.array[size] = val;
         size++;
         return val;
    }

3.出栈操作
注意:这里需要判断值是否为空的情况,可以创建一个异常类来报错

public class MyEmptyStackException extends RuntimeException{
    public MyEmptyStackException() {
    }

    public MyEmptyStackException(String message) {
        super(message);
    }
}
    public boolean empty(){
         return size == 0;
    }

操作实现:

    public int pop() {
         //判断元素是否为空
        if(empty()){
            throw new MyEmptyStackException("栈为空!");
        }
         //直接将栈首的元素记录输出即可
        int set = array[size-1];
        size--;
        return set;
    }

4.peek查看操作
这个方法只是提供查看栈的第一个元素,不会将元素弹出栈。

    public int peek(){
         //栈为空要报错
        if(empty()){
            throw new MyEmptyStackException("栈为空!");
        }
        return array[size-1];
    }

5.整体测试

public class Test {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        myStack.push(1);
        myStack.push(2);
        myStack.push(3);
        myStack.push(4);

        int X = myStack.pop();
        System.out.println(X);
        
        int Y = myStack.peek();
        System.out.println(Y);

        Y = myStack.peek();
        System.out.println(Y);
    }
}

在这里插入图片描述

三、队列的基本概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的原则。

入队列:进行插入操作的一端称为队尾

出队列:进行删除操作的一端称为队头

基本操作如下:
在这里插入图片描述

四、队列的代码实现及解释

1.操作前准备
在这里实现相关操作我们用单链表的方法实现,我们可以定义两个变量分别指向队头和队尾,代码如下:

    static class ListNode{
        public int val;
        public ListNode next;

        public ListNode(int val){
            this.val = val;
        }
    }
	//定义头尾结点
    public ListNode head;
    public ListNode tail;
    //定义一个计数变量
    public int size;

2.判断队列是否为空

    public boolean empty(){
        return size == 0;
    }

3.实现入队列操作

    public void offer(int val){
        ListNode node = new ListNode(val);
        //如果队列为空时
        if(empty()){
            head = node;
            tail = node;
        }else{
            tail.next = node;
            tail = tail.next;
        }
        size++;
    }

4.实现出队列操作

    public int poll(){
        if(empty()){
            return -1;
        }
        //定义一个参数存放头结点的值
        int ret = head.val;
        head = head.next;
        if(head == null){
            tail = null;
        }
        size--;
        return ret;
    }

5.实现查看队头元素

    public int peek(){
        if(empty()){
            return -1;
        }
        return head.val;
    }

6.查看元素个数

    //查看有多少元素
    public int getSize(){
        return size;
    }

7.整体实现

public class Test {
    public static void main(String[] args) {
        MyQueue myQueue = new MyQueue();

        myQueue.offer(1);
        myQueue.offer(2);
        myQueue.offer(3);

        System.out.println(myQueue.getSize());

        System.out.println(myQueue.poll());
        System.out.println(myQueue.poll());

        System.out.println(myQueue.peek());
        System.out.println(myQueue.poll());
    }
    
}

在这里插入图片描述

五. 循环队列

循环队列,顾名思义就是一个环形的队列,这种队列的实现就是通过一个数组进行实现。

在这里插入图片描述
对应操作:

  1. 入队操作
  2. 出队操作
  3. 获取队头元素
  4. 获取队尾元素
  5. 判断队列是否为空
  6. 判断队列是否为满

相应元素的定义与数组的实现

    private int[] elem;
    private int front;  //代表队头
    private int rear;   //表示队尾    
    //给定数组长度
    public MyCircularQueue(int k) {
        elem = new int[k+1];
    }
  1. 判断数组是否为满
    public boolean isFull() {
        if((rear+1)%elem.length == front){
            return true;
        }
        return false;
    }

在这里插入图片描述
如图所示:这里判断队列已满时需要注意,不能单纯的进行 rear++ 这样会出现数组越界的现象,因此 (rear+1)%elem.length == float 这个方法来判断是否出现队列已满的情况。

  1. 判断队列是否为空
    public boolean isEmpty() {
        if(front == rear){
            return true;
        }
        return false;
    }
  1. 入队操作
    public boolean enQueue(int value) {
        if(isFull()){
            return false;
        }
        elem[rear] = value;
        rear = (rear + 1)%elem.length;
        return true;
    }

同样的,这里的队尾 rear 要实现后移,同样需要考虑是否为数组最后一个元素的情况。

  1. 出队操作
    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];
    }

在这里,对获取队尾链表中的三目运算符进行简单的解释。
首先,我们要知道的是,要寻找队尾元素有两种情况第一种如下:

在这里插入图片描述
如图所示,此时的 rear 指针正指在数组尾部这个一般的位置,此时,只要不在在 0 这个特殊的下表位,不难理解,真正的队尾元素,就是队尾指针的前一个位置。 所以只需要 rear-1 即可。

第二种情况:
在这里插入图片描述
如上图所示,此时 rear 正处在特殊的 0 下标位置,此时 rear-1 很明显已经不在适用,这里就需要特殊方法 (elem.length-1) 来判断队尾元素的位置!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值