数据结构之栈和队列

Posted 王嘻嘻-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构之栈和队列相关的知识,希望对你有一定的参考价值。

目录

1、栈(Stack)

1.1 概念

1.2 代码实现

2. 队列(Queue)

2.1 概念

2.2 代码实现

2.3 循环队列

2.3.1 思路解析  

2.3.2 代码实现

3. 双端队列 (Deque)

3.1 概念

3.2 代码实现


1、栈(Stack)

1.1 概念

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

 

1.2 代码实现

1. 利用顺序表实现,即使用尾插 + 尾删的方式实现                                                                         2. 利用链表实现,则头尾皆可

相对来说,顺序表的实现上要更为简单一些,所以我们优先用顺序表实现栈。

package stack_queue.stack;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

//基于数组的顺序表实现 栈
public class MyStack<E> 
    //当前栈的数据个数
    private int size;
    //实际存储数据的动态数组 - ArrayList
    private List<E> data = new ArrayList<>();

    /**
     * 将val入栈
     * @param val
     */
    public void push(E val)
        //默认尾插
        data.add(val);
        size++;
    

    /**
     * 弹出栈顶元素
     * 返回栈顶的值
     * @return
     */
    public E pop()
        if (isEmpty())
            //栈为空
            throw new NoSuchElementException("stack is empty!cannot pop!");
        
        //删除栈顶元素
        E val = data.remove(size - 1);
        size--;
        return val;
        //等同于 return data.remove(--size);
    

    /**
     * 返回栈顶元素但不出栈
     * @return
     */
    public E peek()
        if (isEmpty())
            throw new NoSuchElementException("stack is empty!cannot peek");
        
        return data.get(size-1);
    

    /**
     * 判断当前栈是否为空
     * @return
     */
    private boolean isEmpty() 
        return size == 0;
    

    @Override
    public String toString() 
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < size; i++) 
            sb.append(data.get(i));
            if (i!=size-1)
                //此时还未到栈顶,还没到数组末尾
                sb.append(",");
            
        
        sb.append("] top");
        return sb.toString();
    

2. 队列(Queue)

2.1 概念

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

 

2.2 代码实现

package stack_queue.queue.impl;

import java.util.NoSuchElementException;

/**
 * 基于链表实现的基础队列
 */
public class MyQueue<E> implements Queue<E> 
    //链表的每个节点
    private class Node
        E val;
        Node next;
        public Node(E val)
            this.val = val;
        
    

    //当前队列中的元素个数
    private int size;
    //队首
    private Node head;
    //队尾
    private Node tail;

    //入队
    @Override
    public void offer(E val) 
        Node node = new Node(val);
        if (head == null)
            //此时链表为空
            head = tail = node;
        else 
            tail.next = node;
            tail = node;
        
        size++;
    

    //出队
    @Override
    public E poll() 
        if (isEmpty())
            throw new NoSuchElementException("queue is empty!cannot poll!");
        
        E val = head.val;
        Node node = head;
        head = head.next;
        //将原来头节点脱钩
        node.next = null;
        size--;
        return val;
    

    //返回队首元素
    @Override
    public E peek() 
        if (isEmpty())
            throw new NoSuchElementException("queue is empty!cannot peek!");
        
        return head.val;
    
 
    @Override
    public boolean isEmpty() 
        return size == 0;
    

    @Override
    public String toString() 
        StringBuilder sb = new StringBuilder();
        sb.append("front [");
        //链表的遍历
        for (Node x =head; x != null;x = x.next)
            sb.append(x.val);
            if (x.next != null)
                //还没走到链表尾部
                sb.append(",");
            
        
        sb.append("]");
        return sb.toString();
    

2.3 循环队列

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

数组下标循环的小技巧

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

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

2.3.1 思路解析  

2.3.2 代码实现

package stack_queue.queue.impl;

import java.util.Arrays;
import java.util.NoSuchElementException;

//基于整形的循环队列
public class LoopQueue implements Queue<Integer> 
    private Integer[] data;
    //指向当前循环队列的队首元素
    private int head;
    //指向当前循环队列的队尾元素的下一个位置
    private int tail;
    //当前队列中元素个数
    private int size;
    //n为希望保存的元素个数
    public LoopQueue(int n)
        //在循环队列中浪费一个空间不能存储元素,来判断是否已满
        data = new Integer[n + 1];
    
    
    @Override
    public void offer(Integer val) 
        if (isFull())
            throw new ArrayIndexOutOfBoundsException("queue is full!cannot offer new val");
        
        data[tail] = val;
        tail = (tail + 1) % data.length;
        size++;
    

    @Override
    public Integer poll() 
        if (isEmpty())
            throw new NoSuchElementException("queue is empty!cannot poll");
        
        Integer val = data[head];
        head = (head + 1) % data.length;
        size--;
        return val;
    

    @Override
    public Integer peek() 
        if (isEmpty())
            throw new NoSuchElementException("queue is empty!cannot peek");
        
        return data[head];
    

    @Override
    public boolean isEmpty() 
        return tail == head;
    

    @Override
    public String toString() 
        StringBuilder sb = new StringBuilder();
        sb.append("front [");
        //取得最后一个元素下标
        int lastIndex = tail == 0 ? data.length - 1 : tail - 1;
        for (int i = head;i != tail;)
            sb.append(data[i]);
            if (i!=lastIndex)
                sb.append(", ");
            
            i = (i + 1) % data.length;
        
        sb.append("] tail");
        return sb.toString();
    

    public boolean isFull()
        return (tail + 1) % data.length == head;
    

3. 双端队列 (Deque)

3.1 概念

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

3.2 代码实现

package stack_queue.queue;

import java.util.ArrayDeque;
import java.util.Deque;

//双端队列
public class DequeTest 
    public static void main(String[] args) 
        Deque<Integer> deque = new ArrayDeque<>();
        deque.addLast(1);
        deque.addLast(3);
        deque.addLast(5);
        //尾插尾删或者头插头删就是栈
        while (!deque.isEmpty())
            System.out.println(deque.pollLast());//5  3  1
        

//        //头插尾删或者尾插头删就是队列
//        while (!deque.isEmpty())
//            System.out.println(deque.pollFirst());//1  3  5
//        

    


本节完^_^

以上是关于数据结构之栈和队列的主要内容,如果未能解决你的问题,请参考以下文章

数据结构之栈和队列

数据结构之栈和队列

剑指Offer数据结构之栈和队列[Python版]

剑指Offer数据结构之栈和队列[Python版]

数据结构之栈和队列

Leetcode题解——数据结构之栈和队列