数据结构梳理(4) - 队列

前言上次说完了栈,今天我们再来看看它的好兄弟----队列,大致的梳理内容和栈差不多,不过在实际应用当中,队列相比栈来说,有很多的变种,而且它们使用都非常的广泛,我们除了要会最基本的队列的实现,还要扩展下知识广度,知道队列的一系列变种以及使用等。目录一、队列的特性及种类二、基于数组实现队列三、基于链表实现队列四、jdk源码中的Queue实现五、优先级队列六、阻塞队列七、双端队列...
摘要由CSDN通过智能技术生成

前言

上次说完了栈,今天我们再来看看它的好兄弟----队列,大致的梳理内容和栈差不多,不过在实际应用当中,队列相比栈来说,有很多的变种,而且它们使用都非常的广泛,我们除了要会最基本的队列的实现,还要扩展下知识广度,知道队列的一系列变种以及使用等。

目录

一、队列的特性及种类
二、基于数组实现队列
三、基于链表实现队列
四、jdk源码中的Queue实现
五、优先级队列
六、阻塞队列
七、双端队列

正文

一、队列的特性及变种

和栈一样,队列的特性也可以用四个字来概括,那就是“先进先出",和栈不同,栈是只能操作一端,都是栈顶操作,而队列是在队尾插入元素,在队头删除元素,非常类似排队的效果,如果喜欢弄混,可以想一想自己平时排队,是不是都是在队尾去开始排,等排到队头了,也就离开队列了,这个是队列的基本特性。

队列因其先进先出的性质,应用比较广泛的就是消息队列,例如在各种异步处理中,当然我们可能发现在平常的开发过程中,听到的队列,都是诸如“阻塞队列”,“优先级队列”等名词,真正单纯的队列使用比较少,其实这些都是基于队列的变种,都是在队列上“添枝加叶”,然后为了方便的实现业务场景的需求,才衍生出来的,具体各种不同的类型也有很多,我们没有必要去全部掌握它们,只要挑几个比较典型的掌握之后,然后遇到问题时,也可以自己订制一个这样特别的队列来满足需求,后面会挑几个典型的特殊队列来一起学习。

然后,再放张图加深一下对队列的映像
在这里插入图片描述

二、基于数组实现队列

我们按照同样的套路,首先是手动实现一遍队列这个数据结构,老规矩,先基于数组实现,然后是基于链表,好了,开始吧!

有了之前实现栈的经验,我们同样的,首先确定基本的成员变量及其初始化工作,因为是基于数组,所以我们声明一个数组成员变量来存放数据,然后为了实现入队出队这两个操作,我们需要分别声明两个指针,然后再加上一个队列当前数据数量的size变量。ok,成员变量基本就是这么多,然后就是初始化的问题,初始化主要是使用默认大小初始化数组,然后队头指针和队尾指针,一般根据代码风格,指向0或者-1,都可以,最终代码如下

private static final int length_default=10;//队列默认的大小
private int[] arr;
private int head;//队头
private int tail;//队尾
private int size;//队列元素个数

public ArrayQueue() {
	this(length_default);
}

public ArrayQueue(int length) {
	arr=new int[length];
	head=0;
	tail=-1;
}

接下来就是核心操作入队和出队的操作,刚才说了,我们实现这两个操作,最核心的点就是借助两个队头队尾的指针,我们先来思考入队操作,当一个元素要插入,我们的原则是在队尾插入,所以应该是移动队尾指针,队尾指针默认是指向-1的,所以我们将队尾指针++,然后在队尾指针处填入插入的数据,这样就实现了队尾的数据插入,然后同样的,我们再思考出队,出队是在队头进行的操作,所以相应的也是移动队头这个指针,当我们要出队的时候,只需将队头指针++即可,这样就实现了出队操作。

但是为了提高数组的控件利用率,当我们的数组中存放的数据元素小于数组长度,但是队头指针和队尾指针又指向了数组最后一个元素,那么可使用取余操作,来将指针指向0,实现循环效果,这样只有当数组中的元素数量达到队列容量时,也就是存满了,才会提示队列已满。

最终入队和出队的代码如下

//在队尾插入数据
public void enQueue(int data){
	if(isFull()){
		throw new RuntimeException("队列满啦");
	}	
	tail=(tail+1)%arr.length;
	arr[tail]=data;
	size++;
}

//在队头删除数据
public void deQueue(){
	if(isEmpty()){
		throw new NullPointerException("队列为空");
	}
	head=(head+1)%arr.length;
	size--;
}

同样的,为了使用的方便,我们最好添加一些辅助方法,例如判空,判满,获取队头和队尾的元素等,我最后一共添加了如下辅助方法

public int getHead(){
	if(isEmpty()){
		throw new RuntimeException("队列为空");
	}
	return arr[head];
}

public int getTail(){
	if(isEmpty()){
		throw new RuntimeException("队列为空");
	}
	return arr[tail];
}

public int getSize(){
	return size;
}

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

public boolean isFull(){
	return size==arr.length;
}

public void clear(){
	head=0;
	tail=-1;
	size=0;
}

@Override
public String toString() {
	if(isEmpty()){
		return "null";
	}
	String str = "[ ";  
	int j=head;
       for (int i = 0; i < size; i++) {  
           str += arr[j%arr.length] + ", ";  
           j++;
       }  
       str = str.substring(0, str.length()-2) + " ]";  
       return str;
}

最后,不要忘了写测试用例测试它哦!

三、基于链表实现队列

上面实现完了基于数组版本的,接下来实现基于链表的,由于和上面差不多,我就不赘述了,首先是节点和成员变量的定义,然后就是初始化工作,代码如下

public class Node{//节点内部类
	private int data;
	private Node next;
	
	public Node() {
		
	}
	
	public Node(int data,Node next){
		this.data=data;
		this.next=next;
	}
	
	public int getData(){
		return data;
	}
}

private Node head;//队头
private Node tail;//队尾
private int size;

public LinkedQueue() {//构造函数
	head=null;
	tail=null;
	size=0;
}

然后是出队和入队的操作,思想和上面的类似

public void enQueue(int data){
	Node node=new Node(data, null);
	if(head==null){
		head=node;
	}else{
		tail.next=node;
	}
	tail=node;
	size++;
}

public void deQueue(){
	if(head==null){
		throw new NullPointerException(
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值