JAVA学习日记(12)——栈和队列

1.栈(Stack)

1.1 概念

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

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

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

在这里插入图片描述
在这里插入图片描述

1.2 方法

方法解释
E push(E item)压栈
E pop()弹栈
E peek()查看栈顶元素
boolean empty()判断栈是否为空

代码示例

public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        stack.push(12);
        stack.push(13);
        stack.push(14);
        stack.push(15);
        System.out.println(stack);
        System.out.println("size = " + stack.size());
        int ret = stack.pop();
        System.out.println(stack.peek());
        System.out.println(ret);
        System.out.println(stack);
        System.out.println("size = " + stack.size());
    }

运行结果

[12, 13, 14, 15]
size = 4
14
15
[12, 13, 14]
size = 3

1.3 实现

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

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

代码示例

//顺序栈
class MyStack {
    public int[] elem;
    public int top;

    public MyStack() {
        this.elem = new int[10];
    }

    public void push(int item) {
    	//这里不考虑扩容
        if (isFull()) {
            System.out.println("溢出");
            return;
        }
        this.elem[top++] = item;
    }

    public int pop() {
        if (empty()) {
            throw new RuntimeException("栈为空");
        }
        int ret = this.elem[top-1];
        this.top--;
        return ret;
    }

    public int peek() {
        if (empty()) {
            return -1;
        }
        return this.elem[top-1];
    }

    public boolean empty() {
        if (top == 0) {
            return true;
        }else {
            return false;
        }
    }

    public boolean isFull() {
        if (top == elem.length) {
            return true;
        }else {
            return false;
        }
    }
}
public static void main(String[] args) {
        MyStack myStack = new MyStack();
        myStack.push(1);
        myStack.push(2);
        myStack.push(3);
        myStack.push(4);
        //System.out.println(myStack.pop());
        System.out.println(myStack.peek());
        System.out.println(myStack.pop());
        System.out.println(myStack.peek());
}

运行结果

4
4
3

如果要使用泛型,我们只需要添加上<>,把相应的类型变成泛型即可

代码示例

//顺序栈
class MyStack<T> {
    public T[] elem;
    public int top;

    public MyStack() {
        this.elem = (T[])new Object[10];
    }

    public void push(T item) {
        if (isFull()) {
            System.out.println("溢出");
            return;
        }
        this.elem[top++] = item;
    }

    public T pop() {
        if (empty()) {
            throw new RuntimeException("栈为空");
        }
        T ret = this.elem[top-1];
        this.top--;
        //这里置空防止T是个引用类型
        this.elem[top] = null;
        return ret;
    }

    public T peek() {
        if (empty()) {
            throw new RuntimeException("栈为空");
        }
        return this.elem[top-1];
    }

    public boolean empty() {
        if (top == 0) {
            return true;
        }else {
            return false;
        }
    }

    public boolean isFull() {
        if (top == elem.length) {
            return true;
        }else {
            return false;
        }
    }
}

public class TestDemo {
    public static void main(String[] args) {
        MyStack<Integer> myStack = new MyStack<>();
        myStack.push(1);
        myStack.push(2);
        myStack.push(3);
        myStack.push(4);
        //System.out.println(myStack.pop());
        System.out.println(myStack.peek());
        System.out.println(myStack.pop());
        System.out.println(myStack.peek());
    }
}

运行结果

4
4
3

2.队列(Queue)

2.1 概念

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

在这里插入图片描述

2.2 方法

解释方法1方法2
入队列add(e)offer(e)
出队列remove()poll()
队首元素element()peek()

一般情况下方法2用的比较多

示例代码

public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(1);
        queue.add(2);
        queue.add(3);
        queue.offer(5);
        queue.offer(6);
        queue.offer(7);
        System.out.println(queue);
        System.out.println(queue.poll());
        System.out.println(queue.peek());
        System.out.println(queue);
        System.out.println(queue.isEmpty());
    }

运行结果

[1, 2, 3, 5, 6, 7]
1
2
[2, 3, 5, 6, 7]
false

2.3 实现

队列也可以数组和链表结构实现,使用链表的结构实现更优一点,因为如果使用数组的结构,出队列在数组头上出,效率会很低
在这里插入图片描述
代码示例

class Node {
    public int val;
    public Node next;

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

class MyQueue {
    public Node head;
    public Node last;

    public void offer(int val) {
        Node node = new Node(val);
        if (head == null) {
            this.head = node;
            this.last = node;
        }else {
            this.last.next = node;
            this.last = this.last.next;
        }

    }

    public int poll() {
        int ret = this.head.val;
        if (head.next == null) {
            this.head = null;
            this.last = null;
            return ret;
        }else {
            this.head = this.head.next;
            return ret;
        }
    }

    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空");
        }
        return this.head.val;
    }

    public boolean isEmpty() {
        return this.head == null;
    }
}

public static void main(String[] args) {
        MyQueue myQueue = new MyQueue();
        myQueue.offer(1);
        myQueue.offer(2);
        myQueue.offer(3);
        myQueue.offer(4);
        System.out.println(myQueue.poll());
        System.out.println(myQueue.peek());
        System.out.println(myQueue.isEmpty());
 }

运行结果

1
2
false

转成泛型与上面同理

2.4 循环队列

假设我们现在想用数组实现一个队列,那么我们会发现如果每次假设数组头部出,尾部进,那么就会存在有空余的情况,那么这种情况到底是属于空还是满?我们就不好评判了,因此,要用数组实现,我们可以把数组看成是一个循环的数组,即就形成了循环队列

我们让front和rear分别代表队列的头和尾

  1. 刚开始的时候两个指针重合,此时队列为空
  2. 此时我们开始添加数据,每次添加一个,rear往后走一步
  3. 当rear和front相遇的时候我们可以判定为满,但是此时会出现一个问题,当我们rear和front只差一个位置的时候,此时再添加元素,rear会和front重合,按照我们的判断条件此时为空了,但是实际上整个队列里都放满数据,这里就有矛盾了
  4. 因此为了解决上述问题,我们可以通过放弃一个位置,即rear的下一位是front,此时我们就可以判定为满,如图所示便是满的,有了这样一个改变,我们给定的数组长度和实际可用会少一位,因此在实际使用的时候要达到预期长度,可以给长度加1
  5. 我们现在已经解决了判定是否未满的情况,但是通过看图,发现有很多小伙伴肯定会发现,此时当rear在数组最后位置上的时候,例如在图中7号位置上的时候,我们该怎样让rear移动呢?答案很简单,我们让此时rear每次移动它的下标加上1然后和整个数组的长度取模,即(rear+1)%数组.length,对于front来说也是一样的,即(front+1)%数组.length
    在这里插入图片描述
    示例代码
class MyCircularQueue {

    public int[] elem;
    public int front;
    public int rear;
   //构造方法
    public MyCircularQueue(int k) {
        this.elem = new int[k+1];
    }
    //入队
    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }else {
            elem[rear] = value;
            rear = (rear+1) % elem.length;
            return true;
        }
    }
    //出队
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }else {
            front = (front+1) % elem.length;
            return true;
        }
    }
    //显示对头
    public int Front() {
        if (isEmpty()) {
            return -1;
        }
        return this.elem[front];
    }
	//显示队尾
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }
        int index = this.rear == 0 ? this.elem.length-1 : this.rear-1;
        return this.elem[index];
    }
    //判断是否为空
    public boolean isEmpty() {
        return front == rear;
    }
    //判断是否为满
    public boolean isFull() {
        return (this.rear+1) % this.elem.length == front;
    }
}

public class TestDemo3 {
    public static void main(String[] args) {
        MyCircularQueue myCircularQueue = new MyCircularQueue(8);
        myCircularQueue.enQueue(1);
        myCircularQueue.enQueue(2);
        myCircularQueue.enQueue(3);
        myCircularQueue.enQueue(4);
        myCircularQueue.enQueue(5);
        System.out.println(myCircularQueue.Front());
        System.out.println(myCircularQueue.Rear());
    }
}

运行结果

1
5

3.双端队列(Deque)

3.1 概念

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。

那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

3.2 方法

错误处理抛出异常返回特殊值抛出异常返回特殊值
入队列addFirst(e)offerFirst(e)addLast(e)offerLast(e)
出队列removeFirst()pollFirst()removeLast()pollLast()
获取元素getFirst()peekFirst()getLast()peekList()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值