DS博客作业02--栈和队列
| 这个作业属于哪个班级 | 数据结构--网络2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | DS博客作业02--栈和队列 |
| 这个作业的目标 | 学习栈和队列的结构设计及运算操作 |
| 姓名 | 邓宏 |
0.PTA得分截图
1.本周学习总结(0-5分)
1.1 栈
顺序栈的结构、操作函数
结构:
操作函数:
定义:
typedef struct
{ ElemType data[MaxSize]; //栈中数据元素
int top; //top为栈顶指针
} Stack;
typedef Stack *SqStack;
初始化:
void InitStack(SqStack s)
{
s = new SqStack;//分配一个顺序栈的空间,首地址存放s处
s->top = -1; //栈顶指针置为-1
}
入栈出栈:
bool Push(SqStack &s,ElemType e)
{
if(s->top == MAXSIZE - 1) //判断是否栈满
{
return false;
}
s->data[s->top++] = e; //入栈
return true;
}
bool Pop(SqStack &s,ElemType e)
{
if(StackEmpty(s)) //判断是否为空栈
{
return false;
}
e = s->data[s->top--]; //退栈
return true;
}
销毁栈:
void DestroyStack(SqStack s)
{
delete s;
}
链栈的结构、操作函数
结构:
操作函数:
定义:
typedef struct StackNode
{
ElemType data;
struct StackNode *next;
}Node,*Stack;
初始化:
bool InitStack(Stack &s)
{
s = NULL;
return true;
}
入栈出栈:
void Push(Stack& s, ElemType e)
{
Stack p;
p = new Node;
p->data = e; //新建节点p
p->next = s->next; //插入*p节点作为开始节点
s->next = p;
}
bool Pop(Stack& s, ElemType& e)
{
Stack p;
if (StackEmpty(s)) //栈空的情况
return false;
p = s->next; //p指向开始节点,从栈顶开始出栈
e = p->data;
s->next = p->next; //删除*p节点
delete p; //释放*p节点
return true;
}
1.2 栈的应用
括号匹配检验
假设表达式中包含三种括号:圆括号、方括号和花括号,并且它们可以任意嵌套。例如{()[{}]}或[{()}([])]等为正确格式,而{[}()]或[({)]为不正确的格式。
算法需要一个栈,在读入字符的过程中,如果是左括号,则直接入栈,等待相匹配的同类右括号;如果是右括号,且与当前栈顶左括号匹配,则将栈顶左括号出栈,如果不匹配则属于不合法的情况。另外,如果碰到一个右括号,而堆栈为空,说明没有左括号与之匹配,则非法。那么,当字符读完的时候,如果是表达式合法,栈应该是空的,如果栈非空,那么则说明存在左括号没有相应的右括号与之匹配,也是非法的情况。
#include<iostream>
#include<stack>
#include<string>
#include<vector>
using namespace std;
bool MatchBrackets(string braket_st);
int main()
{
string bracket_str;
cin >> bracket_str;
bool temp;
temp = MatchBrackets(bracket_str);
return 0;
}
bool MatchBrackets(string braket_st)
{
stack<char> st;//初始化栈
int i;
for (i = 0; i < braket_st.size(); i++)
{
if (braket_st[i] == '(')
st.push(braket_st[i]);//入栈
else if (braket_st[i] == ')')
{
if (st.empty())//栈为空
{
cout << "no";
return false;
}
if (st.top() != '(')
{
cout << st.top() << endl;//取栈顶元素
cout << "no";
}
else//成功
{
st.pop();//输出
}
}
if (braket_st[i] == '[')
st.push(braket_st[i]);//输入
else if (braket_st[i] == ']')
{
if (st.empty())//栈为空
{
cout << "no";
return false;
}
if (st.top() != '[')
{
cout << st.top() << endl;//取栈顶元素
cout << "no";
}
else//成功
{
st.pop();
}
}
if (braket_st[i] == '{')
st.push(braket_st[i]);//输入
else if (braket_st[i] == '}')
{
if (st.empty())//栈空
{
cout << "no";
return false;
}
if (st.top() != '{')
{
cout << st.top() << endl;//取栈顶元素
cout << "no";
}
else
{
st.pop();
}
}
}
if (st.empty() && braket_st[i] == '\0')//找到配对并且扫描完成后栈为空
{
cout << "yes";
return true;
}
else if (!st.empty())//非空条件下.不配对
{
cout << st.top() << endl << "no";
return false;
}
}
1.3 队列
注:包括顺序队列和环形队列
顺序队列的结构、操作函数
定义:
typedef struct
{ ElemType data[MaxSize];
int front,rear; //队首和队尾指针
}Queue;
typedef Queue *SqQueue;
初始化:
void InitQueue(SqQueue &q)
{ q=new Queue;
q->front=q->rear=-1;
}
入队出队:
bool enQueue(SqQueue &q,ElemType e)
{
if (q->rear+1==MaxSize) return false;
//队满上溢出
q->rear=q->rear+1;
q->data[q->rear]=e;
return true;
}
bool deQueue(SqQueue &q,ElemType &e)
{
if (q->front==q->rear) //队空下溢出
return false;
q->front=q->front+1;
e=q->data[q->front];
return true;
}
销毁
void DestroyQueue(SqQueue &q)
{
delete q;
}
环形队列的结构、操作函数
定义:
typedef struct
{
ElemType data[MaxSize];
int front,rear;
} Queue;
typedef Queue *SqQueue;
初始化:
void InitQueue(SqQueue &q)
{ q=new Queue;
q->front=q->rear=0;
}
出队入队:
bool enQueue(SqQueue &q,ElemType e)
{ if ((q->rear+1)%MaxSize==q->front) //队满上溢出
return false;
q->rear=(q->rear+1)%MaxSize;
q->data[q->rear]=e;
return true;
}
bool deQueue(SqQueue &q,ElemType &e)
{ if (q->front==q->rear) //队空下溢出
return false;
q->front=(q->front+1)%MaxSize;
e=q->data[q->front];
return true;
}
注:循环队列长度=Q.rear-Q.front+MAXQSIZE)%MAXQSIZE
链队列的结构、操作函数
定义:
typedef struct QNode{
QElemType data;
struct Qnode *next;
}Qnode, *QueuePtr;//定义节点类型
typedef struct {
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LinkQueue; //定义队列类型
初始化:
Status InitQueue (LinkQueue &Q)
{
Q.front=Q.rear=new QNode;
if(!Q.front) exit(OVERFLOW);
Q.front->next=NULL;
return OK;
}
入队出队:
Status EnQueue(LinkQueue& Q, QElemType e)
{
p = (QueuePtr)malloc(sizeof(QNode));
if (!p) exit(OVERFLOW);
p->data = e; p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
Status DeQueue(LinkQueue& Q, QElemType& e)
{
if (Q.front == Q.rear) return ERROR;
p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if (Q.rear == p) Q.rear = Q.front;
delete p;
return OK;
}
队列应用,要有具体代码操作。
破解迷宫:
使用队列来求解,假设当前点位为(x, y),在队列中的索引为front,遍历该位置的四个方位,如果方位可走则入队,并记录这个方位元素的前驱为front。如下图所示,当前点位上方的点位不可走,不入队;右方可走,入队;下方可走入队;左方可走入队;然后将front++,这时候当前点位变成(x, y+1),继续遍历它的四个方位,淘汰掉不可走的,可走的方位都会入队…。这样一层一层向外扩展可走的点,所有可走的点位各个方向都会尝试,而且机会相等,直到找到出口为止,这个方法称为“广度优先搜索方法”。然后我们从出口反向找其上一个方块的下标,直到下标为0,这个反向过程就能找到最短路径。
public static QueueByArray<MazeElem> getMazePathByArrayQueue(int[][] maze, int startX,
int startY, int endX, int endY){
int x, y, di;
QueueByArray<MazeElem> queue = new QueueByArray();
//入口元素进队
MazeElem elem = new MazeElem(startX, startY, 0,-1);
queue.enQueue(elem);
maze[startX][startY] = -1; //将入口元素置为-1,避免回过来重复搜索
int front = 0; //记录当前操作的可走方块在队列中的索引
//队列不为空且未找到路径时循环
while(front<=queue.getRear()){
x = queue.getElement(front).x;
y = queue.getElement(front).y;
if(x == endX && y == endY){ //找到了出口
int k = front, j;
//反向找到最短路径,将该路径上的方块的pre设置为-1
do{
j = k;
k = queue.getElement(k).pre; //上一个可走方块索引
queue.getElement(j).pre = -1; //将路径上的元素的pre值为-1
}while(k!=0);
//返回队列,队列中pre为-1的元素,构成最短路径
return queue;
}
for(di = 0; di<4; di++){ //循环扫描每个方位,把每个可走的方块插入队列中
switch (di){
case 0: //上
x = queue.getElement(front).x-1;
y = queue.getElement(front).y;
break;
case 1: //右
x = queue.getElement(front).x;
y = queue.getElement(front).y+1;
break;
case 2: //下
x = queue.getElement(front).x+1;
y = queue.getElement(front).y;
break;
case 3: //左
x = queue.getElement(front).x;
y = queue.getElement(front).y-1;
break;
}
if(x>=0 && y>=0 && x<maze.length && y<maze[0].length){
if(maze[x][y] == 0){
//将该相邻方块插入队列
queue.enQueue(new MazeElem(x, y, 0, front));
maze[x][y] = -1; //赋值为-1,避免回过来重复搜索
}
}
}
front ++;
}
return null;
}
2.PTA实验作业(4分)
此处请放置下面2题代码所在码云地址(markdown插入代码所在的链接)。如何上传VS代码到码云
2.1 符号配对
2.1.1 解题思路及伪代码
遍历输入数据,取符号置于全新的数组中,然后依次入栈并加以判断
2.1.2 总结解题所用的知识点
1.数组的应用
2.栈的应用
2.2 银行业务队列简单模拟
2.2.1 解题思路及伪代码
用A,B两个队列分别存储顾客编号。A存储顾客编号为奇数的,则B存储顾客号为偶数。当A窗口没有解决完2个顾客的时且A队列不空,则A出队;当A解决完2人且B队列不空,则B出队
2.2.2 总结解题所用的知识点
1.采用queue库函数更加便捷,获取队首元素
2.需要借用flag来判断空格的输入输出
3.当A或B两个有一个队列为空就退出循环,然后单独输出不为空的队列
3.阅读代码(0--1分)
从键盘上输入一个逆波兰表达式,用伪码写出其求值程序。规定:逆波兰表达式的长度不超过一行,以$符作为输入结束,操作数之间用空格分隔,操作符只可能有+、-、、/四种运算。例如:234 34+2$
答:[题目分析]逆波兰表达式(即后缀表达式)求值规则如下:设立运算数栈OPND,对表达式从左到右扫描(读入),当表达式中扫描到数时,压入OPND栈。当扫描到运算符时,从OPND退出两个数,进行相应运算,结果再压入OPND栈。这个过程一直进行到读出表达式结束符$,这时OPND栈中只有一个数,就是结果。
float expr( )
//从键盘输入逆波兰表达式,以‘$’表示输入结束,本算法求逆波兰式表达式的值。
{float OPND[30]; // OPND是操作数栈。
init(OPND); //两栈初始化。
float num=0.0; //数字初始化。
scanf (“%c”,&x);//x是字符型变量。
while(x!=’$’)
{switch
{case‘0’<=x<=’9’:while((x>=’0’&&x<=’9’)||x==’.’) //拼数
if(x!=’.’) //处理整数
{num=num*10+(ord(x)-ord(‘0’)); scanf(“%c”,&x);}
else //处理小数部分。
{scale=10.0; scanf(“%c”,&x);
while(x>=’0’&&x<=’9’)
{num=num+(ord(x)-ord(‘0’)/scale;
scale=scale*10; scanf(“%c”,&x); }
}//else
push(OPND,num); num=0.0;//数压入栈,下个数初始化
case x=‘ ’:break; //遇空格,继续读下一个字符。
case x=‘+’:push(OPND,pop(OPND)+pop(OPND));break;
case x=‘-’:x1=pop(OPND);x2=pop(OPND);push(OPND,x2-x1);break;
case x=‘*’:push(OPND,pop(OPND)*pop(OPND));break;
case x=‘/’:x1=pop(OPND);x2=pop(OPND);push(OPND,x2/x1);break;
default: //其它符号不作处理。
}//结束switch
scanf(“%c”,&x);//读入表达式中下一个字符。
}//结束while(x!=‘$’)
printf(“后缀表达式的值为%f”,pop(OPND));
}//算法结束。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人