C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
一、介绍
栈和队列是限定插入和删除只能在表的“端点”进行的线性表,是线性表的子集,是插入和删除位置受限的线性表。
(操作受限的线性表)
二、栈
1)概念:
栈(stack)是一个特殊的线性表,是限仅在一端(通常是表尾)进行插入和删除操作的线性表。
表尾(即an端)称为栈顶Top;
表头(即a1端)称为栈底Base;
插入元素到栈顶(即表尾)的操作,称为入栈。
从栈顶(即表尾)删除最后一个元素的操作,称为出栈。
“入” = 压入 = PUSH(x)“出” = 弹出 = POP(y)
2)特点:先进后出
3)示意图:
4)常见问题:
数制转换 表达式求值
括号匹配的检验 八皇后问题
行编辑程序 函数调用
迷宫求解 递归调用的实现
5)栈的表示:
1、栈的抽象数据类型的类型定义:
2、相关操作:
6)顺序栈的表示和实现:
1、存储方式:
同一般线性表的顺序存储结构完全相同,利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。
- 附设top指针,指示栈顶元素在顺序栈中的位置。
- 另设base指针,指示栈底元素在顺序栈中的位置。
但是,为了方便操作,通常top 指示真正的栈顶元素之上的下标地址
- 另外,用stacksize表示栈可使用的最大容量。
2、栈空标志:
base == top 是栈空标志
3、栈满标志:
top - base == stacksize
4、栈满处理方法:
- 报错返回操作系统。
- 分配更大的操作空间,作为栈的存储空间,将原有的内容移入新栈。
使用数组作为顺序栈存储方式的特点:简单,方便、但易产生溢出(数组大小固定)
- **上溢(overflow):**栈已经满,又要压入元素
- **下溢(underflow):**栈已经空,还要弹出元素
注:上溢是一种错误,使问题的处理无法进行;而下溢一般认为是一种结束条件,即问题处理结束。
5、定义顺序栈结构
#include <iostream>
#include <cstdlib>using namespace std;#define OK 1 //成功标识
#define ERROR 0 //失败标识#define MAXSIZE 100 //存储空间的初始分配量typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等typedef int SElemType; //ElemType的类型根据实际情况而定,这里假定为inttypedef struct{SElemType *base; // 栈底指针SElemType *top; // 栈顶指针int stackSize; // 栈可用最大容量
}SqStack;
6、顺序栈的初始化
// 顺序栈的初始化
Status InitStack(SqStack* S) { // 构造一个空栈S->base = new SElemType[MAXSIZE]; // 或S.base = (SElemType*)malloc(MAXSIZE*sizeof(SElemType))if (!S->base) return ERROR; // 内存分配失败S->top = S->base; // 栈顶指针等于栈底指针S->stackSize = 0; return OK;
}
7、顺序栈的清空与销毁
// 清空栈
Status ClearStack(SqStack *S) {if (S->base) { // 如果存在这个栈S->top = S->base;S->stackSize = 0; // 将栈顶等于栈底就为空 }return OK;
}// 销毁栈
Status DestroyStack(SqStack* S) {if (S->base) {delete[] S->base;S->base = nullptr;S->top = nullptr;S->stackSize = 0;}return OK;
}
8、求顺序栈长度和判断是否为空
// 判断顺序栈是否为空
Status StackEmpty(SqStack S) {//若栈为空,返回TRUE;否则返回FALSEif (S.top == S.base) {return OK;}else {return ERROR;}
}// 求顺序栈长度
int StackLength(SqStack S) {return S.top - S.base;
}
9、顺序栈的入栈
// 入栈操作
Status Push(SqStack* S, SElemType e) {if (S->top - S->base >= MAXSIZE) return ERROR; // 栈满*S->top = e;S->top++;S->stackSize++;return OK;
}
10、顺序栈的出栈
// 出栈操作
Status Pop(SqStack* S, SElemType* e) {if (S->top == S->base) return ERROR; // 栈空S->top--;*e = *S->top;S->stackSize--;return OK;
}
11、获取栈顶元素
// 获取栈顶元素
Status GetTop(SqStack S, SElemType* e) {if (S.top == S.base) return ERROR; // 栈空*e = *(S.top - 1);return OK;
}
12、运行测试
// 打印栈内元素
void PrintStack(SqStack S) {if (S.top == S.base) {cout << "栈为空" << std::endl;return;}cout << "栈内元素: ";for (SElemType* p = S.base; p < S.top; ++p) {cout << *p << " ";}cout << endl;
}int main() {SqStack S;SElemType e;// 初始化栈if (InitStack(&S) == OK) {cout << "栈初始化成功" << endl;}else {cout << "栈初始化失败" << endl;return -1;}// 入栈操作if (Push(&S, 1) == OK && Push(&S, 2) == OK && Push(&S, 3) == OK) {cout << "入栈成功" << endl;PrintStack(S); // 打印栈内元素}else {cout << "入栈失败" << endl;}// 获取栈顶元素if (GetTop(S, &e) == OK) {cout << "栈顶元素: " << e << endl;}else {cout << "栈为空" << endl;}// 出栈操作while (Pop(&S, &e) == OK) {cout << "出栈元素: " << e << std;PrintStack(S); // 打印栈内元素}// 判断栈是否为空if (StackEmpty(S) == OK) {cout << "栈为空" << endl;}else {cout << "栈非空" << endl;}// 获取栈的长度cout << "栈的长度: " << StackLength(S) << endl;// 清空栈if (ClearStack(&S) == OK) {cout << "栈已清空" << endl;}else {cout << "清空栈失败" << endl;}// 销毁栈if (DestroyStack(&S) == OK) {cout << "栈已销毁" << endl;}else {cout << "销毁栈失败" << endl;}return 0;
}
13、总体代码
#include <iostream>
#include <cstdlib>using namespace std;#define OK 1 //成功标识
#define ERROR 0 //失败标识#define MAXSIZE 100 //存储空间的初始分配量typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等typedef int SElemType; //ElemType的类型根据实际情况而定,这里假定为inttypedef struct{SElemType *base; // 栈底指针SElemType *top; // 栈顶指针int stackSize; // 栈可用最大容量
}SqStack;// 顺序栈的初始化
Status InitStack(SqStack* S) { // 构造一个空栈S->base = new SElemType[MAXSIZE]; // 或S.base = (SElemType*)malloc(MAXSIZE*sizeof(SElemType))if (!S->base) return ERROR; // 内存分配失败S->top = S->base; // 栈顶指针等于栈底指针S->stackSize = 0; return OK;
}// 判断顺序栈是否为空
Status StackEmpty(SqStack S) {//若栈为空,返回TRUE;否则返回FALSEif (S.top == S.base) {return OK;}else {return ERROR;}
}// 求顺序栈长度
int StackLength(SqStack S) {return S.top - S.base;
}// 清空栈
Status ClearStack(SqStack *S) {if (S->base) { // 如果存在这个栈S->top = S->base;S->stackSize = 0; // 将栈顶等于栈底就为空 }return OK;
}// 销毁栈
Status DestroyStack(SqStack* S) {if (S->base) {delete[] S->base;S->base = nullptr;S->top = nullptr;S->stackSize = 0;}return OK;
}// 入栈操作
Status Push(SqStack* S, SElemType e) {if (S->top - S->base >= MAXSIZE) return ERROR; // 栈满*S->top = e;S->top++;S->stackSize++;return OK;
}// 出栈操作
Status Pop(SqStack* S, SElemType* e) {if (S->top == S->base) return ERROR; // 栈空S->top--;*e = *S->top;S->stackSize--;return OK;
}// 获取栈顶元素
Status GetTop(SqStack S, SElemType* e) {if (S.top == S.base) return ERROR; // 栈空*e = *(S.top - 1);return OK;
}// 打印栈内元素
void PrintStack(SqStack S) {if (S.top == S.base) {cout << "栈为空" << std::endl;return;}cout << "栈内元素: ";for (SElemType* p = S.base; p < S.top; ++p) {cout << *p << " ";}cout << endl;
}int main() {SqStack S;SElemType e;// 初始化栈if (InitStack(&S) == OK) {cout << "栈初始化成功" << endl;}else {cout << "栈初始化失败" << endl;return -1;}// 入栈操作if (Push(&S, 1) == OK && Push(&S, 2) == OK && Push(&S, 3) == OK) {cout << "入栈成功" << endl;PrintStack(S); // 打印栈内元素}else {cout << "入栈失败" << endl;}// 获取栈顶元素if (GetTop(S, &e) == OK) {cout << "栈顶元素: " << e << endl;}else {cout << "栈为空" << endl;}// 出栈操作while (Pop(&S, &e) == OK) {std::cout << "出栈元素: " << e << std::endl;PrintStack(S); // 打印栈内元素}// 判断栈是否为空if (StackEmpty(S) == OK) {cout << "栈为空" << endl;}else {cout << "栈非空" << endl;}// 获取栈的长度cout << "栈的长度: " << StackLength(S) << endl;// 清空栈if (ClearStack(&S) == OK) {cout << "栈已清空" << endl;}else {cout << "清空栈失败" << endl;}// 销毁栈if (DestroyStack(&S) == OK) {cout << "栈已销毁" << endl;}else {cout << "销毁栈失败" << endl;}return 0;
}
14、运行结果
7)链栈的表示和实现
1、存储方式
链栈是运算受限的单链表,只能正在链表头部进行操作
-
链表的头指针就是栈顶
-
不需要头结点
-
基本不存在栈满的情况
-
空栈相当于头指针指向空
-
插入和删除仅在栈顶处执行
2、定义链栈结构
#include <iostream>
using namespace std;// 函数结果状态码
#define OK 1 //成功标识
#define ERROR 0 //失败标识#define MAXSIZE 100 //线性表存储空间的初始分配量typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等typedef int SElemType; //ElemType的类型根据实际情况而定,这里假定为inttypedef struct StackNode {SElemType data;struct StackNode* next;
}StackNode, *LinkStack;
3、链栈的初始化
// 初始化链栈
Status InitStack(LinkStack& S) {S = NULL;return OK;
}
4、链栈的清空与销毁
// 销毁链栈
void DestroyStack(LinkStack& S) {LinkStack p;while (S != NULL) {p = S;S = S->next;delete p;}
}// 清空链栈
void ClearStack(LinkStack& S) {DestroyStack(S);InitStack(S);
}
5、求链栈长度和判断链栈是否为空
// 获取链栈的长度
int StackLength(LinkStack S) {int length = 0;LinkStack p = S;while (p != NULL) {length++;p = p->next;}return length;
}// 判断链栈是否为空
Status StackEmpty(LinkStack S) {if (S == NULL) {return OK;}else {return ERROR;}
}
6、链栈的入栈
// 入栈操作
Status Push(LinkStack& S, SElemType e) {LinkStack p;p = new StackNode; // 生成一个新节点if (p == NULL) return ERROR; // 内存分配失败p->data = e; // 将新节点数据域置为ep->next = S; // 将新节点插入栈顶S = p; // 修改栈顶指针return OK;
}
7、链栈的出栈
// 出栈操作
Status Pop(LinkStack& S, SElemType& e) {if (S == NULL) return ERROR; // 栈空LinkStack p = S;;e = p->data;S = p->next;delete p;return OK;
}
8、获取栈顶元素
// 获取栈顶元素
Status GetTop(LinkStack& S, SElemType& e) {if (S == NULL) return ERROR; // 栈空e = S->data;return OK;
}
9、运行测试
// 打印链栈内元素
void PrintStack(LinkStack& S) {if (S == NULL) {cout << "栈为空" << endl;return;}cout << "栈内元素: ";LinkStack p = S;while (p != NULL) {cout << p->data << " ";p = p->next;}cout << endl;
}int main() {LinkStack stack;InitStack(stack);int choice, value;bool running = true;while (running) {cout << "1. 入栈\n2. 出栈\n3. 获取栈顶元素\n4. 判断链栈是否为空\n5. 清空链栈\n6. 获取链栈长度\n7. 打印栈内元素\n8. 退出\n";cout << "请输入你的选择: ";cin >> choice;switch (choice) {case 1:cout << "请输入你要入栈的元素: ";cin >> value;if (Push(stack, value) == OK) {cout << "入栈成功" << endl;}else {cout << "入栈失败" << endl;}PrintStack(stack);break;case 2:if (Pop(stack, value) == OK) {cout << "出栈的元素为: " << value << endl;}else {cout << "链栈为空" << endl;}PrintStack(stack);break;case 3:if (GetTop(stack, value) == OK) {cout << "栈顶元素为: " << value << endl;}else {cout << "链栈为空" << endl;}break;case 4:if (StackEmpty(stack) == OK) {cout << "链栈为空" << endl;}else {cout << "链栈不为空" << endl;}break;case 5:ClearStack(stack);cout << "清空链栈" << endl;PrintStack(stack);break;case 6:cout << "链栈的长度为: " << StackLength(stack) << endl;break;case 7:PrintStack(stack);break;case 8:running = false;break;default:cout << "Invalid choice" << endl;break;}}DestroyStack(stack);return 0;
}
10、总体代码
#include <iostream>
using namespace std;// 函数结果状态码
#define OK 1 //成功标识
#define ERROR 0 //失败标识#define MAXSIZE 100 //线性表存储空间的初始分配量typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等typedef int SElemType; //ElemType的类型根据实际情况而定,这里假定为inttypedef struct StackNode {SElemType data;struct StackNode* next;
}StackNode, *LinkStack;// 初始化链栈
Status InitStack(LinkStack& S) {S = NULL;return OK;
}// 销毁链栈
void DestroyStack(LinkStack& S) {LinkStack p;while (S != NULL) {p = S;S = S->next;delete p;}
}// 入栈操作
Status Push(LinkStack& S, SElemType e) {LinkStack p;p = new StackNode; // 生成一个新节点if (p == NULL) return ERROR; // 内存分配失败p->data = e; // 将新节点数据域置为ep->next = S; // 将新节点插入栈顶S = p; // 修改栈顶指针return OK;
}// 出栈操作
Status Pop(LinkStack& S, SElemType& e) {if (S == NULL) return ERROR; // 栈空LinkStack p = S;;e = p->data;S = p->next;delete p;return OK;
}// 获取栈顶元素
Status GetTop(LinkStack& S, SElemType& e) {if (S == NULL) return ERROR; // 栈空e = S->data;return OK;
}// 判断链栈是否为空
Status StackEmpty(LinkStack S) {if (S == NULL) {return OK;}else {return ERROR;}
}// 清空链栈
void ClearStack(LinkStack& S) {DestroyStack(S);InitStack(S);
}// 获取链栈的长度
int StackLength(LinkStack S) {int length = 0;LinkStack p = S;while (p != NULL) {length++;p = p->next;}return length;
}// 打印链栈内元素
void PrintStack(LinkStack& S) {if (S == NULL) {cout << "栈为空" << endl;return;}cout << "栈内元素: ";LinkStack p = S;while (p != NULL) {cout << p->data << " ";p = p->next;}cout << endl;
}int main() {LinkStack stack;InitStack(stack);int choice, value;bool running = true;while (running) {cout << "1. 入栈\n2. 出栈\n3. 获取栈顶元素\n4. 判断链栈是否为空\n5. 清空链栈\n6. 获取链栈长度\n7. 打印栈内元素\n8. 退出\n";cout << "请输入你的选择: ";cin >> choice;switch (choice) {case 1:cout << "请输入你要入栈的元素: ";cin >> value;if (Push(stack, value) == OK) {cout << "入栈成功" << endl;}else {cout << "入栈失败" << endl;}PrintStack(stack);break;case 2:if (Pop(stack, value) == OK) {cout << "出栈的元素为: " << value << endl;}else {cout << "链栈为空" << endl;}PrintStack(stack);break;case 3:if (GetTop(stack, value) == OK) {cout << "栈顶元素为: " << value << endl;}else {cout << "链栈为空" << endl;}break;case 4:if (StackEmpty(stack) == OK) {cout << "链栈为空" << endl;}else {cout << "链栈不为空" << endl;}break;case 5:ClearStack(stack);cout << "清空链栈" << endl;PrintStack(stack);break;case 6:cout << "链栈的长度为: " << StackLength(stack) << endl;break;case 7:PrintStack(stack);break;case 8:running = false;break;default:cout << "Invalid choice" << endl;break;}}DestroyStack(stack);return 0;
}
11、运行结果
三、栈与递归
1)递归的定义
若一个对象部分地包含它自己,或用它自己给自己定义,则称这个对象是递归的;
若一个过程直接地或间接地调用自己,则称这个过程是递归的过程。
例如:递归求n的阶乘
long(Facty( long n ) {if(n == 0) return 1;else return n * Fact(n-1);
}
以下三种情况常常用到递归方法:
- 递归定义的数学函数
- 具有递归特性的数据结构
- 可递归求解的问题
2)递归问题——用分治法求解
分治法:对于一个较为复杂的问题,能够分解成几个相对简单的且解法相同或类似的子问题来求解
必备的三个条件:
- 能将一个问题转变成、个新问题,而新问题与原问题的解法相同或类同,不同的仅是处理的对象,且这些处理对象是变化有规律的
- 可以通过上述转化而使问题简化
- 必须有一个明确的递归出口,重或称递归的边界
3)分治法求递归问题算法的一般形式
void p (参数表) {if (递归结束条件) 可直接求解步骤; ——基本项else p (较小的参数); ——归纳项
}
例如:
long Fact ( long n){if(n == 0) return 1; // 基本项else return n * Fact(n - 1); // 归纳项
}
4)函数调用过程
调用前,系统完成:
(1)将实参,返回地址等传递给被调用函数
(2)为被调用函数的局部变量分配存储区(3)将控制转移到被调用函数的入口
调用后,系统完成:
(1)保存被调用函数的计算结果
(2)释放被调用函数的数据区(3)依照被调用函数保存的返回地址将控制转移到调用函数
当多个函数构成嵌套调用
遵循后调用先返回
例如:
int main(void){...y= fact(3);...
}
double fact(int n) {...Z = mypow(3.5, 2);...
}
double mypow(double x, in n) {...
}
5)递归的优缺点
优点:结构清晰,程序易读。
缺点:每次调用要生成工作记录,保存状态信息,入栈;返回时要出栈恢复状态信息。时间开销大。
6)递归 -> 非递归
方法1:尾递归、单向递归→循环结构
方法2:自用栈模拟系统的运行时栈
- 递归程序在执行时需要系统提供栈来实现
- 仿照递归算法执行过程中递归工作栈的状态变化可写出相应的非递归程序
- 改写后的非递归算法与原来的递归算法相比,结构不够清晰,可读性较差有的还需要经过一系列优化
四、队列
1)概念:
队列(queue)是一种先进先出(Frist In Frist Out ----FIFO)的线性表。在表一端插入(表尾),在另一端(表头)删除。
2)特点:先进先出
3)示意图:
4)常见问题:
脱机打印输出:按申请的先后顺序依次输出。
多用户系统中,多个用户排成队,分时地循环使用CPU和主存。
按用户的优先级排成多个队,每个优先级一个队列。
实时控制系统中,信号按接收的先后顺序依次处理。
网络电文传输,按到达的时间先后顺序依次进行。
5)队列的表示:
- 队列(Queue)是仅在 表尾 进行插入操作,在 表头 进行删除操作的线性表。
- 表尾即an端,称为队尾;表头即a1端,称为队头。
- 它是一种先进先出( FIFO )的线性表。
例如:队列 Q = (a1, a2 a3 .. an-1. an)
插入元素称为入队删除元素称为出队
队列的存储结构为链队或顺序队(常用循环顺序队)
6)循环顺序队的表示与实现
1、真溢出与假溢出
真溢出是数组没有空位再入新的元素了。
假溢出是数组中不按原来顺序依次放入,以按其他顺序还有空位可以放入新的元素。
解决假上溢的方法:
2、解决假上溢方法——引入循环队列
base[0] 接在base[MAXQSIZE - 1] 之后,若rear + 1 == M,则令rear = 0
实现方法:利用模(mod,C语言中:%)运算。
因为如上图所示:
- 当在 0 位置时,(0 + 1)% 6 = 1
- 当在 1 位置时,(1 + 1)% 6 = 2
- …
- 当在 5 位置时,数组满时,(5 + 1)% 6 = 0 回到 0 位置
所以:
插入元素:Q.base[Q.rear] = x;Q.rear = (Q.rear + 1) % MAXQSIZE // 达到最大值等于0,回到原点删除元素:x = Q.base[s.front];Q.front = (Q.front + 1) % MAXQSIZE
3、解决队空队满如何判定的方法
4、定义循环顺序表队列结构
#include <iostream>
#include <stdexcept>using namespace std;
#define MAXSIZE 100 // 队列的最大容量
#define OK 1 //成功标识
#define ERROR 0 //失败标识typedef int QElemType; // 队列元素的类型,这里假定为int
typedef int Status;typedef struct {QElemType data[MAXSIZE]; // 存储队列元素的数组// QElemType *base; // 动态分配存储空间int front; // 头指针int rear; // 尾指针
} SqQueue;
5、循环顺序表队列的初始化
// 初始化队列
Status InitQueue(SqQueue& Q) {// Q.base = new QElemType[MAXQSIZE] // 分配数组空间// Q.base = (QElemType*) malloc (MAXQSIZE *sizeof(QElemType)); // 动态分配空间// if (!Q.base) exit(OVERFLOW); // 存储分配失败Q.front = 0; // 头指针Q.rear = 0; // 尾指针return OK;
}
6、循环顺序表队列的清空与销毁
// 销毁队列
Status DestroyQueue(SqQueue& Q) {// 顺序表实现的队列不需要显式释放内存Q.front = 0;Q.rear = 0;return OK;
}// 清空队列
Status ClearQueue(SqQueue& Q) {Q.front = 0;Q.rear = 0;return OK;
}
7、求循环顺序表队列长度和判断循环顺序表队列是否为空
// 获取队列的长度
int QueueLength(SqQueue Q) {return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}// 判断队列是否为空
bool QueueEmpty(SqQueue Q) {return Q.front == Q.rear;
}
8、循环顺序表队列的入队
// 入队操作
Status EnQueue(SqQueue& Q, QElemType e) {if ((Q.rear + 1) % MAXSIZE == Q.front) { // 队列满return ERROR;}Q.data[Q.rear] = e;Q.rear = (Q.rear + 1) % MAXSIZE; // 循环队列 队尾 + 1return OK;
}
9、循环顺序表队列的出队
// 出队操作
Status DeQueue(SqQueue& Q, QElemType& e) {if (Q.front == Q.rear) { // 队列空return ERROR;}e = Q.data[Q.front];Q.front = (Q.front + 1) % MAXSIZE; // 循环队列 队头 + 1return OK;
}
10、获取循环顺序表队列顶元素
// 获取队列头部元素
Status GetHead(SqQueue Q, QElemType& e) {if (Q.front == Q.rear) { // 队列空return ERROR;}e = Q.data[Q.front];return OK;
}
11、运行测试
// 打印队列内元素
void PrintQueue(SqQueue Q) {if (Q.front == Q.rear) {cout << "队列为空" << endl;return;}cout << "队列为: ";int i = Q.front;while (i != Q.rear) {cout << Q.data[i] << " ";i = (i + 1) % MAXSIZE;}cout << endl;
}int main() {SqQueue queue;InitQueue(queue);int choice, value;bool running = true;while (running) {cout << "1. 入队\n2. 出队\n3. 获得队头元素\n4. 判断队列是否为空\n5. 清空队列\n6. 获得队列长度\n7. 打印队列\n8. 退出\n";cout << "请输入你的选择: ";cin >> choice;switch (choice) {case 1:cout << "请输入要入队的元素: ";cin >> value;if (EnQueue(queue, value) == OK) {cout << "入队成功" << endl;}else {cout << "队列已满" << endl;}PrintQueue(queue);break;case 2:if (DeQueue(queue, value) == OK) {cout << "出队的元素: " << value << endl;}else {cout << "队列为空" << endl;}PrintQueue(queue);break;case 3:if (GetHead(queue, value) == OK) {cout << "队头元素为: " << value << endl;}else {cout << "队列为空" << endl;}break;case 4:if (QueueEmpty(queue)) {cout << "队列为空" << endl;}else {cout << "队列不为空" << endl;}break;case 5:ClearQueue(queue);cout << "队列已清空" << endl;PrintQueue(queue);break;case 6:cout << "队列长度: " << QueueLength(queue) << endl;break;case 7:PrintQueue(queue);break;case 8:running = false;break;default:cout << "Invalid choice" << endl;break;}}DestroyQueue(queue);return 0;
}
12、总体代码
#include <iostream>
#include <stdexcept>using namespace std;
#define MAXSIZE 100 // 队列的最大容量
#define OK 1 //成功标识
#define ERROR 0 //失败标识typedef int QElemType; // 队列元素的类型,这里假定为int
typedef int Status;typedef struct {QElemType data[MAXSIZE]; // 存储队列元素的数组// QElemType *base; // 动态分配存储空间int front; // 头指针int rear; // 尾指针
} SqQueue;// 初始化队列
Status InitQueue(SqQueue& Q) {// Q.base = new QElemType[MAXQSIZE] // 分配数组空间// Q.base = (QElemType*) malloc (MAXQSIZE *sizeof(QElemType)); // 动态分配空间// if (!Q.base) exit(OVERFLOW); // 存储分配失败Q.front = 0; // 头指针Q.rear = 0; // 尾指针return OK;
}// 销毁队列
Status DestroyQueue(SqQueue& Q) {// 顺序表实现的队列不需要显式释放内存Q.front = 0;Q.rear = 0;return OK;
}// 入队操作
Status EnQueue(SqQueue& Q, QElemType e) {if ((Q.rear + 1) % MAXSIZE == Q.front) { // 队列满return ERROR;}Q.data[Q.rear] = e;Q.rear = (Q.rear + 1) % MAXSIZE; // 循环队列 队尾 + 1return OK;
}// 出队操作
Status DeQueue(SqQueue& Q, QElemType& e) {if (Q.front == Q.rear) { // 队列空return ERROR;}e = Q.data[Q.front];Q.front = (Q.front + 1) % MAXSIZE; // 循环队列 队头 + 1return OK;
}// 获取队列头部元素
Status GetHead(SqQueue Q, QElemType& e) {if (Q.front == Q.rear) { // 队列空return ERROR;}e = Q.data[Q.front];return OK;
}// 判断队列是否为空
bool QueueEmpty(SqQueue Q) {return Q.front == Q.rear;
}// 清空队列
Status ClearQueue(SqQueue& Q) {Q.front = 0;Q.rear = 0;return OK;
}// 获取队列的长度
int QueueLength(SqQueue Q) {return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}// 打印队列内元素
void PrintQueue(SqQueue Q) {if (Q.front == Q.rear) {cout << "队列为空" << endl;return;}cout << "队列为: ";int i = Q.front;while (i != Q.rear) {cout << Q.data[i] << " ";i = (i + 1) % MAXSIZE;}cout << endl;
}int main() {SqQueue queue;InitQueue(queue);int choice, value;bool running = true;while (running) {cout << "1. 入队\n2. 出队\n3. 获得队头元素\n4. 判断队列是否为空\n5. 清空队列\n6. 获得队列长度\n7. 打印队列\n8. 退出\n";cout << "请输入你的选择: ";cin >> choice;switch (choice) {case 1:cout << "请输入要入队的元素: ";cin >> value;if (EnQueue(queue, value) == OK) {cout << "入队成功" << endl;}else {cout << "队列已满" << endl;}PrintQueue(queue);break;case 2:if (DeQueue(queue, value) == OK) {cout << "出队的元素: " << value << endl;}else {cout << "队列为空" << endl;}PrintQueue(queue);break;case 3:if (GetHead(queue, value) == OK) {cout << "队头元素为: " << value << endl;}else {cout << "队列为空" << endl;}break;case 4:if (QueueEmpty(queue)) {cout << "队列为空" << endl;}else {cout << "队列不为空" << endl;}break;case 5:ClearQueue(queue);cout << "队列已清空" << endl;PrintQueue(queue);break;case 6:cout << "队列长度: " << QueueLength(queue) << endl;break;case 7:PrintQueue(queue);break;case 8:running = false;break;default:cout << "Invalid choice" << endl;break;}}DestroyQueue(queue);return 0;
}
13、运行结果
7)链队的表示与实现
1、定义链队结构
#include <iostream>
#include <stdexcept>using namespace std;#define OK 1 //成功标识
#define ERROR 0 //失败标识typedef int QElemType; // 队列元素的类型,这里假定为int
typedef int Status;typedef struct QueueNode {QElemType data;struct QueueNode* next;
} QueueNode, * QueuePtr;typedef struct {QueuePtr front; // 头指针QueuePtr rear; // 尾指针
} LinkQueue;
2、链队的初始化
// 初始化队列
Status InitQueue(LinkQueue& Q) {// Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode)) // 动态分配内存空间Q.front = Q.rear = new QueueNode; // 创建头结点if (Q.front == NULL) return ERROR; // 内存分配失败Q.front->next = NULL;return OK;
}
3、链队的清空与销毁
// 销毁队列
Status DestroyQueue(LinkQueue& Q) {while (Q.front != NULL) {QueuePtr p = Q.front;Q.front = Q.front->next;delete p;}return OK;
}// 清空队列
Status ClearQueue(LinkQueue& Q) {DestroyQueue(Q);return InitQueue(Q);
}
4、求链队长度和判断链队是否为空
// 获取队列的长度
int QueueLength(LinkQueue Q) {int length = 0;QueuePtr p = Q.front->next;while (p != NULL) {length++;p = p->next;}return length;
}// 判断队列是否为空
bool QueueEmpty(LinkQueue Q) {return Q.front == Q.rear;
}
5、链队的入队
// 入队操作
Status EnQueue(LinkQueue& Q, QElemType e) {QueuePtr s = new QueueNode; // 创建新节点if (s == NULL) return ERROR; // 内存分配失败s->data = e;s->next = NULL;Q.rear->next = s;Q.rear = s;return OK;
}
6、链队的出队
// 出队操作
Status DeQueue(LinkQueue& Q, QElemType& e) {if (Q.front == Q.rear) return ERROR; // 队列空QueuePtr p = Q.front->next;e = p->data;Q.front->next = p->next;if (p->next == NULL) Q.rear = Q.front; // 如果队列变为空,更新尾指针delete p;return OK;
}
7、获取队顶元素
// 获取队列头部元素
Status GetHead(LinkQueue Q, QElemType& e) {if (Q.front == Q.rear) return ERROR; // 队列空e = Q.front->next->data;return OK;
}
8、运行测试
// 打印队列内元素
void PrintQueue(LinkQueue Q) {if (Q.front == Q.rear) {cout << "队列为空" << endl;return;}cout << "队列为: ";QueuePtr p = Q.front->next;while (p != NULL) {cout << p->data << " ";p = p->next;}cout << endl;
}int main() {LinkQueue queue;InitQueue(queue);int choice, value;bool running = true;while (running) {cout << "1.入队\n2.出队\n3.获得队头元素\n4.判断队列是否为空\n5.清空队列\n6.获得队列长度\n7.打印队列\n8.退出\n";cout << "请输入你的选择: ";cin >> choice;switch (choice) {case 1:cout << "请输入要入队的元素: ";cin >> value;if (EnQueue(queue, value) == OK) {cout << "入队成功" << endl;}else {cout << "入队失败" << endl;}PrintQueue(queue);break;case 2:if (DeQueue(queue, value) == OK) {cout << "出队的元素: " << value << endl;}else {cout << "队列为空" << endl;}PrintQueue(queue);break;case 3:if (GetHead(queue, value) == OK) {cout << "队头元素为: " << value << endl;}else {cout << "队列为空" << endl;}break;case 4:if (QueueEmpty(queue)) {cout << "队列为空" << endl;}else {cout << "队列不为空" << endl;}break;case 5:ClearQueue(queue);cout << "队列已清空" << endl;PrintQueue(queue);break;case 6:cout << "队列长度为: " << QueueLength(queue) << endl;break;case 7:PrintQueue(queue);break;case 8:running = false;break;default:cout << "Invalid choice" << endl;break;}}DestroyQueue(queue);return 0;
}
9、总体代码
#include <iostream>
#include <stdexcept>using namespace std;#define OK 1 //成功标识
#define ERROR 0 //失败标识typedef int QElemType; // 队列元素的类型,这里假定为int
typedef int Status;typedef struct QueueNode {QElemType data;struct QueueNode* next;
} QueueNode, * QueuePtr;typedef struct {QueuePtr front; // 头指针QueuePtr rear; // 尾指针
} LinkQueue;// 初始化队列
Status InitQueue(LinkQueue& Q) {// Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode)) // 动态分配内存空间Q.front = Q.rear = new QueueNode; // 创建头结点if (Q.front == NULL) return ERROR; // 内存分配失败Q.front->next = NULL;return OK;
}// 销毁队列
Status DestroyQueue(LinkQueue& Q) {while (Q.front != NULL) {QueuePtr p = Q.front;Q.front = Q.front->next;delete p;}return OK;
}// 入队操作
Status EnQueue(LinkQueue& Q, QElemType e) {QueuePtr s = new QueueNode; // 创建新节点if (s == NULL) return ERROR; // 内存分配失败s->data = e;s->next = NULL;Q.rear->next = s;Q.rear = s;return OK;
}// 出队操作
Status DeQueue(LinkQueue& Q, QElemType& e) {if (Q.front == Q.rear) return ERROR; // 队列空QueuePtr p = Q.front->next;e = p->data;Q.front->next = p->next;if (p->next == NULL) Q.rear = Q.front; // 如果队列变为空,更新尾指针delete p;return OK;
}// 获取队列头部元素
Status GetHead(LinkQueue Q, QElemType& e) {if (Q.front == Q.rear) return ERROR; // 队列空e = Q.front->next->data;return OK;
}// 判断队列是否为空
bool QueueEmpty(LinkQueue Q) {return Q.front == Q.rear;
}// 清空队列
Status ClearQueue(LinkQueue& Q) {DestroyQueue(Q);return InitQueue(Q);
}// 获取队列的长度
int QueueLength(LinkQueue Q) {int length = 0;QueuePtr p = Q.front->next;while (p != NULL) {length++;p = p->next;}return length;
}// 打印队列内元素
void PrintQueue(LinkQueue Q) {if (Q.front == Q.rear) {cout << "队列为空" << endl;return;}cout << "队列为: ";QueuePtr p = Q.front->next;while (p != NULL) {cout << p->data << " ";p = p->next;}cout << endl;
}int main() {LinkQueue queue;InitQueue(queue);int choice, value;bool running = true;while (running) {cout << "1.入队\n2.出队\n3.获得队头元素\n4.判断队列是否为空\n5.清空队列\n6.获得队列长度\n7.打印队列\n8.退出\n";cout << "请输入你的选择: ";cin >> choice;switch (choice) {case 1:cout << "请输入要入队的元素: ";cin >> value;if (EnQueue(queue, value) == OK) {cout << "入队成功" << endl;}else {cout << "入队失败" << endl;}PrintQueue(queue);break;case 2:if (DeQueue(queue, value) == OK) {cout << "出队的元素: " << value << endl;}else {cout << "队列为空" << endl;}PrintQueue(queue);break;case 3:if (GetHead(queue, value) == OK) {cout << "队头元素为: " << value << endl;}else {cout << "队列为空" << endl;}break;case 4:if (QueueEmpty(queue)) {cout << "队列为空" << endl;}else {cout << "队列不为空" << endl;}break;case 5:ClearQueue(queue);cout << "队列已清空" << endl;PrintQueue(queue);break;case 6:cout << "队列长度为: " << QueueLength(queue) << endl;break;case 7:PrintQueue(queue);break;case 8:running = false;break;default:cout << "Invalid choice" << endl;break;}}DestroyQueue(queue);return 0;
}
10、运行结果
相关文章:

C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
一、介绍 栈和队列是限定插入和删除只能在表的“端点”进行的线性表,是线性表的子集,是插入和删除位置受限的线性表。 (操作受限的线性表) 二、栈 1)概念: 栈(stack)是一个特殊的线性表,是限…...

读书笔记~管理修炼-缄默效应
缄默效应:学会正确批评下属 员工明明犯了错误,却不及时告知你,总是拖到最后一刻无法弥补时才不得不承认出了问题——你遇到过这样的问题吗? 这其实是缄默效应在发挥作用。 在职场中,即使再扁平化的环境&…...

视频会议系统会前预约模块必须包含哪些功能?
视频会议系统会前预约模块必须包含哪些功能? 视频会议系统的会前预约模块是企业高效管理会议资源、提升会议效率的重要工具。一个完善的会前预约模块必须包含一系列功能,以确保会议的顺利进行和资源的合理分配。以下是对视频会议系统会前预约模块必须包…...

RabbitMQ中的Topic模式
在现代分布式系统中,消息队列(Message Queue)是实现异步通信、解耦系统组件的重要工具。RabbitMQ 是一个广泛使用的开源消息代理,支持多种消息传递模式,其中 Topic 模式 是一种灵活且强大的模式,允许生产者…...

tslib(触摸屏输入设备的轻量级库)的学习、编译及测试记录
目录 tslib的简介tslib的源码和make及make install后得到的文件下载tslib的主要功能tslib的工作原理tslib的核心组成部分tslib的框架和核心函数分析tslib的框架tslib的核心函数ts_setup()的分析(对如何获取设备名和数据处理流程的分析)函数ts_setup()自身的主要代码ts_setup()对…...

Ubuntu vi(vim)编辑器配置一键补全main函数
1.打开对应的配置文件 vi ~/.vim/snippets/c.snippets 2.按G将光标定位到文件末尾 3.按i进入插入模式 以tab键开头插入下的内容,空行也要加 tab键 4.:wq保存退出 5.再打开任意一个新的 .c文件后,插入模式输入 main 然后按tal键就能补全了...

验证码机制
偶然间看到了验证码机制,顺便总结一下: 首先,验证码是从后端生成的,随机生成; 【后端永远认为前端有可能会被伪造】 1.后端调用相关的绘图第三方类库,或是(平台PHP、.NET、java)系…...

【CVE-2024-56145】PHP 漏洞导致 Craft CMS 出现 RCE
大多数开发人员都同意,与 15 年前相比,PHP 是一种更加理智、更加安全和可靠的语言。PHP5早期的不良设计已让位于更好的开发生态系统,其中包括类、自动加载、更严格的类型、更理智的语法以及一大堆其他改进。安全性也没有被忽视。 register_globals一些老读者可能还记得和的…...

使用FakeSMTP创建本地SMTP服务器接收邮件具体实现。
以下代码来自Let’s Go further节选。具体说明均为作者本人理解。 编辑邮件模版 主要包含三个template: subject:主题plainBody: 纯文本正文htmlBody:超文本语言正文 {{define "subject"}}Welcome to Greenlight!{{end}} {{def…...

【网络安全】逆向工程 练习示例
1. 逆向工程简介 逆向工程 (RE) 是将某物分解以了解其功能的过程。在网络安全中,逆向工程用于分析应用程序(二进制文件)的运行方式。这可用于确定应用程序是否是恶意的或是否存在任何安全漏洞。 例如,网络安全分析师对攻击者分发…...

Oracle Database 21c Express Edition数据库 和 Sqlplus客户端安装配置
目录 一. 前置条件二. Win10安装配置Oracle数据库2.1 数据库获取2.2 数据库安装2.3 数据库配置确认2.4 数据库访问 三. Win10配置Oracle数据库可对外访问3.1 打开文件和打印机共享3.2 开放1521端口 四. 端口与地址确认4.1 查看监听器的状态4.2 Win10查看1521端口是否被监听4.3 …...

arcgisPro将面要素转成CAD多段线
1、说明:正常使用【导出为CAD】工具,则导出的是CAD三维多线段,无法进行编辑操作、读取面积等。这是因为要素面中包含Z值,导出则为三维多线段数据。需要利用【复制要素】工具禁用M值和Z值,再导出为CAD,则得到…...

相机内外参知识
已知相机的内外参数矩阵,可以求得相机在世界坐标系下的原点坐标。这里需要理解几个概念: 内参数矩阵(Intrinsic Matrix): 描述相机本身的属性,比如焦距、主点位置等。外参数矩阵(Extrinsic Matrix…...

从代币角度介绍solana账户体系
1、solana 的账户概念介绍 Solana的账户体系是其区块链的核心组成部分,它允许数据和价值在链上存储和转移。以下是Solana账户体系的一些关键特点: • 账户模型: • 在Solana上,所有数据都存储在所谓的“账户”中,类似…...

前端引入字体文件
1. 字体下载 阿里矢量图图标库地址 https://www.iconfont.cn/,页面打开后选中,素材库 > 字体库 左侧两个标签页可以切换,右侧放大镜图标可以搜索自己需要的字体 字体预览区域可以自行调整进行字体预览 右上角点击字体包下载,下…...

qemu启动后网络怎么设置?配合qemu-system-riscv64的命令设置
QEMU启动的时候,可以选择组网方式,一般有两种选择,user模式和tap模式 user模式就是用NAT,tap模式就是用bridge网桥模式。以前也有过一次实践:FreeBSD RISCV 在QEME中实践-网络配置_pkg.txz: not found-CSDN博客 user…...

如何测量分辨率
一、什么是分辨率? 分辨率指的是分清物体细节的能力。分辨率是一个成像系统还原空间频率的能力。一些人只是简单的用分辨率去描述极限分辨率,但是相机在在不同的对比度的情况下还原低,中和高频率的能力,也可以显示全面综合的信息。…...

汇总贴:cocos creator
1 cocoscreator-doc-TS:目录-CSDN博客 访问节点和组件 常用节点和组件接口 创建和销毁节点 加载和切换场景 获取和设置资源 监听和发射事件 节点系统事件 缓动系统(cc.tween) 使用计时器 使用对象池 使用 TypeScript 脚本 模块化脚本 脚本执行顺序 全局…...

[N1CTF 2018]eating_cms
[N1CTF 2018]eating_cms 知识点 文件上传 解题 这个题感觉还好,知识点真心不难,就是全混在一起。 思路差不多挺离谱 首先看到,有一个登录界面,然后猜测有注册界面 admin注册不了,随便注册一个账号。 注册之后&…...

重拾设计模式--建造者模式
文章目录 建造者模式(Builder Pattern)概述建造者模式UML图作用:建造者模式的结构产品(Product):抽象建造者(Builder):具体建造者(Concrete Builderÿ…...

【机器学习】以机器学习为翼,翱翔网络安全创新苍穹
我的个人主页 我的领域:人工智能篇,希望能帮助到大家!!!👍点赞 收藏❤ 在数字化浪潮汹涌澎湃的当下,网络安全如同守护数字世界的坚固堡垒,其重要性不言而喻。而机器学习技术的蓬勃…...

人工智能在VR展览中扮演什么角色?
人工智能(AI)在VR展览中扮演着多重关键角色,这些角色不仅增强了用户体验,还为展览的组织者提供了强大的工具。 接下来,由专业从事VR展览制作的圆桌3D云展厅平台为大家介绍AI在VR展览中的一些主要作用: 个性…...

mysql,创建数据库和用户授权核心语句
一.库操作1.创建库create database if not exists 库名 default 字符集 default 校对规则2.删除库drop database if exists 库名3.修改库的,字符集,校对规则alter databse 库名 default 字符集 default 校对规则4.查看当前使用的库seclect databse();5.查看库show databases;…...

日期区间选择器插件的操作流程
我们知道,在开发过程中,为了能够在规定时间内完成项目,有时候我们都会使用插件来大大提高我们的开发效率,有些插件是可以直接拿来用,但是有些插件拿过来之后是需要进行修改,在使用插件的时候还有很多的注意…...

【WRF教程第3.2期】预处理系统 WPS详解:以4.5版本为例
预处理系统 WPS 详解:以4.5版本为例 WPS 嵌套域(WPS Nested Domains)USGS 和 MODIS 土地利用重力波拖拽方案静态数据(Gravity Wave Drag Scheme Static Data)1. 什么是重力波拖拽方案(GWDO)静态…...

深度学习的DataLoader是什么数据类型,为什么不可用来索引
在 Python 中,DataLoader是torch.utils.data.DataLoader类的实例对象,用于加载数据,它本身不是一种基本数据类型,而是一种特殊的迭代器类型,主要用于按批次加载数据,以下是其通常不可索引的原因:…...

物理信息神经网络(PINN)八课时教案
物理信息神经网络(PINN)八课时教案 第一课:物理信息神经网络概述 1.1 PINN的定义与背景 物理信息神经网络(Physics-Informed Neural Networks,简称PINN)是一种将物理定律融入神经网络训练过程中的先进方…...

Linux setfacl 命令详解
文章目录 Linux setfacl 命令详解一、ACL 和 setfacl 简介二、基本语法三、常用操作1. 查看 ACL2. 为用户设置权限3. 为组设置权限4. 删除 ACL 条目5. 设置默认 ACL6. 递归设置 ACL 四、示例操作1. 创建示例目录和文件2. 设置 ACL3. 验证 ACL 五、注意事项六、总结 Linux setfa…...

电商环境下的财务ERP系统架构
先介绍一下自己的工作经历,2002年开始进入ERP实施行业,专注于O记EBS系统,正好赶上中国经济和信息化高度发展的阶段,先后实施过很多大国企和民企的大型ERP项目,在实施过程中逐渐对ERP系统的架构、模块设计有更深入的认识…...

Linux相关概念和易错知识点(25)(信号原理、操作系统的原理、volatile)
目录 1.信号的产生 (1)kill (2)raise、abort 2.对block、pending、handler表的管理 (1)信号集(sigset_t) (2)block表的管理 ①操作相关的函数 ②sigpr…...