数据结构--栈和队列(下)

一丶队列

什么是队列?
上篇博客我们说了,栈是一种后进先出的数据结构,而在这里,同为数据结构的队列又是怎么的表现形式呢?
我们用一张图来进行查看:
在这里插入图片描述

(1)队列的概念

队列是只允许在一端进行插入,另外一端进行删除的特殊的线性表。进行插入操作的一端叫做队尾进行删除操作的一端叫做队头。因为这种特殊性,所以队列具有先进先出的性质。

(2)队列的分类

因为队列也是顺序结构,那么它的构造其实也分两种,顺序队列和链式队列

1>顺序队列

顺序队列大致情况如下图所示:
在这里插入图片描述
在这里,关于顺序队列的操作里面,有一点需要说明。就是删除操作
在这里删除操作有两种。

第一种:front不动

front不动进行删除的话,那么每删除一次,队列所有元素前移一位。
在这里插入图片描述
那么这意味着什么?每一次删除的时间复杂度都是O(N),所以这种的话就会很麻烦。

第二种:rear不动

还有一种就是尾指针rear不动,那么如果是这种方式。就会有一个很严重的问题,具体如下:
在这里插入图片描述
可以看到,如果是rear不动,front往后走,那么此时的时间复杂度就是O(1),但是此时有一个问题:就是当删除为空的时候,此时rear和front都在队列尾,这个时候还能插入嘛?那空间满了吗?

这个时候当然不能插入,但是空间没有满,甚至可以说都没有用到。所以就会造成假溢出

那么为了解决这种情况,我们怎么办呢?

头尾相连,用循环链表

(看后面)

2>链式队列

这种的话,就是只在头部和尾部进行插入和删除的特殊单链表。

//先定义节点
public static class Node<E>{
	 private E value;//节点值
	 private Node<E> next;//节点的next属性
	 public Node(E val){
		value = val;
	 	next = null;
	 	}
	 }
	 Node<E> front; // 标记队头
	 Node<E> back; // 标记队尾
	 int size;//记录有效节点
	 //插入操作
	 public boolean offer(E e){
	 	Node<E> node = new Node<>(e);
		if(null == front){
	 		front = node;
	 	}else{
	 		back.next = node;
	 	}
	 		back = node;
	 		size++;
	 		return true;
	 }
	 //删除操作
	 public E poll(){
	 	if(0 == size){
	 		throw new RuntimeException("队列为空,无法出队列");
	 	}
	 	Node<E> delNode = front;
		front = front.next;
		if(null == front){
	 		back = null;
	 	}
	 		size--;
	 		return delNode.value;
	 }
	 //查看队列头
	 public E peek(){
	 	if(0 == size){
	 		throw new RuntimeException("队列为空,无法获取队头元素");
	 }
	 	return front.value;
	 }
	 //查看是否为空
	 public boolean isEmpty(){
	 	return 0 == size;
	 }
	 public int size(){
	 	return size;
 }

二丶循环队列

循环队列是用来干嘛的呢?就是为了解决我上述所说的顺序队列rear不动进行删除操作的时候所造成的的假溢出问题
具体图示如下:
在这里插入图片描述

具体的代码用一道LeetCode题来进行说明,题目来源:
设计循环队列

具体的解答如下:

class MyCircularQueue {
    int[] array;
    int front;//定义头结点
    int rear;//定义尾结点
    int count;//定义有效节点
    int N;//定义数组长度
    
	//定义数组
    public MyCircularQueue(int k) {
        array = new int[k];
        N = k;
    }
    //入队列
    public boolean enQueue(int value) {
        if(isFull()){
            return false;
        }
        array[rear] = value;
        rear++;
        if(rear == N){
            rear = 0;
        }
        count++;
        return true;
    }
    //出队列
    public boolean deQueue() {
        if(isEmpty()){
            return false;
        }
        front++;
        front %= N;
        count--;
        return true;
    }
    //返回队列头
    public int Front() {
        if(isEmpty()){
            return -1;
        }
        return array[front];
    }
    //返回队列尾
    public int Rear() {
        if(isEmpty()){
            return -1;
        }
        return array[(rear + N - 1)% N];
    }
    //检查是否满了
    public boolean isEmpty() {
        return 0 == count;
    }
    //检查是否为空
    public boolean isFull() {
        return count == array.length;
    }
}

这里的话有一个点需要特别说明一下。

关于返回队列尾

这里的话如果返回队列尾有一个地方需要特别说明一下。因为我们的rear指针指向最后一个元素的下一位,也就是说如果是队列满的话就是这种情况。
在这里插入图片描述
那么如果此时要返回队列尾的话是返回多少呢?

很明显此时rear指针下标是0,那么返回前一位就是 -1,数组越界了呀

为了解决这种问题怎么办呢?

(rear - 1 + array.length) % array.length

三丶双端队列

所谓双端队列是指可以在两端都进行插入和删除的特殊队列。这种队列在这里我们不进行过多说明,用双向链表实现一下。

 public static class ListNode<E>{

        ListNode<E> next;
        ListNode<E> pre;
        E val;

        ListNode(E val){
            this.val = val;
        }
    }
    ListNode<E> front;
    ListNode<E> rear;
    int size = 0;

    //判断是否为空
    public boolean isEmpty(){
        return 0 == size;
    }
    public int size(){
        return size;
    }
    //入队列
    public void offer(E e){
        ListNode<E> newNode = new ListNode<>(e);
        if(isEmpty()){
            front = newNode;
        }else{
            rear.next = newNode;
            newNode.pre = rear;
        }
        rear = newNode;
        size++;
    }

    //出队列
    public E poll(){
        E key = null;
        if(isEmpty()){
            return  null;
        }
        if(size == 1){
            rear = null;
            front = null;
        }
        key = rear.val;
        rear = rear.pre;
        rear.next.pre = null;
        rear.next = null;
        size--;
        return key;
    }
    //获取队头
    public E peek(){
        return front.val;
    }
    

四丶总结

刷题刷题!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值