【数据结构】 -- 栈与队列常用方法及简单模拟实现

栈(Stack)

栈的概念

栈是一种特殊的线性表,只允许在固定的一端插入或删除数据,进行数据的插入和删除的一端称为栈顶,另一端称为栈底,栈中的数据遵循先进后出的原则
在这里插入图片描述
入栈/压栈: 数据的插入,插入的数据在栈顶
出栈: 数据的删除,删除的数据在栈顶
在这里插入图片描述
从上图可以看到,Stack继承了Vector,实现了List接口,Vector与ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的.

栈的使用

构造方法

方法解释
Stack()构造一个空栈

常用方法

	//压栈
    public E push(E item) {}
 	//出栈
    public synchronized E pop() {}
	//查看栈顶元素
    public synchronized E peek() {}
	//判断栈是否为空
    public boolean empty() {}
	//栈的有效元素的长度
    public int size() {}
Stack<Integer> stack = new Stack<>();
stack.push(1); // 入栈
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack.empty()); // 判断栈中是否为空 --> false
System.out.println(stack.size()); //输入栈的有效的元素的个数 --> 4
System.out.println(stack.pop()); //出栈,删除栈顶元素 --> 4 此时栈中还剩 1 2 3 
System.out.println(stack.peek()); //查看栈顶元素 --> 3
System.out.println(stack.pop()); //出栈,删除栈顶元素 --> 3 此时栈中还剩 1 2 
System.out.println(stack.pop()); //出栈,删除栈顶元素 --> 2 此时栈中还剩 1 
System.out.println(stack.pop()); //出栈,删除栈顶元素 --> 1 此时栈中没有元素了
System.out.println(stack.empty()); //判断栈中是否为空 --> true

栈的模拟实现

public class MyStack {
    public int[] elem;
    public int size;

    public MyStack() {
        this.elem = new int[5];
    }
    //压栈
    public int push(int x)  {
        //判断栈是否满
        if (isFull()) {
            //二倍扩容
            this.elem = Arrays.copyOf(elem,2*elem.length);
        }
        this.elem[size++] = x; //先压栈,在size++
        return 0;
    }
    private boolean isFull() {
        if (size == elem.length) {
            return true;
        }
        return false;
    }
    //出栈
    public int pop() {
        int a = elem[size-1];     
        size--;
        //因为添加数据的时候,是根据size的值添加的,这里的size--,
        //后面添加新的数据的时候,可以直接覆盖旧的数据,这样就达到了删除~~
        return a;
    }
    //获取栈顶元素
    public int peek() {
        if (empty()) {//如果栈为空的话,抛出一个异常
            throw new RuntimeException("栈为空,无法获取栈顶元素!");
        }
        //获取栈顶元素并返回
        int a = elem[size-1];
        return a;
    }
    //栈中有效元素的长度
    public int size() {
    //在每次添加或删除时,让size++或size--,随时记录栈中的长度,在获取栈的长度的时候,
    //直接返回size,此时的时间复杂度为O(1) ,, 如果要遍历栈的活,这样时间复杂度为O(N).
        return size;
    }
    //判断栈是否为空
    public boolean empty() {
    //如果栈中没有元素,也就是长度为0,则返回true
        if(size == 0) {
            return true;
        }
        return false;
    }
}

队列(Queue)

队列的概念

只允许在一端进行插入操作,在另一端进行删除操作的一种的特殊的线性结构,队列具有先进后出的机制,进行插入的一端称为队尾,删除的一端称为队头
在这里插入图片描述

在java中,Queue是一个接口,底层使用LinkedList实现的,在实例化Queue的时候,必须实例化LinkedList对象.
在这里插入图片描述

队列的使用

常用方法

//入队列
boolean offer(E e);
//出队列,删除对头元素
E poll();
//判断队列是否为空
boolean isEmpty();
//获取队列头元素
E peek();
//获取队列的长度
int size();
Queue<Integer> queue = new LinkedList<>();
queue.offer(1); // 入队列
queue.offer(2);
queue.offer(3);
System.out.println(queue.isEmpty()); // 判断队列是否为空 --> false
System.out.println(queue.poll()); //出队列 --> 1
System.out.println(queue.peek()); //获取队列头元素 --> 2
System.out.println(queue.size()); //获取队列的长度 --> 2

队列的模拟实现

public class MyQueue {
    static class Node {
        int val;
        Node prev;
        Node next;

        Node(int val) {
            this.val = val;
        }
    }
    private Node head; //队头
    private Node last; //队尾
    private int usedSize;

    //尾插
    public void offer(int e) {
        MyDeque.Node node = new MyDeque.Node(e);
        if (head == null) { // 第一次插入
            head = node;
        }else {
            //不是第一次插入
            last.next = node;
            last.next.prev = last;
        }
        last = node;
        usedSize++;
    }
    //头删
    public int poll() {
        if(isEmpty()) {
            throw new NullPointerException("链表无数据!");
        }
        int val = head.val;
        if (head == last) { //只有一个节点
            head = null;
            last = null;
        } else {
            head = head.next;
            head.prev.next = null;
            head.prev = null;
        }
        usedSize--;
        return val;
    }
    //获取队头元素,获取链表中第一个节点的值
    public int peek(){
        if (isEmpty()) {
            throw new NullPointerException("链表无元素!");
        }
        return head.val;
    }
    //获取队列的长度
    public int size() {
        return usedSize;
    }
    //判断队列是否为空
    public boolean isEmpty() {
        if (head == null) {
            return true;
        }
        return false;
    }
}

循环队列

实际中我们有时还会使用一种队列叫做循环队列,循环队列通常使用数组实现
把普通的数组想象成一个环,然后让它循环起来就好了~~
在这里插入图片描述

  • 如何让数组循环起来?
    这里提供一个方法:
    在这里插入图片描述
    公式: (index+1)%array.length
    在向下一步走 让下标+1在模上数组的长度就可以得到下一个元素的下标了,来达到数组循环的目的~~

  • 如何区分空与满?
    1.可以使用一个计数器usedSize记录当前数组的元素 usedSize = array.length 代表数组满了
    2.可以"牺牲"一个数组空间 代表满(实现目的):
    当 head = last 为空 当 (last+1)%array.length = head 为满
    在这里插入图片描述

代码实现

class MyCircularQueue {

    private int[] elme;
    private int head;//代表首元素
    private int last;//代表尾巴元素

    public MyCircularQueue(int k) {
        this.elme = new int[k+1]; // 因为是要浪费一个空间的所以这里多给了一个空间
    }
    //插入元素
    public boolean enQueue(int value) {
    //如果队列满了就没法插入了
        if (isFull()) {
            return false;
        }
        this.elme[last] = value;
        last = (last+1)%this.elme.length;
        return true;
    }
    //删除元素
    public boolean deQueue() {
    //如果队列为空没法删除
        if (isEmpty()) {
            return false;
        }
        head = (head+1)%this.elme.length;
        return true;
    }
    //获取头元素
    public int Front() {
    //如果队列为空则返回-1
        if (isEmpty()) {
            return -1;
        }
        //返回首元素
        return this.elme[head];
    }
    //获取队尾元素
    public int Rear() {
    //如果队列为空则返回-1
        if (isEmpty()) {
            return -1;
        }
        //当last = 0 时,返回数组最后一个下标的元素 ,  否则返回last上一个元素
        if (last == 0) {
            return this.elme[this.elme.length-1];
        } else {
            return this.elme[last-1];
        }
    }
    //判断队列是否为空
    public boolean isEmpty() {
    //当head等于last说明队列是空的
        return head == last;
    }
    //判断队列是否为满
    public boolean isFull() {
    //当last下一个元素为head则说明队列满了
        return ((last+1)%this.elme.length == head);
    }
}

双端队列(Deque)

双端队列的概念

双端队列是指,允许两端都可以进行入队和出队操作,deque 是 “double ended queue” 的简称,既可以从队头出队入队,也可以从队尾出队入队.
在这里插入图片描述
在java中Deque是一个接口,要想使用Deque在进行实例化的时候,必须实例化它的实现类

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

在这里插入图片描述

双端队列的使用

boolean offerFirst(E e); // 头插
boolean offerLast(E e); // 尾插
E pollFirst(); // 头删
E pollLast(); // 尾删
E peekFirst(); // 获取头元素
E peekLast(); // 获取尾巴元素
boolean isEmpty(); // 判断队列是否为空
int size(); // 获取队列的长度

这里用LinkedList举例

public static void main(String[] args) {
	Deque<Integer> deque = new LinkedList<>();
	System.out.println("===头插法===");
	deque.offerFirst(1);
	deque.offerFirst(2);
	deque.offerFirst(3);
	
	System.out.println("===尾插法===");
	deque.offerLast(4);
	deque.offerLast(5);
	deque.offerLast(6);
	
	System.out.println("===头删法===");
	System.out.println(deque.pollFirst()); // --> 3
	
	System.out.println("===尾删法===");
	System.out.println(deque.pollLast());// --> 6
	
	System.out.println("===查看头结点===");
	System.out.println(deque.peekFirst());// --> 2
	
	System.out.println("===查看尾巴结点===");// --> 5
	System.out.println(deque.peekLast());
}

双端队列的模拟实现

public class MyDeque {
    static class Node {
        int val;
        Node prev;
        Node next;

        Node(int val) {
            this.val = val;
        }
    }

    private Node head; //队头
    private Node last; //队尾
    private int usedSize;

    //入队,向双向链表尾部位置插入新节点
    public void lastOffer(int e) {
        Node node = new Node(e);
        if (head == null) { // 第一次插入
            head = node;
        }else {
            //不是第一次插入
            last.next = node;
            last.next.prev = last;
        }
        last = node;
        usedSize++;
    }
    //入队,向双向链表头部位置插入新节点
    public void headOffer(int e) {
        Node node = new Node(e);
        if (head == null) { // 第一次插入
            head = node;
            last = node;
        } else {
            //不是第一次插入
            node.next = head;
            head.prev = node;
            head = node;
        }
        usedSize++;
    }

    //出队列,将双向链表尾部第一个节点删除掉
    public int lastPoll() {
        if(isEmpty()) {
            throw new NullPointerException("链表无数据!");
        }
        int val = last.val;
        if (head == last) {
            head = null;
            last = null;
        }else {
            last = last.prev;
            last.next = null;
        }
        usedSize--;
        return val;
    }
    //出队列,将双向链表头部第一个节点删除掉
    public int headPoll() {
        if(isEmpty()) {
            throw new NullPointerException("链表无数据!");
        }
        int val = head.val;
        if (head == last) { //只有一个节点
            head = null;
            last = null;
        } else {
            head = head.next;
            head.prev.next = null;
            head.prev = null;
        }
        usedSize--;
        return val;
    }

    //获取队头元素,获取链表中第一个节点的值
    public int headPeek(){
        if (isEmpty()) {
            throw new NullPointerException("链表无元素!");
        }
        return head.val;
    }
    //获取队尾巴元素,获取链表中第一个节点的值
    public int lastPeek(){
        if (isEmpty()) {
            throw new NullPointerException("链表无元素!");
        }
        return last.val;
    }

    //队列的长度
    public int size() {
        return usedSize;
    }

    //队列是否为空
    public boolean isEmpty() {
        if (head == null) {
            return true;
        }
        return false;
    }

    //打印队列 ,, 注意,java 中Deque并没有这个方法,只是为了测试能看到队列中的数据方便写的
    public void display() {
        Node cur = head;
        while(cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值