队列队列的分类和实现
Posted lulipro - 代码钢琴家
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了队列队列的分类和实现相关的知识,希望对你有一定的参考价值。
队列简介
如图是含有n个元素的队列的模型。根据队列的出入元素特点,可以确定,元素a1最先入队,紧接着a2,s3 ... 如果a2要出队,必须等a1出队。a1最先入队,也是最先出队,an最后入队,也是最后出队。
链式队列
链式队列是队列的实现方式之一。链式队列内部使用带头结点的单向链表来实现。它的好处的是灵活,队列容量理论上是不受限制的。
我们使用链表的首结点来表示队列的队头,链表的尾结点代表队尾。
当队列为空时,队尾元素指针指向头结点headNode。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cassert> #define NDEBUG struct Node{ int element; //结点保存的元素 Node*next; //next指针 Node(int e=0, Node*nxt=NULL):element(e),next(nxt) { } }; class LinkedQueue { private: Node headNode; //头结点 ,headNode.next 就是队头结点的指针 Node* pRearNode; //表的最后一个结点的指针,队尾结点的指针 int size; //队列实际元素个数 public: LinkedQueue():headNode(),pRearNode(&headNode),size(0) { //初始化时, pRearNode 指向 headNode } ~LinkedQueue() { clear(); } void clear() { Node*p = headNode.next; Node*t; while(p!=NULL){ t = p; p=p->next; delete t; } //回归初始状态 headNode.next = NULL; pRearNode = &headNode; size=0; } bool isEmpty()const { #ifndef NDEBUG if(size==0){ //调试用 assert(pRearNode == &headNode); } #endif return (size==0 && pRearNode == &headNode ) ; } int length()const { return size; } //入队 bool enQueue(int e) { Node*p_new = new Node(e,NULL); pRearNode->next = p_new; pRearNode = p_new; size++; } //出队 bool deQueue() { if(isEmpty()) return false; //如果队为空 Node*p_del = headNode.next; //获取待删结点的指针 headNode.next = (headNode.next)->next; //跳过,链接 //如果删除的是最后一个结点。则应该重新赋值pRearNode,指向headNode if(pRearNode == p_del) pRearNode = &headNode; delete p_del; size--; } //获取队尾元素 bool getRear(int& e) const { if(!isEmpty()){ e = pRearNode->element; return true; } return false; } //获取队头元素 bool getFront(int& e)const { if(!isEmpty()){ e = (headNode.next)->element; return true; } return false; } }; int main() { using namespace std; LinkedQueue queue; printf("length is :%d\\n",queue.length()); printf("is empty? :%d\\n",queue.isEmpty()); puts("\\n----------put 10 elements to the queue-----------"); for(int i=0;i<10;++i){ queue.enQueue(i); } printf("length is :%d\\n",queue.length()); printf("is empty? :%d\\n",queue.isEmpty()); printf("--------pop 15 elements from the queue-------------\\n"); for(int i=0,e;i<15;++i){ queue.getFront(e); if(queue.deQueue()) printf("%d ",e); } printf("\\nlength is :%d\\n",queue.length()); printf("is empty? :%d\\n",queue.isEmpty()); return 0; }
循环队列
具体实现要点
我们必须让循环队列的索引值限制在一定的范围内(长度我n的数组的索引一定是0~n-1),而不是让rear一直加1或者front一直减1。可以使用数学问题去解决:我们知道:将一个数M对n取模后,得到的结果将被映射到 0~n-1之间,循环队列就是利用的这个特点来完成索引的变化的。
实现方式1
设一个变量size,保存队列中元素的实际个数。每次出队,size减1,入队,size加1。而队列的 空,满,队列长度都需要使用他来实现。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cassert> class CycleQueue { private: enum{QUEUE_CAPACITY = 10}; //内部常量,存储循环队列的最大容量 int*elements ; //存储元素的数组 int front; //保存队头结点的索引 int rear; //保存下一个即将入队元素在数组中的索引。 int size; //保存队列的实际容量 public: CycleQueue() { elements = new int[QUEUE_CAPACITY]; front = rear=0; size =0; } ~CycleQueue() { delete[] elements; } void clear() { //回归初始状态 front = rear=0; size =0; } bool isEmpty()const { return size ==0; } bool isFull()const { return size == QUEUE_CAPACITY; } int length()const { return size; } //入队 bool enQueue(int e) { if(isFull()) return false; elements[rear] = e; rear = (rear+1)%QUEUE_CAPACITY; size++; return true; } //出队 bool deQueue() { if(isEmpty()) return false; front = (front+1)%QUEUE_CAPACITY; size --; return true; } //获取队尾元素 bool getRear(int& e) const { if(isEmpty()) return false; e= elements[rear-1]; return true; } //获取队头元素 bool getFront(int& e)const { if(isEmpty()) return false; e= elements[front]; return true; } };
实现方式2
可以不使用size变量,另一种方法也可以实现队列的 空,满,队列长度的获取。即:让队列空出一个元素空间,我称他为标记空间。因此数组长度为n的循环队列,则只能存储n-1个元素。
这个标记空间是循环队列中队尾元素逻辑上的后一个数组空间,但是这个空间在数组中的实际位置也是随着出队,入队动态变化的。
ARRAY_CAPACITY是内部数组实际的容量,他的值是QUEUE_CAPACITY+1。因为队列容器比数组容量少一。
空判断:rear == front ? ”空“:"不为空"
满判断:(rear+1)%ARRAY_CAPACITY == front ? ”满“:"不为满"
实际元素个数:(rear - front + ARRAY_CAPACITY) % ARRAY_CAPACITY
#include<iostream> #include<cstdio> #include<cstdlib> #include<cassert> class CycleQueue { private: enum{QUEUE_CAPACITY=10}; //储循环队列的最大容量. enum{ARRAY_CAPACITY=QUEUE_CAPACITY+1}; //数组容量 int*elements ; //存储元素的数组 int front; //保存队头结点的索引 int rear; //保存下一个即将入队元素在数组中的索引。 public: CycleQueue() { elements = new int[ARRAY_CAPACITY]; front = rear=0; } ~CycleQueue() { delete[] elements; } void clear() { //回归初始状态 front = rear=0; } bool isEmpty()const { return front == rear; } bool isFull()const { return (rear+1)%ARRAY_CAPACITY == front; } int length()const { return (rear-front + ARRAY_CAPACITY)%ARRAY_CAPACITY; } //入队 bool enQueue(int e) { if(isFull()) return false; elements[rear] = e; rear = (rear+1)%ARRAY_CAPACITY; return true; } //出队 bool deQueue() { if(isEmpty()) return false; front = (front+1)%ARRAY_CAPACITY; return true; } //获取队尾元素 bool getRear(int& e) const { if(isEmpty()) return false; e= elements[rear-1]; return true; } //获取队头元素 bool getFront(int& e)const { if(isEmpty()) return false; e= elements[front]; return true; } }; int main() { using namespace std; CycleQueue queue; printf("length is :%d\\n",queue.length()); printf("is empty? :%d\\n",queue.isEmpty()); puts("\\n----------put 10 elements to the queue-----------"); for(int i=0;i<10;++i){ queue.enQueue(i); } printf("length is :%d\\n",queue.length()); printf("is empty? :%d\\n",queue.isEmpty()); printf("--------pop 5 elements from the queue-------------\\n"); for(int i=0,e;i<5;++i){ queue.getFront(e); if(queue.deQueue()) printf("%d ",e); } printf("\\nlength is :%d\\n",queue.length()); printf("is empty? :%d\\n",queue.isEmpty()); puts("\\n----------put 2 elements to the queue-----------"); for(int i=0,e;i<2;++i){ queue.enQueue(i+100); } printf("\\nlength is :%d\\n",queue.length()); printf("is empty? :%d\\n",queue.isEmpty()); printf("--------pop all elements from the queue-------------\\n"); while(!queue.isEmpty() ){ int e; queue.getFront(e); if(queue.deQueue()) printf("%d ",e); } printf("\\nlength is :%d\\n",queue.length()); printf("is empty? :%d\\n",queue.isEmpty()); return 0; }
双端队列
以上是关于队列队列的分类和实现的主要内容,如果未能解决你的问题,请参考以下文章