DS博客作业02--栈和队列
| 这个作业属于哪个班级 | 数据结构--网络2011/2012 |
| :----: | :----: | :----: |
| 这个作业的地址 | DS博客作业02--栈和队列 |
| 这个作业的目标 | 学习栈和队列的结构设计及运算操作. |
| 姓名 | 陈宇杭 |
0. PTA得分截图
1. 本周学习总结
1.1 栈
① 顺序栈
- 顺序栈通常用数组来存放数据,栈内数据在栈顶进行交互;
操作函数
- 顺序栈结构体
struct SNode {
ElementType* Data; /* 存储元素的数组 */
Position Top; /* 栈顶指针 */
int MaxSize;
};
- 创建栈并初始化
Stack CreateStack(int MaxSize)
{
Stack S = (Stack)malloc(sizeof(struct SNode));
S->Data = (ElementType*)malloc(MaxSize * sizeof(ElementType));
S->Top = -1;//将栈空时的栈顶指针设为 -1
S->MaxSize = MaxSize;
return S;
}
- 入栈
void PushStack(Stack S, ElementType x)//入栈
{
if (S->Top == S->MaxSize)
{
printf("Stack Full\n");
return;
}
S->Top++;
S->Data[S->Top] = x;
}
- 出栈
ElementType PopStack(Stack S)//出栈,并将栈顶元素作为返回值
{
if (S->Top == -1)
{
printf("Stack Empty\n");
return 0;
}
return S->Data[S->Top--];
}
- 栈空
int StackEmpty(Stack S)//当栈为空时返回1
{
if (S->Top == -1)
return 1;
else
return 0;
}
② 链栈
- 相比于顺序栈,链栈没有预申请空间不足的顾虑;头插法也保证入栈出栈操作的一步到位;
操作函数
由于C++标准库的栈函数就是以链栈结构为基础,此处只介绍操作函数;
#include<stack>//头文件
stack<int> s;//创建一个int变量类型的链栈 s ;
s.empty(); //如果栈为空则返回true, 否则返回false;
s.size(); //返回栈中元素的个数
s.top(); //返回栈顶元素, 但不删除该元素
s.pop(); //弹出栈顶元素, 但不返回其值
s.push(); //将元素压入栈顶,大多是使用头插法
链栈稍微牺牲空间,换来条理分明的结构与更快的操作速度
1.2 栈的应用
-
中缀表达式改写为后缀表达式
在数学计算中,我们习惯与通过中缀表达形式计算算式,即数字在运算符号的两边,而对于计算机而言,后缀表达式更适合处理算式,因此必然有,从中缀表达式到后缀表达式的过程,并且计算机利用后缀表达式计算的过程,,而这些都可以通过栈实现。
通过一个预先设定好的优先级入栈出栈,'+''-'优先级最低,'*''/'较高,括号内的算式优先级更高,在进行比较出栈入栈操作 -
括号匹配检验
在辅助编程的软件中,总会有一个括号匹配的判断检验,如果有括号没有对应的另一半进行配对,编译器就会报错来提醒那些粗心的程序员;而对于括号来说,每一个左括号都需要一个相应的右括号与之匹配,表达式中越迟出现并且没有得到匹配的左括号匹配的优先级越高,这种先进先出的特点正与栈结构匹配;
基本思路:创建一个栈,在读入字符的过程中,如果是左括号,则直接入栈,等待相匹配的同类右括号;如果是右括号,且与当前栈顶左括号匹配,则将栈顶左括号出栈; -
迷宫求解
对迷宫来说,所有的迷宫都有一个特点,即从入口出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路退回,换一个方向再继续探索,直至所有可能的通路都探索到为止;原路返回的操作,显然需要个先进先出的结构来储存上一级信息,所以栈自然可以来求解迷宫;
伪代码:
初始化,将起点加入堆栈;
while(堆栈不为空){
取出栈顶位置为当前位置;
if(当前位置是终点)
使用堆栈记录的路径标记从起点至终点的路径;
else
{
按照某个顺序将当前位置下一个可以探索的位置入栈;
if(当前位置的四周均为死路)
当前位置出栈;
}
}
1.3 队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头.
① 顺序队列
- 结构体定义
typedef struct
{
ElemType data[MaxSize];
int front, rear;
}SqQueue;
typedef SqQueue* Queue;
- 初始化队列
void InitQueue(Queue Q)
{
Q = (Queue)malloc(sizeof(SqQueue)*Max);
Q->front = Q->rear = 0;
}
- 销毁队列
void DestroyQueue(Queue Q)
{
free(Q);
}
- 判断队列是否为空
bool QueueEmpty(Queue Q)
{
return (Q->front == Q->rear);
}
- 入队
bool EnQueue(Queue Q,ElemType e)
{
if(Q->rear == MaxSize)
return false;
Q->data[Q->rear++] = e;
return true;
}
- 出队
bool DeQueue(Queue Q,ElemType &e)
{
if(Q->front == Q->rear)
return false;
e = Q->data[Q->front++];
return true;
}
② 环形队列
- 结构体定义
typedef struct
{
ElemType data[MaxSize];
int front, rear;
}SqQueue;
typedef SqQueue* Queue;
- 初始化队列
void InitQueue(Queue Q)
{
Q = (Queue)malloc(sizeof(SqQueue)*Max);
Q->front = Q->rear = 0;
}
- 销毁队列
void DestroyQueue(Queue Q)
{
free(Q);
}
- 判断队列是否为空
bool QueueEmpty(Queue Q)
{
return (Q->front == Q->rear);
}
- 入队
bool EnQueue(Queue Q,ElemType e)
{
if((Q->rear +1) % MaxSize == Q->front)
return false;
Q->data[Q->rear] = e;
Q->rear = (Q->rear + 1) % MaxSize;
return true;
}
- 出队
bool DeQueue(Queue Q,ElemType &e)
{
if(Q->front == Q->rear)
return false;
e = Q->data[Q->front];
Q->front = (Q->front + 1) % MaxSize;
return true;
}
③ 链式队列
- 结构体定义
typedef struct QNode /* 声明链式队列的结点 */
{
int data;
struct QNode *next;
}Node;
typedef struct QueuePoint /* 声明链式队列的首尾指针 */
{
Node *front;
Node *rear;
};
typedef QueuePoint* Queue;
- 初始化队列
Queue InitQueue (Queue Q)
{
Q = (Queue)malloc(sizeof(QueuePoint));
Q->front = Q->rear = NULL;
return Q;
}
- 销毁队列
void DestroyQueue(Queue Q)
{
Node* pre = Q->front,* p;
if(pre != NULL)
{
p = pre->next;
while(p != NULL)
{
free(pre);
pre = p;p = p->next;
}
free(pre);
}
free(Q);
}
- 判断队列是否为空
bool QueueEmpty(Queue Q)
{
return (Q->rear == NULL);
}
- 入队
void EnQueue(Queue Q,ElemType e)
{
Node* p;
p = (Node*)malloc(sizeof(Node));
p->data = e;
p->next = NULL;
if(Q->rear == NULL)
Q->front = Q->rear = p;
else
{
Q->rear->next = p;
Q->rear = p;
}
}
- 出队
bool DeQueue(Queue Q,ElemType &e)
{
Node* p;
if(Q->rear == NULL)
return false;
p = Q->front;
if(Q->front == Q->rear)
Q->front = Q->rear = NULL;
else
Q->front = Q->front->next;
e = p->data;
free(p);
return true;
}
④ 队列应用
- 银行业务队列简单模拟
某银行有A、B两个业务窗口,且处理业务的速度不一样,其中A窗口处理速度是B窗口的2倍 —— 即当A窗口每处理完2个顾客时,B窗口处理完1个顾客。编号为奇数的顾客需要到A窗口办理业务,为偶数的顾客则去B窗口;给出顾客人数与编号,顺序输出顾客办理完成业务的编号;
创建队列A,B;
读入人数n;
for (int i = 0; i < n; i++)//入列
{
cin >> numb;
if (numb % 2 == 1)
EnQueue(WindowA, numb);//奇数号入A列
else
EnQueue(WindowB, numb);//偶数号入B列
}
按照AAB的顺序输出即可;
2. PTA实验作业
2.1 符号配对
2.1.1 解题思路及伪代码
-
解题思路
后缀表达式,顾名思义是运算符在数字后的表达式,即取运算符前的两个数字作为被运算数和运算数;
在中缀表达式转后缀表达式的过程中,同样需要考虑运算符优先级的问题,这就可能导致优先度高的运算符先于优先度低的运算符输出,先入先出的特性,适合用栈结构来辅助转换;
令'+'与'-'作为低级运算符,优先度为0;'*'与'/'作为高级运算符,优先度为1;括号内式子优先度最高,在我的思路里将括号内的算式看做新的算式,进行递归转换;
将按序读取到的运算符与栈顶运算符进行对比,优先度低的运算符入栈,优先度高的运算符输出,优先度相等则先出栈后入栈,遇到括号则递归调用函数;
数值数据则直接输出,不进行栈操作; -
伪代码
创建字符串并将算式读入字符串内;
某函数()
{
创建并初始化栈;
遍历字符串
{
if(读到'\0'或')')
返回;
if(字符为数字,小数点或正负值的符号)
输出字符;
else if(栈内为空 或 字符优先级高于栈顶字符)
入栈;
else if(字符优先级低于栈顶字符)
{
do 出栈并输出 while (字符优先级高于栈顶 或 栈为空);
入栈;
}
else if(字符为左括号)
递归调用 某函数();
}
}
- 源代码
展开
#include
#include
#include
#include
using namespace std;
#define Max 1000
#define ERROR -1
typedef char ElementType;
typedef int Position;
struct SNode {
ElementType* Data; /* 存储元素的数组 */
Position Top; /* 栈顶指针 */
int MaxSize;
};
typedef struct SNode* Stack;
Stack CreateStack(int MaxSize);
void EnStack(Stack S, char x);
char PopStack(Stack S);
void SymbolPairing(Stack S);
void NotesSymbol(Stack S);
int main()
{
Stack S;
S = CreateStack(Max);
SymbolPairing(S);
return 0;
}
void SymbolPairing(Stack S)
{
char chL = 0, chR = 0;
char leftSymbolL, leftSymbolR;
while ((chR = getchar()) && chR != EOF)
{
if (chL == '.' && chR == '\n')
break;
if (chR == '[' || chR == '{' || chR == '(')
EnStack(S, chR);
else if (chL == '/' && chR == '*')
{
EnStack(S, chL);
EnStack(S, chR);
//NotesSymbol(S);
chR = ' ';
}
else if (chR == ']' || chR == '}' || chR == ')' || (chL == '*' && chR == '/'))
{
if (S->Top == -1)
{
cout << "NO" << endl;
if (chL == '*' && chR == '/')
cout << "?-" << chL << chR << endl;
else
cout << "?-" << chR << endl;
return;
}
leftSymbolL = PopStack(S);
if ((leftSymbolL == '[' && chR != ']') || (leftSymbolL == '{' && chR != '}') || (leftSymbolL == '(' && chR != ')') || (leftSymbolL == '*' && !(chL == '*' && chR == '/')))
{
cout << "NO" << endl;
cout << leftSymbolL << "-?" << endl;
return;
}
else if (leftSymbolL == '*')
{
if (chL == '*' && chR == '/')
PopStack(S);
else
{
cout << "NO" << endl;
cout << PopStack(S) << leftSymbolL << "-?" << endl;
return;
}
}
chR = ' ';
}
chL = chR;
}
if (S->Top != -1)
{
cout << "NO" << endl;
if (S->Data[S->Top] != '*')
cout << PopStack(S) << "-?" << endl;
else
cout << PopStack(S) << PopStack(S) << "-?" << endl;
}
else
cout << "YES" << endl;
return;
}
Stack CreateStack(int MaxSize)
{
Stack S = (Stack)malloc(sizeof(struct SNode));
S->Data = (ElementType*)malloc(MaxSize * sizeof(ElementType));
S->Top = -1;
S->MaxSize = MaxSize;
return S;
}
void EnStack(Stack S, char x)
{
if (S->Top == S->MaxSize)
{
printf("Stack Full\n");
return;
}
S->Top++;
S->Data[S->Top] = x;
}
char PopStack(Stack S)
{
if (S->Top == -1)
{
printf("Stack Empty\n");
return 0;
}
return S->Data[S->Top--];
}
/*void NotesSymbol(Stack S)
{
char chL = 0, chR = 0;
while ((chR = getchar()) && chR != EOF)
{
if (chL == '.' && chR == '\n')
{
cout << "NO" << endl;
cout << PopStack(S) << PopStack(S) << "-?" << endl;
exit(0);
}
if (chL == '*' && chR == '/')
{
PopStack(S);
PopStack(S);
return;
}
chL = chR;
}
}*/
2.1.2 总结解题所用的知识点
- 四则运算法则;
- 顺序栈的结构;
- 顺序栈的出栈入栈操作;
- 递归函数的调用;
- 标准输入输出流的运用;
2.2 符号配对
2.2.1 解题思路及伪代码
- 解题思路
这题啊,这题比上面那题好搞多了;
弄两个队列A、B,读入数据,将奇数编号的入A列,将偶数编号的入B列;
在按A、A、B的顺序输出(若队列为空则跳过);
结束 - 伪代码
创建队列 A, B;
读入人数 n;
for(从0到n)
{
读入数据x;
如果x为奇数
入A列;
如果x为偶数
入B列;
}
按AAB的顺序输出;
结束;
- 源代码
展开
#include
#include
#include
#include
using namespace std;
#define Max 2000
#define ERROR -1
typedef int ElementType;
typedef int Position;
struct QNode {
ElementType* Data; /* 存储元素的数组 */
Position Front, rear; /* 队列的头、尾指针 */
int MaxSize; /* 队列最大容量 */
};
typedef struct QNode* Queue;
Queue CreateQueue(int MaxSize);
void EnQueue(Queue Q, int x);
int PopQueue(Queue Q);
int QueueEmpty(Queue Q);
int main()
{
int n;
cin >> n;
int flag = 0;
int numb;
Queue WindowA, WindowB;
WindowA = CreateQueue(Max);
WindowB = CreateQueue(Max);
for (int i = 0; i < n; i++)
{
cin >> numb;
if (numb % 2 == 1)
EnQueue(WindowA, numb);
else
EnQueue(WindowB, numb);
}
while (!QueueEmpty(WindowA) || !QueueEmpty(WindowB))
{
if (!QueueEmpty(WindowA))
{
if (flag++)
cout << ' ';
cout << PopQueue(WindowA);
}
if (!QueueEmpty(WindowA))
{
if (flag++)
cout << ' ';
cout << PopQueue(WindowA);
}
if (!QueueEmpty(WindowB))
{
if (flag++)
cout << ' ';
cout << PopQueue(WindowB);
}
}
return 0;
}
Queue CreateQueue(int MaxSize)
{
Queue Q = (Queue)malloc(sizeof(struct QNode));
Q->Data = (ElementType*)malloc(MaxSize * sizeof(ElementType));
Q->Front = 0;
Q->rear = 0;
Q->MaxSize = MaxSize;
return Q;
}
void EnQueue(Queue Q, int x)
{
if (Q->rear == Q->MaxSize)
{
printf("Queue Full\n");
return;
}
Q->Data[Q->rear++] = x;
return;
}
int PopQueue(Queue Q)
{
if (Q->rear == Q->Front)
{
printf("Queue Empty\n");
return -1;
}
return Q->Data[Q->Front++];
}
int QueueEmpty(Queue Q)
{
if (Q->rear == Q->Front)
return 1;
else
return 0;
}
2.2.2 总结解题所用的知识点
- 顺序队列的结构
- 顺序队列的入列出列操作
- 奇数和偶数的定义
- 生活常识
3.阅读代码
3.1 题目及解题代码
bool validateStackSequences(int *pushed, int pushedSize, int *popped, int poppedSize)
{
if (pushed == NULL || pushedSize < 0 || popped == NULL || poppedSize < 0) {
return false;
}
if (pushedSize > 1000 || poppedSize > 1000){
return false;
}
int tmpStack[1000] = {0};
int tmpStackTop = -1;
int pushedIter = 0;
int poppedIter = 0;
while (pushedIter < pushedSize) {
// 入栈一个数
tmpStack[++tmpStackTop] = pushed[pushedIter++];
// 当前栈顶如果和pop当前位置相同,则出栈
while (tmpStackTop >= 0 && poppedIter < poppedSize && tmpStack[tmpStackTop] == popped[poppedIter]) {
// 出栈后,栈顶下移,pop序列增加
tmpStack[tmpStackTop--] = 0;
poppedIter++;
}
}
// 最终栈底回到-1位置,否则没有完全匹配
if (tmpStackTop != -1) {
return false;
}
return true;
}
3.2 该题的设计思路及伪代码
-
思路
考察程序员对栈结构的理解,以及栈的基础操作的应用; -
伪代码
创建栈 S;
创建队列 Q;//图个方便
将popped的数据入队列Q;
while(队列不空)
{
若pushed还有数据输入
入栈;
否则
将栈顶元素与队头元素匹配,若不同
返回false;
将栈顶元素与队头元素匹配,若相同
出栈且出列;
}
返回true;
3.3 分析该题目解题优势及难点。
解题优势:Java以及Python中的库函数(类似)可以大幅简化代码(10行);
难点:考察栈的基础知识以及应用,难点在于理解深度以及循环跳出(false)条件判断;