Java双端队列的代码实现

什么是顺序队列

顺序队列是先进先出的数据结构,它是一种特殊的线性表,只能在队头front进行删除操作,只能在队尾rear进行插入操作,所以把进行插入操作的一端称为队尾,进行删除操作的一端称为队头。

顺序队列的队头指针front指向队头元素,队尾指针rear指向下一个入队元素的存储位置

由于入队和出队操作时头尾指针只会增加不会减小,当rear指向到分配的连续空间之外时,队列无法再插入新元素,被删除元素的空间永远无法被重新利用,这种情况叫队列的“假上溢”现象,即头尾指针前的空间没有使用,但是却显示队列已满。
在这里插入图片描述
为了避免“假上溢”的现象,实际使用队列时,一般使用循环队列。


循环队列

在实际使用队列时,为了使队列空间可以重复使用,避免“假上溢”现象,人们使用%(取余)运算控制front和rear指针的移动:如果front指针+1或者rear指针+1时超过了一开始分配给队列的使用空间,就使用%运算使他们指向这片使用空间的起始位置。
在分配内存空间时,循环队列实际分配的空间=要分配的空间+1,此区域不进行任何操作,目的是区分队列全空和队列全满时的情况。

循环队列front指针始终指向队列头元素,rear指针指向队尾元素的下一个位置
循环队列判空条件为front == rear,判满条件为front == (rear + 1) % MaxSize

在这里插入图片描述

双端队列

双端队列,顾名思义就是一个两端都是结尾的队列,相对于普通队列,双端队列的头部head和尾部tail都可以进行出队和入队操作,因此双端队列可以同时实现栈和队列的操作。为了节省空间,一般双端队列都是在循环队列的基础上实现。

双端队列head指针始终指向队列头元素,tail指针指向队尾元素的下一个位置
双端队列判空条件为head == tail,判满条件为head == (tail + 1) % MaxSize
双端队列若使用头部入队和头部出队(或尾部入队和尾部出队)可以实现栈的功能

在这里插入图片描述

双端队列的基本操作

双端队列一般有如下基本操作:

  • isFull():判断队列是否已满
  • isEmpty():判断队列是否为空
  • headInsert():从队头插入
  • tailInsert():从队尾插入
  • headPop():从队头出队
  • tailPop():从队尾出队
  • getHead():获取队头元素
  • getTail():获取队头元素
  • display():遍历双端队列

## 双端队列的定义 定义一个双端队列类DQueue,里面安放指针和各种操作的方法,使用构造器初始化maxSize的值,初始化后的queue数组的实际长度等于队列长度+1,目的是建立循环队列区分队满和队空。
  • 定义变量maxSize表示双端队列的最大容量。
  • 定义数组queue[]用于存放数据。
  • 定义队头指针head指向队列的队头元素。
  • 定义队尾指针tail指向队尾元素的下一个区域。
//双端队列类
class DQueue{
	
	public int maxSize;			//双端队列的最大容量
	public int queue[];  	   	//数据域
	public int head = 0;		//队头指针,指向第一个元素
	public int tail = 0;		//队尾指针,指向最后一个元素的后一个位置
	
	public DQueue(int n) {
		//队尾指针需要指向最后一个元素的后一个位置,所以需要空出一个位置,即数组的实际大小+1
		this.maxSize = n + 1;
		queue = new int[this.maxSize];
	}
}

判断队满 isFull()

因为双端队列是在循环队列的基础上实现的,所以判断队满条件和循环队列判断队满的条件一致。

//判断队列是否已满
	public boolean isFull() {
		return (tail + 1) % maxSize == head;
	}

判断队空 isEmpty()

因为双端队列是在循环队列的基础上实现的,所以判断队空条件和循环队列判断队空的条件一致。

//判断队列是否为空
	public boolean isEmpty() {
		return tail == head;
	}

从头部入队 headInsert()

因为是从头部入队且head指针指向的是对头元素,所以从头部入队前要找到head指针所指位置的前一个位置,此位置就是待插入位置。修改head指针,使head指针-1指向待插入位置,然后使用%运算防止越界。

  • 判断队列是否已满。
  • 修改head指针,找到head指针的前一个位置。
  • 插入数据。
//从头部入队
	public void headInsert(int data) {
		//先判断队列是否已满
		if(isFull())
			return ;
		//头插,head自减1找到待插入位置,使用%防止越界
		head = (head - 1 + maxSize) % maxSize;
		queue[head] = data;
	}

从尾部入队 tailInsert()

因为tail指向的队尾元素的后一个位置,此位置正好是待插入位置。所以从尾部入队时,先在tail所指位置插入数据,然后再修改tail指针,使tail指针+1指向插入元素位置的后一个位置,并使用%运算防止越界。

  • 判断队列是否已满。
  • 插入数据。
  • 找到tail指针的后一个位置,修改tail指针。
//从尾部入队
	public void tailInsert(int data) {
		//先判断队列是否已满
		if(isFull())
			return ;
		queue[tail] = data;
		//尾插,tail自增1找到待插入的位置,使用%防止越界
		tail = (tail + 1) % maxSize;
	}

从头部出队 headPop()

head始终指向头部第一个元素,从头部出队前先使用变量接取待出队的值,然后修改head指针使其+1,即向后移动指向已出队元素的后一元素。修改指针时同样使用%运算防止溢出。

  • 判断队列是否为空。
  • 使用变量接取队头元素。
  • 修改head指针,找到head指针的后一个位置。
  • 返回变量值。
//从头部出队
	public int headPop() {
		//先判断队列是否为空
		if(isEmpty())
			return -1;
		int value = queue[head];
		//出队后向后移动head指针
		head = (head + 1) % maxSize;
		return value;
	}

从尾部出队 tailPop()

因为tail始终指向队尾元素的下一个位置,所以出队前先使tail-1,即向前移动tail指针,使用%运算防止溢出,向前移动后的tail指针指向的元素即是队尾元素。

  • 判断队列是否为空。
  • 修改tail指针,找到tail指针的前一个位置。
  • 使用变量接取队尾元素。
  • 返回变量值。
//从尾部出队
	public int tailPop() {
		//先判断队列是否为空
		if(isEmpty())
			return -1;
		//先向前移动tail指针指向待出队数据位置
		tail = (tail - 1 + maxSize) % maxSize;
		int value = queue[tail];
		return value;
	}

获取队头元素 getHead()

只需返回head指针所指元素即可。

//获取队头元素
	public int getHead() {
		return queue[head];
	}

获取队尾元素 getTail()

队尾指针指向队尾元素的后一个位置,队尾元素位置就是tail-1。如果队尾指针目前指向的queue数组索引是0,此时-1就会产生数组越界,所以取队尾元素时需要%运算来防止越界。

//获取队尾元素
	public int getTail() {
		return queue[(tail - 1 + maxSize) % maxSize];
	}

遍历队列 display()

双端队列的遍历方式与循环队列的遍历相同,不停往后移动head指针来遍历队列中的元素,遍历时同样需要%运算防止数组越界。

//遍历队列
	public void display() {
		if(isEmpty()) {
			System.out.println("队列空");
			return ;
		}
		while(head != tail) {
			System.out.println(queue[head]);
			//head向后移动
			head = (head + 1) % maxSize;
		}
	}

完整代码

//循环双端队列类
class DQueue{
	
	public int maxSize;	//双端队列的最大容量
	public int queue[];  	   	//数据域
	public int head = 0;		//队头指针,指向第一个元素
	public int tail = 0;		//队尾指针,指向最后一个元素的后一个位置
	
	public DQueue(int n) {
		//队尾指针需要指向最后一个元素的后一个位置,所以需要空出一个位置,即数组的实际大小+1
		this.maxSize = n + 1;
		queue = new int[this.maxSize];
	}
	
	
	//判断队列是否已满
	public boolean isFull() {
		return (tail + 1) % maxSize == head;
	}
	
	
	//判断队列是否为空
	public boolean isEmpty() {
		return tail == head;
	}
	
	
	//从头部入队
	public void headInsert(int data) {
		//先判断队列是否已满
		if(isFull())
			return ;
		//头插,head自减1找到待插入位置,使用%防止越界
		head = (head - 1 + maxSize) % maxSize;
		queue[head] = data;
	}
	
	
	//从尾部入队
	public void tailInsert(int data) {
		//先判断队列是否已满
		if(isFull())
			return ;
		queue[tail] = data;
		//尾插,tail自增1找到待插入的位置,使用%防止越界
		tail = (tail + 1) % maxSize;
	}
	
	
	//从头部出队
	public int headPop() {
		//先判断队列是否为空
		if(isEmpty())
			return -1;
		int value = queue[head];
		//出队后向后移动head指针
		head = (head + 1) % maxSize;
		return value;
	}
	
	
	//从尾部出队
	public int tailPop() {
		//先判断队列是否为空
		if(isEmpty())
			return -1;
		//先向前移动tail指针指向待出队数据位置
		tail = (tail - 1 + maxSize) % maxSize;
		int value = queue[tail];
		return value;
	}
	
	
	//获取队头元素
	public int getHead() {
		return queue[head];
	}
	
	
	//获取队尾元素
	public int getTail() {
		return queue[(tail - 1 + maxSize) % maxSize];
	}
	
	
	//遍历队列
	public void display() {
		if(isEmpty()) {
			System.out.println("队列空");
			return ;
		}
		while(head != tail) {
			System.out.println(queue[head]);
			//head向后移动
			head = (head + 1) % maxSize;
		}
	}
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值