系列文章目录
其他章节相关文章
本章节其他相关文章
文章目录
前言
本文为王道数据结构的第三章——栈和队列的编程题。
运行软件:vscode
使用c++文件编写
一、栈的顺序存储
一、顺序栈
1、头文件并使用宏定义给出存储最大结点数
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 8
2、栈的结构体定义
typedef struct{
int data[MaxSize]; // 静态数组存放栈中元素
int top; // 栈顶指针,指向栈顶的数组下标
}SqStack;
3、初始化
//初始化栈, 指向栈顶元素
void InitStack(SqStack &S){
S.top = -1;
}
4、新元素入栈
bool Push(SqStack &S, int x){
if(S.top == MaxSize-1) // 栈满、报错
return false;
S.top += 1; // 指针先+1
S.data[S.top] = x; // 新元素入栈
// S.data[++S.top] = x; //top先自加1然后找位置
return true;
}
5、栈顶元素出栈
bool Pop(SqStack &S, int &x){
if(S.top == -1)
return false;
x = S.data[S.top--]; // 先使用top,然后top自减1
return true;
}
6、读取栈顶元素(不将栈顶元素弹出)
bool GetTop(SqStack S, int &x){
if(S.top == -1)
return false;
x = S.data[S.top];
return true;
}
7、判断栈是否为空
//判空
bool SqStackEmpty(SqStack S){
if(S.top == -1)
return true;
return false;
}
8、建立并测试栈
void testStack(){
SqStack S; // 声明一个顺序栈(分配空间)
InitStack(S);
int x;
scanf("%d", &x);
while(x!=9999){
if(Push(S, x)){
printf("已成功入栈请继续输入:");
scanf("%d", &x);
}
else{
printf("栈已满\n");
break;
}
}
GetTop(S, x);
printf("%d\n",x);
while(S.top != -1){
Pop(S, x);
printf("%d ", x);
}
}
二、共享栈
共享栈结构为:两个栈共用一片数组空间,一个栈自底向上,另一个栈自顶向下。
1、共享栈的结构体定义
typedef struct{ // 栈顶指针指向栈顶元素
int data[MaxSize];
int top0;
int top1;
}ShStrack;
2、初始化
//初始化栈, 指向栈顶元素
void InitShStrack(ShStrack S){
S.top0 = -1; // 第一个栈自底向上
S.top1 = MaxSize; // 第二个栈自顶向下
}
3、向第一个栈添加元素
// 向第一个栈添加元素
bool PushShStrack_0(ShStrack &S, int x){
if(S.top0+1 == S.top1) //判断栈是否已满
return false;
S.data[++S.top0] = x;
return true;
}
4、向第一个栈删除并返回栈顶元素
// 向第一个栈删除并返回栈顶元素
bool PopShStrack_0(ShStrack &S, int &x){
if(S.top0 == -1)
return false;
x = S.data[S.top0--];
return true;
}
5、向第一个栈取出栈顶元素
// 向第一个栈取出栈顶元素
bool GetTop_0(ShStrack S, int &x){
if(S.top0 == -1)
return false;
x = S.data[S.top0];
return true;
}
6、向第二个栈添加元素
// 向第二个栈添加元素
bool PushShStrack_1(ShStrack &S, int x){
if(S.top1-1 == S.top0)
return false;
S.data[--S.top1] = x;
return true;
}
7、向第二个栈删除并返回栈顶元素
// 向第二个栈删除并返回栈顶元素
bool PopShStrack_1(ShStrack &S, int &x){
if(S.top1 == MaxSize)
return false;
x = S.data[S.top1++];
return true;
}
8、取出第二个栈的栈顶元素
// 取出第二个栈的栈顶元素
bool GetShStrack_1(ShStrack S, int &x){
if(S.top1 == MaxSize)
return true;
x = S.data[S.top1];
return true;
}
二、栈的链式存储
一、链栈(不带头结点)
1、头文件
#include<stdio.h>
#include<stdlib.h>
2、结点的结构体定义
//定义一个结点
typedef struct LinkNode{
int data;
struct LinkNode *next;
}*LiStack;
3、初始化
//初始化(不带头结点)
void InitLiStack(LiStack &L){
L = NULL;
}
4、进栈
//进栈(不带头结点)
bool Push(LiStack &L, int x){
LiStack p = (LiStack)malloc(sizeof(LiStack));
if(p == NULL)
return false;
p->data = x;
p->next = L;
L = p;
return true;
}
5、出栈
// 出栈(不带头结点)
bool Pop(LiStack &L, int &x){
if(L == NULL)
return false;
x = L->data;
LiStack p;
p = L;
L = L->next;
free(p);
return true;
}
6、取栈顶元素
// 取栈顶元素
bool GetTop(LiStack L, int &x){
if(L==NULL)
return false;
x = L->data;
return true;
}
7、建立栈并测试运行
void test(){
LiStack L;
InitLiStack(L);
int x;
scanf("%d", &x);
while(x != 9999){
if(Push(L, x)){
printf("入栈成功!请继续输入(9999为退出):");
scanf("%d", &x);
}
else{
printf("栈满!\n");
}
}
GetTop(L, x);
printf("%d\n", x);
while(L!=NULL){
Pop(L, x);
printf("%d ", x);
}
}
二、链栈(带头结点)
1、结点的结构体定义
与不带头结点相同
2、初始化
//初始化(带头结点)
void InitLiStackH(LiStack &L){
L = (LiStack)malloc(sizeof(LiStack));
L->next = NULL;
}
3、进栈
//进栈(带头结点)
bool PushH(LiStack &L, int x){
LiStack p = (LiStack)malloc(sizeof(LiStack));
if(p == NULL)
return false;
p->data = x;
p->next = L->next;
L->next = p;
return true;
}
4、出栈
// 出栈(带头结点)
bool PopH(LiStack &L, int &x){
if(L->next == NULL)
return false;
LiStack p=L->next;
x = p->data;
L->next = p->next;
free(p);
return true;
}
5、取栈顶元素
//取出栈顶元素(带头结点)
bool GetTopH(LiStack L, int &x){
if(L->next == NULL)
return false;
x = L->next->data;
return true;
}
6、建立栈并测试运行
void testH(){
LiStack L;
InitLiStackH(L);
int x;
scanf("%d", &x);
while(x != 9999){
if(PushH(L, x)){
printf("入栈成功!请继续输入(9999为退出):");
scanf("%d", &x);
}
else{
printf("内存无空间\n");
}
}
GetTopH(L, x);
printf("%d\n", x);
while(L->next!=NULL){
PopH(L, x);
printf("%d ", x);
}
}
三、队列的顺序存储
循环队列,将队尾的表达式写成Q.rear = (Q.rear+1)%MaxSize 当Q.rear == MaxSize时,队尾指针会重新指向位序为0的数据,形成循环。
判空条件为 Q.rear == Q.front ,所以判满的时候要牺牲一个空间存放Q.rear指针,不能使二者指向同一个位置所以判满条件为(Q.rear+1)%MaxSize == Q.front
★★★ 队列元素个数 = (Q.rear + MaxSize - Q.front) % MaxSize★★★
本代码基于队尾指针指向队尾的下一个元素,如指向队尾元素则代码稍有不同
一、使用一个存储空间存放尾指针
1、头文件并使用宏定义给出存储最大结点数
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 20
2、队列的结构体定义
//定义一个队列
typedef struct{
int data[MaxSize];
int front, rear; // front指向队头,rear指向队尾的下一个元素
//int size; //方法一:不想浪费一个空间存放Q.rear指针,需要设置一个变量size初始为0,记录队列长度
// 插入成功size++,删除成功size--
//判空 Q.size == 0; 判满 Q.size == MaxSize;
//int tag //方法二:不想浪费一个空间存放Q.rear指针,需要设置一个变量tag,记录最近一个操作是插入还是删除,插入为1,删除为0
//只有插入才会导致队满,只有删除才会导致队空,插入成功:tag = 1; 删除成功:tag = 0
//判空 Q.rear == Q.front && Q.tag == 0; 判满:Q.rear == Q.front && Q.tag == 1;
}SqQueue;
3、初始化
//初始化一个队列
void InitSqQueue(SqQueue &Q){
Q.front = Q.rear = 0;
}
4、新元素入队
// 元素入队
bool EnSqQueue(SqQueue &Q, int x){
if((Q.rear+1)%MaxSize == Q.front) // 特别注意判满条件
return false;
Q.data[Q.rear] = x;
Q.rear = (Q.rear +1)%MaxSize;
return true;
}
5、队尾元素出队
// 元素出队
bool DeSqQueue(SqQueue &Q, int &x){
if(Q.rear == Q.front)
return false;
x = Q.data[Q.front];
Q.front = (Q.front+1)%MaxSize;
return true;
}
6、读取队首元素(不将队首元素弹出)
bool GetHead(SqQueue Q, int &x){
if(Q.rear == Q.front)
return false;
x = Q.data[Q.front];
return true;
}
7、判断队是否为空
//判空
bool SqQueueEmpty(SqQueue Q){
if (Q.rear == Q.front)
return true;
return false;
}
8、建立并测试队列
void test(){
SqQueue Q;
InitSqQueue(Q);
int x;
scanf("%d", &x);
while(x != 9999 ){
if(EnSqQueue(Q, x)){
printf("已入队!请继续输入(9999为退出):");
scanf("%d", &x);
}
else{
printf("队满\n");
break;
}
}
GetHead(Q, x);
printf("%d\n", x);
while(Q.rear!= Q.front){
DeSqQueue(Q, x);
printf("%d ", x);
}
}
二、队尾指针指向队尾元素,并设置size判空判满
1、结构体定义
typedef struct{
int data[MaxSize];
int rear, front, size;
}SqQueueD;
2、初始化
//初始化
void InitSqQueueD(SqQueueD &Q){
Q.front = 0;
Q.size = 0;
Q.rear = MaxSize-1;
}
3、判空
//判空
bool SqQueueDEmpty(SqQueueD Q){
if(Q.size == 0)
return true;
return false;
}
4、元素入队
//元素入队
bool InSqQueueD(SqQueueD &Q, int x){
if(Q.size == MaxSize)
return false;
Q.rear = (Q.rear+1)%MaxSize;
Q.data[Q.rear] = x;
Q.size++;
return true;
}
5、元素出队
// 元素出队
bool DeSqQueueD(SqQueueD &Q, int &x){
if(Q.size == 0)
return false;
x = Q.data[Q.front];
Q.front = (Q.front+1)%MaxSize;
Q.size--;
return true;
}
6、取队头元素
// 取队头元素
bool GetHeadD(SqQueueD Q, int &x){
if(Q.size == 0)
return false;
x = Q.data[Q.front];
return true;
}
7、建立并测试队列
void testD(){
SqQueueD Q;
InitSqQueueD(Q);
int x;
scanf("%d", &x);
while(x != 9999){
if(InSqQueueD(Q, x)){
printf("已入队!请继续输入(9999为退出):");
scanf("%d", &x);
}
else{
printf("队满\n");
break;
}
}
GetHeadD(Q, x);
printf("%d\n", x);
while(Q.size != 0){
DeSqQueueD(Q, x);
printf("%d ", x);
}
}
三、队尾指针指向队尾元素,并设置tag判空判满 ,
思路:只有删除才会使队列为空,所以删除时将tag置为0,只有添加元素才会使队满,所以入队时将tag置为1。
1、结构体定义
typedef struct SqQueueT{
int data[MaxSize];
int front, rear, tag;
}SqQueueT;
2、初始化
//初始化
void InitSqQueueT(SqQueueT &Q){
Q.front = 0;
Q.rear = 0;
Q.tag = 0;
}
3、判空
//判空
bool QueueTEmpty(SqQueueT Q){
if(Q.front == Q.rear && Q.tag == 0)
return true;
return false;
}
4、元素入队
//入队
bool EnQueueT(SqQueueT &Q, int x){
if(Q.rear == Q.front && Q.tag == 1)
return false;
Q.data[Q.rear] = x;
Q.rear = (Q.rear+1)%MaxSize;
Q.tag = 1;
return true;
}
5、元素出队
//出队
bool DeQueueT(SqQueueT &Q, int &x){
if(Q.rear == Q.front && Q.tag == 0)
return false;
x = Q.data[Q.front];
Q.front = (Q.front + 1)%MaxSize;
Q.tag = 0;
return true;
}
6、取队头元素
//取出队头元素
bool GetHeadT(SqQueueT Q, int &x){
if(Q.rear == Q.front && Q.tag == 0)
return false;
x = Q.data[Q.front];
return true;
}
7、建立并测试队列
void testT(){
SqQueueT Q;
InitSqQueueT(Q);
int x;
scanf("%d", &x);
while(x != 9999){
if(EnQueueT(Q, x)){
printf("已入队!请继续输入(9999为退出):");
scanf("%d", &x);
}
else{
printf("队满\n");
break;
}
}
GetHeadT(Q, x);
printf("%d\n", x);
while(!QueueTEmpty(Q)){
DeQueueT(Q, x);
printf("%d ", x);
}
}
四、队列的链式存储
多了一个结构体存放首尾指针
一、链队(带头结点)
1、头文件
#include<stdio.h>
#include<stdlib.h>
2、队列的结构体定义
//定义链式队列的结点
typedef struct LinkNode{
int data;
struct LinkNode *next;
}LinkNode;
//定义链式队列的队头和队尾指针
typedef struct{
LinkNode *front, *rear;
}LinkQueue;
3、初始化
//初始化(带头结点)
void InitLinkQueueD(LinkQueue &Q){
Q.front = Q.rear = (LinkNode *)malloc(sizeof(LinkNode));
Q.front->next =NULL;
}
4、新元素入队
//新元素入队
bool EnQueueD(LinkQueue &Q, int x){
LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));
if(p == NULL)
return false;
p->data = x;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return true;
}
5、队尾元素出队
//元素出队
bool DeQueueD(LinkQueue &Q, int &x){
if(Q.rear == Q.front)
return false;
LinkNode *p = Q.front->next;
x = p->data;
Q.front->next = p->next;
if(p == Q.rear) //修改队尾指针
Q.rear = Q.front;
free(p);
return true;
}
6、建立并测试队列
//测试(带头结点)
void testD(){
LinkQueue Q;
InitLinkQueueD(Q);
int x;
scanf("%d", &x);
while(x != 9999){
EnQueueD(Q, x);
printf("入队成功!请继续输入(9999为退出):");
scanf("%d", &x);
}
while(Q.rear != Q.front){
DeQueueD(Q, x);
printf("%d ", x);
}
}
一、链队(不带头结点)
1、队列的结构体定义
与带头结点一样
3、初始化
//初始化(不带头结点)
void InitLinkQueue(LinkQueue &Q){
Q.front = Q.rear = NULL;
}
4、新元素入队
//新元素入队
bool EnQueue(LinkQueue &Q, int x){
LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));
p->data = x;
p->next = NULL;
if(Q.front == NULL){
Q.front = Q.rear = p;
return true;
}
Q.rear->next = p;
Q.rear = p;
return true;
}
5、队尾元素出队
//元素出队
bool DeQueue(LinkQueue &Q, int &x){
if(Q.front == NULL)
return false;
LinkNode *p = Q.front;
x = p->data;
Q.front = p->next;
if(p == Q.rear) //修改队尾指针
Q.rear = Q.front;
free(p);
return true;
}
6、建立并测试队列
//测试(不带头结点)
void test(){
LinkQueue Q;
InitLinkQueue(Q);
int x;
scanf("%d", &x);
while(x != 9999){
EnQueue(Q, x);
printf("入队成功!请继续输入(9999为退出):");
scanf("%d", &x);
}
while(Q.rear != Q.front){
DeQueue(Q, x);
printf("%d ", x);
}
}
五、3.3节
1、假设一个算术表达式中包含圆括号、方括号、花括号3种类型括号,编写一个算法来判别表达式中的括号是否匹配,以字符“\0”作为算术表达式的结束符。
//第一题 遇见'(','{','['入栈,否则弹出栈顶元素,是否匹配,匹配则继续进行,否则返回false。
bool pipei(char str[]){
LinkStack S;
InitLinkStack(S);
char e;
int i = 0;
while(str[i] != '\0'){
if(str[i] == '(' || str[i] == '[' || str[i] == '{')
Push(S, str[i]);
else{
if(!Pop(S, e))
return false;
if(str[i] == ')' && e != '(')
return false;
if(str[i] == '}' && e != '{')
return false;
if(str[i] == ']' && e != '[')
return false;
}
i++;
}
if(S->lenght == 0)
return true;
return false;
}
2、按下图所示铁道进行车厢调度(注意,两侧铁道均匀为单向行驶道,火车调度站有一个用于调度的“栈道”),火车调度站的入口处有n节硬座和软座车厢(分别用H和S表示)等待调度,试编写算法,输出对者n节车厢进行调度的操作(即入栈或出栈操作)序列,以使所有的软座车厢都被调整到硬座车厢之前。
//第二题 输入一个只含H、S的字符串,H代表硬车厢,S代表软车厢,依次扫描字符串,把H压入栈中,S跳过,扫描完字符穿依次弹出栈中所有元素
void test2(char str[]){
LinkStack S;
InitLinkStack(S);
int i = 0;
char e;
while(str[i] != '\0'){
if(str[i] == 'S')
printf("%c", str[i]);
else
Push(S, str[i]);
i++;
}
while(S->lenght != 0){
Pop(S, e);
printf("%c", e);
}
}
3、利用一个栈实现以下递归函数的非递归运算
//第三题 建立栈将每一层的递归主题压入栈中,使用循环,其终止条件为n= 1||n=0
void test3(int n,double x){
typedef struct{
int no;
double value;
}st[MaxSize];
st S;
int top = -1;
double fv1, fv2;
for(int i = n; i>=2;i--){
top++;
S[top].no = n;
n--;
}
S[top+1].value = 2*x;
S[top+2].value = 1;
while(top != -1){
fv1 = S[top+1].value;
fv2 = S[top+2].value;
S[top].value = fv1*2*x-2*(S[top].no-1)*fv2;
top--;
}
printf("%.f", S[top+1].value);
}
4、某汽车轮渡口,过江渡船每次能载 10 辆车过江。过江车辆分别为客车类和货车类,上船有如下规定:同类车先到先上船,客车先于货车上渡船,且每上 4 辆客车,才允许上一辆货车;若等待客车不足 4 辆则以货车代替;若无货车等待则允许客 车都上船。设计一个算法模拟渡口管理。
// 3.3
// 第四题
void test4(){
SqQueue Q; // 过江渡船载渡队列
SqQueue q1; // 客车队列
SqQueue q2; // 货车队列
InitSqQueue(Q);
InitSqQueue(q1);
InitSqQueue(q2);
int i = 0, j = 0;
while(j<0){ // j表示渡船上的总车辆数
if(!SqQueueEmpty(q1) && i <4){ // 不足十辆时
DeSqQueue(q1, x); // 客车队列不空,则未上足四辆
EnSqQueue(Q, x); // 客车上船
i++;
j++;
}
else if(i == 4 && !SqQueueEmpty(q2)){ // 客车上足四辆
DeSqQueue(q2, x); // 货车上船
EnSqQueue(Q, x);
j++;
i = 0 // 重新计数
}
else{ // 有一个队列为空
while(j<10 && i< 4 && !SqQueueEmpty(q2)){ // 客车队列空
DeSqQueue(q2, x); // 货车上船
EnSqQueue(Q, x);
i++;
j++;
}
i = 0;
}
if(SqQueueEmpty(q1) && SqQueueEmpty(q2))
j = 11; // 客车货车加起来不足十辆
}
}