队列
一.队列(queue)的基本概念
- 队列也是运算受限的线性表。是一种先进先出的线性表。
- 只允许在表的一端插入,在另外表的一端删除。
- 队首front:允许插入的一端。
- 队尾rear:允许删除的一端。
- 排队购物、操作系统中的作业排队。
二.队列的顺序表示和实现
- 顺序队列:利用一组连续的存储单元(一维数组)依次存放从队首到队尾的各个元素。
- 对于顺序队列,和顺序栈一样,有动态和静态之分。
1.静态顺序队列,其类型定义
*#define MAX_QUEUE_SIZE 100
*typedef struct queue{
* Elemtype Queue_array[MAX_QUEUE_SIZE];
* int front;
* int rear;
* }SqQueue;
*设立一个队首指针front,一个队尾指针rear,分别指向队首和队尾元素。
*初始化:front=rear=0.
- 入队:将新元素插入到rear指向的位置,然后在加1.
- 出队:删除front指向的元素,然后加1,并返回删除的元素
- 队列为空:front=rear。
- 队列为满:rear=MAX_QUEUE_SIZE-1或front=rear
重点:
*在非空队列里,队首指针始终指向队头元素,而队尾指针始终指向队尾元素的下一个位置。
*
顺序队列中存在“假溢出”现象。因为在入队和出队操作中,头尾指针只增加不减小,致使别删除的元素空间永远不法重新利用。因此,尽管队列中的元素个数可能远远小于数组大小,但可能由于尾指针已超出向量空间的上界而不能做入队操作。该现象称为假溢出。
//循环队列
- 为充分利用向量空间,克服上述“假溢出”现象方法是:将为队列分配的向量空间看成为一个首尾相接的圆环,并称这种队列为循环队列(Circular Queue)。
- 在循环队列中进行出栈、入栈操作时,队首、队尾指针扔要加1,超前移动。只不过当队首、队尾指针指向向量上界(MAX_QUEUE_SIZE-1)时,其加1操作的结果是指向向量下届0.
重点:真正实用的顺序队列是循环队列
//循环队列基本操作
//1.循环队列的初始化
SqQueue Init_CirQueue() {
SqQueue Q;
Q.front = Q.rear = 0;
return Q;
}
//2.入队操作
//分析:最后一个空间不能存储元素。Q.rear 指向空的,Q.rear + 1等于队列首则不能入队
bool Insert_CirQueue(SqQueue &Q, int e) { //加引用,不然数组的值不能传回
//将数据元素e插入到循环队列Q的队尾
if ((Q.rear + 1) % MAX_QUEUE_SIZE == Q.front) return false;//队列满
Q.Queue_array[Q.rear] = e; //元素插入
Q.rear = (Q.rear + 1) % MAX_QUEUE_SIZE; //队尾指针向前移动
return true;
}
//3.出队操作
//分析:取不到Q.rear 指向后面一位元素,Q.front + 1等于队列尾则不能出队
bool Delete_CirQueue(SqQueue &Q, int &e) { //加引用,不然结构体的值不能传回
//将循环队列Q的队首元素出队
if ((Q.front + 1) == Q.rear) return false;//队列空
e=Q.Queue_array[Q.front] ; //队首取元素
Q.front = (Q.front + 1) % MAX_QUEUE_SIZE; //队首指针向前移动
return true;
}
三.队列的链式表示和实现
//1.队列的链式存储表示
- 队列的链式存储结构简称为链队列,它是限制仅在表头进行删除操作和表尾插入操作的单链表。
- 需要两类不同的结点:数据元素结点,队列的队首指针和队尾指针的结点。
//q.数据元素结点类型定义:
//typedef struct QNode {
// Elemtype data;
// struct QNode* next;
//}QNode;
//b.指针结点类型定义:
//typedef struct link_queue {
// QNode *front,*rear;
//
//}link_queue;
//2.链队运算即指针变化
//链队的操作实际是单链表的操作,只不过是删除在表头进行,插入在表尾进行。
//3.链队列的基本操作
//1.链队列的初始化(键队列)
link_queue* Init_LinkQueue() {
link_queue* q;
QNode* p;
p = (QNode*)malloc(sizeof(QNode)); //开辟头结点
p->next = NULL;
q= (link_queue*)malloc(sizeof(link_queue)); //开辟队列的指针结点
q->front = q->rear = p;
return q;
}
//2.链队列的入队操作
bool Insert_CirQueue(link_queue* q,int e){
QNode* p;
p = (QNode*)malloc(sizeof(QNode)); //链队不用检查队列是否为满,插入
if (!p) return false; //申请新结点失败
p->data = e;
p->next = NULL; //
(q->rear)->next = p; //队列的尾结点内的(指针)指向 新的插入结点
q->rear = p; //队列的尾指针 指向新的插入结点
return true;
}
//3.链队列的出队操作
bool Delete_CirQueue(link_queue* q, int &e) {
QNode* p;
if (q->front ==q->rear) return false; //队空
p=(q->front)->next;
e = p->data;
(q->front)->next=p->next; //
//当队列有有一个结点时(头结点+结点),防止丢失尾指针
if (p == q->rear) q->rear = q->front;
free(p);
return true;
}
//4.链队列的撤销(摧毁队列)
void Destroy_LinkQueue(link_queue* q) {
while (q->front != NULL) {
q->rear = (q->front)->next; //令尾指针队列的第一个结点
free(q->front); //第一次释放头结点,后面是元素结点
q->front = q->rear;
}
}
代码
头文件
#pragma once
#include<iostream>
//1.静态顺序队列,其类型定义
#define MAX_QUEUE_SIZE 100
typedef struct queue{
int Queue_array[MAX_QUEUE_SIZE];
int front;
int rear;
}SqQueue;
//循环队列基本操作
//1.循环队列的初始化
SqQueue Init_CirQueue();
//2.入队操作
bool Insert_CirQueue(SqQueue &Q, int e);
//3.出队操作
bool Delete_CirQueue(SqQueue &Q, int& e);
//1.队列的链式存储表示
//a.数据元素结点类型定义:
typedef struct QNode {
int data;
struct QNode* next;
}QNode;
//b.指针结点类型定义:
typedef struct link_queue {
QNode *front,*rear;
}link_queue;
//3.链队列的基本操作
//1.链队列的初始化(键队列)
link_queue* Init_LinkQueue();
//2.链队列的入队操作
bool Insert_CirQueue(link_queue* q, int e);
//3.链队列的出队操作
bool Delete_CirQueue(link_queue* q, int & e);
//4.链队列的撤销(摧毁队列)
void Destroy_LinkQueue(link_queue* q);
cpp文件
#include "队列queue.h"
using namespace std;
//一.队列(queue)的基本概念
/**
* 队列也是运算受限的线性表。是一种先进先出的线性表。
* 只允许在表的一端插入,在另外表的一端删除。
* 队首front:允许插入的一端。
* 队尾rear:允许删除的一端。
* 排队购物、操作系统中的作业排队。
**/
//二.队列的顺序表示和实现
/**
* 顺序队列:利用一组连续的存储单元(一维数组)依次存放从队首到队尾的各个元素。
* 对于顺序队列,和顺序栈一样,有动态和静态之分。
***/
//1.静态顺序队列,其类型定义
/**
*#define MAX_QUEUE_SIZE 100
*typedef struct queue{
* Elemtype Queue_array[MAX_QUEUE_SIZE];
* int front;
* int rear;
* }SqQueue;
***/
/**
*设立一个队首指针front,一个队尾指针rear,分别指向队首和队尾元素。
*初始化:front=rear=0.
* 入队:将新元素插入到rear指向的位置,然后在加1.
* 出队:删除front指向的元素,然后加1,并返回删除的元素
* 队列为空:front=rear。
* 队列为满:rear=MAX_QUEUE_SIZE-1或front=rear
***/
/**
*在非空队列里,队首指针始终指向队头元素,而队尾指针始终指向队尾元素的下一个位置。
*
* 顺序队列中存在“假溢出”现象。因为在入队和出队操作中,头尾指针只增加不减小,致使
* 别删除的元素空间永远不法重新利用。因此,尽管队列中的元素个数可能远远小于数组大小,但
* 可能由于尾指针已超出向量空间的上界而不能做入队操作。该现象称为假溢出。
***/
//循环队列
/**
* 为充分利用向量空间,克服上述“假溢出”现象方法是:将为队列分配的向量空间看成为一个首尾
*相接的圆环,并称这种队列为循环队列(Circular Queue)。
* 在循环队列中进行出栈、入栈操作时,队首、队尾指针扔要加1,超前移动。只不过当队首、队尾
* 指针指向向量上界(MAX_QUEUE_SIZE-1)时,其加1操作的结果是指向向量下届0.
***/
//循环队列基本操作
//1.循环队列的初始化
SqQueue Init_CirQueue() {
SqQueue Q;
Q.front = Q.rear = 0;
return Q;
}
//2.入队操作
//分析:最后一个空间不能存储元素。Q.rear 指向空的,Q.rear + 1等于队列首则不能入队
bool Insert_CirQueue(SqQueue &Q, int e) { //加引用,不然数组的值不能传回
//将数据元素e插入到循环队列Q的队尾
if ((Q.rear + 1) % MAX_QUEUE_SIZE == Q.front) return false;//队列满
Q.Queue_array[Q.rear] = e; //元素插入
Q.rear = (Q.rear + 1) % MAX_QUEUE_SIZE; //队尾指针向前移动
return true;
}
//3.出队操作
//分析:取不到Q.rear 指向后面一位元素,Q.front + 1等于队列尾则不能出队
bool Delete_CirQueue(SqQueue &Q, int &e) { //加引用,不然结构体的值不能传回
//将循环队列Q的队首元素出队
if ((Q.front + 1) == Q.rear) return false;//队列空
e=Q.Queue_array[Q.front] ; //队首取元素
Q.front = (Q.front + 1) % MAX_QUEUE_SIZE; //队首指针向前移动
return true;
}
//三.队列的链式表示和实现
//1.队列的链式存储表示
/**
* 队列的链式存储结构简称为链队列,它是限制仅在表头进行删除操作和表尾插入操作的单链表。
*需要两类不同的结点:数据元素结点,队列的队首指针和队尾指针的结点。
***/
//q.数据元素结点类型定义:
//typedef struct QNode {
// Elemtype data;
// struct QNode* next;
//}QNode;
//b.指针结点类型定义:
//typedef struct link_queue {
// QNode *front,*rear;
//
//}link_queue;
//2.链队运算即指针变化
//链队的操作实际是单链表的操作,只不过是删除在表头进行,插入在表尾进行。
//3.链队列的基本操作
//1.链队列的初始化(键队列)
link_queue* Init_LinkQueue() {
link_queue* q;
QNode* p;
p = (QNode*)malloc(sizeof(QNode)); //开辟头结点
p->next = NULL;
q= (link_queue*)malloc(sizeof(link_queue)); //开辟队列的指针结点
q->front = q->rear = p;
return q;
}
//2.链队列的入队操作
bool Insert_CirQueue(link_queue* q,int e){
QNode* p;
p = (QNode*)malloc(sizeof(QNode)); //链队不用检查队列是否为满,插入
if (!p) return false; //申请新结点失败
p->data = e;
p->next = NULL; //
(q->rear)->next = p; //队列的尾结点内的(指针)指向 新的插入结点
q->rear = p; //队列的尾指针 指向新的插入结点
return true;
}
//3.链队列的出队操作
bool Delete_CirQueue(link_queue* q, int &e) {
QNode* p;
if (q->front ==q->rear) return false; //队空
p=(q->front)->next;
e = p->data;
(q->front)->next=p->next; //
//当队列有有一个结点时(头结点+结点),防止丢失尾指针
if (p == q->rear) q->rear = q->front;
free(p);
return true;
}
//4.链队列的撤销(摧毁队列)
void Destroy_LinkQueue(link_queue* q) {
while (q->front != NULL) {
q->rear = (q->front)->next; //令尾指针队列的第一个结点
free(q->front); //第一次释放头结点,后面是元素结点
q->front = q->rear;
}
}
int main() {
cout<<"-----队列------" << endl;
cout << "---队列的顺序表示和实现--" << endl;
//1.循环队列的初始化
int mm=0; //取队列的元素
SqQueue squ=Init_CirQueue();
//2.入队操作
cout << "---入队操作:1,2,3,4--" << endl;
bool aaa=Insert_CirQueue( squ, 1);
aaa = Insert_CirQueue(squ, 2);
aaa = Insert_CirQueue(squ, 3);
aaa = Insert_CirQueue(squ, 4);
//3.出队操作
cout << "---出队操作:" << endl;
aaa=Delete_CirQueue(squ,mm);
cout << mm << endl;
aaa = Delete_CirQueue(squ, mm);
cout << mm << endl;
aaa = Delete_CirQueue(squ, mm);
cout << mm << endl;
//输出1 2 3
cout << "---链队列表示和实现--" << endl;
//3.链队列的基本操作
//1.链队列的初始化(键队列)
int node_data;
link_queue* ch_que=Init_LinkQueue();
//2.链队列的入队操作
cout << "---入队操作:3,4,5--" << endl;
aaa= Insert_CirQueue(ch_que, 3);
aaa = Insert_CirQueue(ch_que, 4);
aaa = Insert_CirQueue(ch_que, 5);
//3.链队列的出队操作
cout << "---出队操作:" << endl;
aaa= Delete_CirQueue(ch_que, node_data);
cout << node_data << endl;
aaa = Delete_CirQueue(ch_que, node_data);
cout << node_data << endl;
aaa = Delete_CirQueue(ch_que, node_data);
cout << node_data << endl;
//4.链队列的撤销(摧毁队列)
int node_data2;
link_queue* ch_que2 = Init_LinkQueue();
//入队列
for (int i = 0; i < 9; i++) {
aaa = Insert_CirQueue(ch_que2, i);
}
出队列
//for (int i = 0; i < 9; i++) {
// aaa = Delete_CirQueue(ch_que2, node_data2);
// cout << node_data2 << " ";
//}
//cout << endl;
Destroy_LinkQueue(ch_que2);
出队列
for (int i = 0; i < 9; i++) {
aaa = Delete_CirQueue(ch_que2, node_data2);
cout << node_data2 << " ";
}
cout << endl;
//输出错值,销毁成功
return 0;
}