队列的理解与使用



1、概念与理解


队列在实际生活中是经常遇到的,比如上班高峰期地铁站限流。回归到本质,队列其实也是一种运算受限制的线性表,它的特点是,只可以在对头删除元素,在队尾插入元素,所以这是一种先进先出的数据结构。它和栈不一样,栈是只有一端可以执行插入删除的操作,因此定义栈的运算的时候,只要一个标记能够标志顶点元素就可以。但是在队列中,它是有两端的,并且两端各执行不一样的操作,所以定义一个数据结构来表达队列相对比较复杂一点。



2、队列的数据结构定义



队列是一种受限制的线性表,因此也拥有线性表的特点。根据存储方式的不同,队列可以分为顺序队列和链式队列,根据不同的存储方式,定义的结构也不同。


2.1 、顺序队列的数据结构定义


既然是顺序队列,那么必然包含一个数组成员。前面提到队列含有两端,并且各端对应的操作不相同,因此很有必要使用两个标记来标志这两端。一般我们把执行删除操作的一端称为对头,执行插入操作的一端称为队尾。下面的数据结构均用C语言语法定义,随后会使用实际例子实现基本运算。


typedef struct queue
{
dataType data[MAXSIZE];
int front,rear;
}LQueue


对于顺序队列我们还需要考虑一个问题,假如我们现在有一个容量为5的名称是a的队列,一开始给队列中a[0]-a[4]赋值0,1,2,3,4。然后移除对头的元素0,1。此时,队列还剩2,3,4,空余两个位置。此时如果我们在队尾插入元素,会发现,队列已经满了。但是明明有两个空余的位置对吧?我们可以将队列想象成首位连接的一个圆圈,我们称为循环队列。在确定插入的元素应该处于哪个位置的时候,使用(rear+1)%5求出模,就是新插入的元素应该处于的位置。

好了,上面的一个问题解决了。现在 考虑一下,队列什么时候是空?即对头等于队尾的时候,这里是front==rear表示队列为空。再考虑一下,队列什么时候为满,没错也是front==rear。好了,当front==rear的时候,究竟是空表还是满表呢。思路如下:

假如现在进行插入操作(n代表队列的容量):
rear=(rear+1)%n,front不变。

假如现在进行删除操作:
front=(front+1)%n,rea不变。

发现了没有,其实如果我们执行的是插入操作遇到rear==front的情况,说明对满。执行删除操作的时候则是对空。


2.2、链式队列的数据结构定义


链式队列和顺序队列的不同之处在于,它的数组长度是动态增长,所以它并没有对满的忧虑。首先需要一个节点,表示每个队列元素的结构,其次我们还需要一个数据结构来定义指向对头和队尾的节点。如下:

typedef struct node
{
dataType data;
struct node *next;
}PNode

typedef struct queue
{
PNode *read,*front;
}PQueue

其中node是队列的节点,queue表示整个队列,含有对头和队尾元素。



3、队列的基本运算


①、初始化队列:init_queue

②、判断对空:isEmpty_queue

③、对头元素出队:delete_element

④、队尾添加元素:insert_element

⑤、读取队头元素:get_element

上述只是基于队列的基本运算,实际应用中应该根据队列的特点以及需求共同定义运算。




4、C语言实现链式队列的基本运算



/*
实现链式队列的基本运算
*/

#include <stdio.h>
#include <stdlib.h>


/*
声明链式队列的节点元素
*/
typedef struct node
{
	int data;//存放数据
	struct node *next;//连接下一节点
}Node;


/*
由于队列需要知道队头和队尾元素,所以声明一个队列的基本结构
*/

typedef struct queue
{
	Node *front;//指向对头元素
	Node *rear;//指向队尾元素
}Queue;


/*
声明基本运算
*/

Queue* init_queue();//初始化队列
int isEmpty_queue(Queue *pQueue);//判断对空
void insert_element(Queue *pQueue, int data);//向队尾插入元素
void delete_element(Queue *pQueue);//移除对头元素
int get_element(Queue* pQueue);//获取对头元素

int main(void)
{
	Queue *queue = init_queue();
	for (int i = 1; i <= 5; i++)
	{
		insert_element(queue, i);
	}
	printf("队头元素:%d\n", get_element(queue));
	delete_element(queue);
	printf("删除1之后队头元素:%d\n", get_element(queue));

	system("pause");
	return 0;
}

/*
初始化队列
*/
Queue* init_queue()
{
	Queue *pQueue = (Queue *)malloc(sizeof(Queue));
	//先定义一个表头,然后让队列都指向它,这样就把队列和队列的元素连接起来了。
	Node *pHeader = (Node *)malloc(sizeof(Node));
	pHeader->data =-1;
	pHeader->next = NULL;
	//将对头和队尾都指向表头元素。
	pQueue->front = pHeader;
	pQueue->rear = pHeader;
	return pQueue;
}

/*
判断对空
*/
int isEmpty_queue(Queue *pQueue)
{
	//因为链式表是动态增长的,长度是没有限制的,所以当对头等于队尾,一定是空队列。
	if (pQueue->front == pQueue->rear)
		return 1;
	return 0;
}

/*
在队尾插入元素
*/
void insert_element(Queue *pQueue, int data)
{
	Node *pNode = (Node *)malloc(sizeof(Node));
	pNode->data = data;
	pNode->next = NULL;
	//先获取当前的队尾元素
	Node *pRear = pQueue->rear;
	//此队尾元素是即将插入队尾元素的下一个元素
	pRear->next = pNode;
	//将当前结点设为队尾元素
	pQueue->rear = pNode;
}

/*
移除队头元素
对头用于指向头节点
*/
void delete_element(Queue *pQueue)
{
	if (isEmpty_queue(pQueue) == 1)
	{
		printf("对空/n");
	}
	else
	{
		//获取当前队列的头节点
		Node *pHeader = pQueue->front;
		//获取当前队列的头节点的下一节点元素,即队头
		Node *pFront = pHeader->next;
		//获取对头的下一节点,作为新的对头
		Node *pNewFront = pFront->next;

		pHeader->next=pNewFront;//将新的对头元素赋值给头节点。
		free(pFront);

		//如果队列只有一个元素,则删除之后,队尾指针则指向了不确定的值,所以应该重新将队尾指向头节点
		if (pNewFront == NULL)//说明是空的队列
		{
			pQueue->rear = pHeader;
		}
	}
}


/*
获取队头元素的值
*/
int get_element(Queue* pQueue)
{
	if (isEmpty_queue(pQueue) == 1)
	{
		return -1;
	}
	Node *pFront = pQueue->front->next;
	return pFront->data;
}



注释已经说明的很清楚了。我们很容易犯得一个错误的思维定势是,总是认为next是从队尾指向队头的方向。在队列中,其实是队头指向队尾的方向。


运行效果:






5、c++实现链式队列


首先定义一个Node.h。

class Node
{
public:
	Node();
	~Node();
	int data;
	Node *next;

private:
	
};

Node::Node()
{
}

Node::~Node()
{
}


其次是定义Queue.h

#include "Node.h"
#include <iostream>

class Queue
{
public:
	Queue();
	~Queue();
	Queue(Node *header);
	int isEmpty_queue(Queue *pQueue);//判断对空
	void insert_element(Queue *pQueue, int data);//向队尾插入元素
	void delete_element(Queue *pQueue);//移除对头元素
	int get_element(Queue* pQueue);//获取对头元素
	Node *rear;
	Node *front;

private:

};
Queue::Queue(Node *header)
{
	
	rear = header;
	front = header;
}
Queue::Queue()
{
}

Queue::~Queue()
{
}


/*
判断对空
*/
int isEmpty_queue(Queue *pQueue)
{
	//因为链式表是动态增长的,长度是没有限制的,所以当对头等于队尾,一定是空队列。
	if (pQueue->front == pQueue->rear)
		return 1;
	return 0;
}

/*
在队尾插入元素
*/
void insert_element(Queue *pQueue, int data)
{
	Node *pNode = new Node;
	pNode->data = data;
	pNode->next = nullptr;
	//先获取当前的队尾元素
	Node *pRear = pQueue->rear;
	//此队尾元素是即将插入队尾元素的下一个元素
	pRear->next = pNode;
	//将当前结点设为队尾元素
	pQueue->rear = pNode;
}

/*
移除队头元素
对头用于指向头节点
*/
void delete_element(Queue *pQueue)
{
	if (isEmpty_queue(pQueue) == 1)
	{
		
		std::cout<<"对空"<<std::endl;
	}
	else
	{
		//获取当前队列的头节点
		Node *pHeader = pQueue->front;
		//获取当前队列的头节点的下一节点元素,即队头
		Node *pFront = pHeader->next;
		//获取对头的下一节点,作为新的对头
		Node *pNewFront = pFront->next;

		pHeader->next = pNewFront;//将新的对头元素赋值给头节点。
		free(pFront);

		//如果队列只有一个元素,则删除之后,队尾指针则指向了不确定的值,所以应该重新将队尾指向头节点
		if (pNewFront == nullptr)//说明是空的队列
		{
			pQueue->rear = pHeader;
		}
	}
}

/*
获取队头元素的值
*/
int get_element(Queue* pQueue)
{
	if (isEmpty_queue(pQueue) == 1)
	{
		return -1;
	}
	Node *pFront = pQueue->front->next;
	return pFront->data;
}

最后是Main.cpp

#include <iostream>
#include "Queue.h"

int main()
{
	Node *header = new Node;
	header->data = -1;
	header->next = nullptr;
	Queue *queue = new Queue(header);
	for (int i = 1; i <= 5; i++)
	{
		insert_element(queue, i);
	}
	std::cout<<"队头元素"<<get_element(queue)<<std::endl;
	delete_element(queue);
	std::cout << "移除1之后队头元素" << get_element(queue) << std::endl;

	system("pause");
	return 0;
}


运行结果:





6、java实现顺序队列

Queue.java

public class Queue {
	
	private int data[]=new int[5];
	private int front=-1,rear=-1,num=0;//num表示队列的元素
	
	
	/*
	判断对空
	*/
	public  boolean isEmpty_queue()
	{
		//因为链式表是动态增长的,长度是没有限制的,所以当对头等于队尾,一定是空队列。
		if (num==0)
			return true;
		return false;
	}
	
	
	/*
	在队尾插入元素
	*/
	public void insert_element( int param)
	{
		
		if(num==5)
		{
			System.out.println("队列满");
			return ;
		}
		else
		{
			rear=(rear+1)%5;
			data[rear]=param;
			num++;
		}
	}
	
	/*
	移除队头元素
	对头用于指向头节点
	*/
	public void delete_element()
	{
		if(isEmpty_queue())
		{
			System.out.println("队空");
			return ;
		}else
		{
			front=(front+1)%5;
			num--;
		}
	}
	
	/*
	获取队头元素的值
	*/
	public int get_element()
	{
		if (isEmpty_queue() )
		{
			System.out.println("队空");
			return -1;
		}
		if(front==-1)
			front=0;
		return data[front];
	}

}


QueueMain.java

/**
 *java实现顺序队列
 */
public class QueueMain {
	
	public static void main(String args[])
	{
		Queue queue = new Queue();
		for (int i = 1; i <= 5; i++)
		{
			 queue.insert_element(i);
		}
		System.out.println("队头元素:"+ queue.get_element());
		queue.delete_element();
		System.out.println("删除1之后队头元素:"+queue.get_element());

		
	}

}


运行结果:







---------文章写自:HyHarden---------

--------博客地址:http://blog.csdn.net/qq_25722767-----------





  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值