博客作业02--栈和队列
| 这个作业属于哪个班级 | 数据结构--网络2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | 博客作业02--栈和队列 |
| 这个作业的目标 | 学习栈和队列的结构设计及运算操作 |
| 姓名 | 章审 |
0.PTA得分截图
1.本周学习总结
1.1栈
顺序栈的结构、操作函数
顺序栈的结构
栈中数据元素的逻辑关系呈线性关系,所以栈可以像线性表一样采用顺序存储结构进行存储,即分配一块连续的存储空间来存放栈中元素,并用一个变量指向当前的栈顶元素以反映栈中元素的变化。采用顺序 视频讲解存储结构的栈称为顺序栈。
typedef struct
{
ElemTyqe data[MaxSize];
int top;
}
顺序栈的操作函数
1.初始化栈initStack(&s)
该运算创建一个空栈,由s指向它。实际上就是分配一个顺序栈空间,并将栈顶指针设置为-1。算法如下:
void InitStack(SqStack *&s)
{
s=(SqStack *)malloc(sizeof(SqStack));
s->top=-1;
}
2.销毁栈DestroyStack(&s)
该运算释放顺序栈s占用的储存空间。算法如下:
void DestroyStack(SqStack *&s)
{
free(s);
}
3.判断栈是否为空StackEmpty(s)
该运算实际上用于判断条件s->top==-1是否成立。算法如下:
bool StackEmpty(SqStack *s)
{
return(s->top==-1);
}
4.进栈Push(&S,e)
该运算新建一个结点,用于存放元素e(由p指向它),然后将其插入到头结点之后作为新的首结点。算法如下:
void Push(LinkStNode *&s,ElemType e)
{
LinkStNode *p;
p=(LinkStNode*)malloc(sizeof(LinkStNode));
p->data=e;
p->next=s->next;
s->next=p;
}
5.出栈Pop(&s,&e)
该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e,然后将其删除。
bool Pop(LinkStNode*&s,ElemType &e)
{
LinkStNode *p;
if(s->next==NULL)
return false;
p=s->next;
e=p->data;
s->next=p->next;
free(p);
return true;
}
6.取栈顶元素GetTop(s, &e)
该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e。算法如下:
bool GetTop(LinkStNode *s, ElemType&e)
{
if(s->next==NULL)
return false;
e=s->next->data;
return true;
}
链栈的结构、操作函数
链栈的结构
栈中数据元素的逻辑关系呈线性关系,所以栈可以像线性表一样采用链式存储结构。采用链式存储结构的栈称为链栈(linked stack),链表有多种,这里采用带头结点的单链表来实现链栈。
链栈中节点类型LinkStNode的声明如下:
typedef struct linknode
{
Elem Type data;
struct linknode *next;
} LinkStNode;
链栈的操作函数
1.初始化栈
该运算创建一个空链栈s。实际上是创建链栈的头结点,并将其next域置为NULL。算法如下:
void InitStack(LinkStNode*&s)
{
s=(LinkStNode*)malloc(sizeof(LinkStNode));
s->next=NULL;
}
2.销毁栈
该运算释放链栈s占用的全部结点空间,和单链表的销毁算法完全相同。算法如下:
viod DestroyStack(LinkStNode*&s)
{
LinkStNode * pre=s,*p=s->next;
while(p!=NULL)
{
free(pre);
pre=p;
p=pre->next;
}
free(pre);
}
3.判断栈是否为空
该运算判断s->next=NULL的条件是否为成立。算法如下:
bool StackEpty(LinkStNode *s)
{
return(s->next==NULL);
}
4.进栈Push(&s,e)
该运算新建一个结点,用于存放元素e(由p指向它),然后将其插入到头结点之后作为新的首结点。算法如下:
void Push(LinkStNode *&s,ElemType e)
{
LinkStNode *p;
p=(LinkStNode*)malloc(sizeof(LinkStNode));
p->data=e;
p->next=s->next;
s->next=p;
}
5.出栈Pop(&s,&e)
该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e,然后将其删除。算法如下:
bool Pop(LinkStNode*&s,ElemType&e)
{
LinkStNode*p;
if(s->next==NULL)
return false;
p=s->next;
e=p->data;
s->next=p->next;
free(p):
return true;
}
6.取栈顶元素
该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e。算法如下:
bool GetTop(Link*s,ElemType&e)
{
if(s->next==NULL)
return false;
e=s->nest->data;
return true;
}
1.2栈的应用
表达式的转换
代码如下
#include <iostream>
#include <stack>
#include <string>
using namespace std;
int main()
{
stack<char>st;
char s[50];
int i = 0, n = 0;
char c[22];
char e;
cin >> c;
while (c[i]!='\0')//进行遍历,开始转换成后缀表达式
{
switch (c[i])
{
case'('://判定为左括号
st.push('(');//左括号进栈
i++;//继续扫描其他字符
break;
case')'://判定为右括号
e = st.top();//出栈元素e
st.pop();
while (e != '(')//不为‘(’时循环
{
s[n++] = e;//将e放入s中;
s[n++] = ' ';
e = st.top();//继续出栈元素e
st.pop();
}
i++;//继续扫描其他字符
break;
case'+'://判定为加号或减号
case'-':
while (!st.empty())//栈不空循环
{
e = st.top();//取栈顶元素e
if (e != '(')//e不是‘(’
{
s[n++] = e;//将e存放到s中
s[n++] = ' ';
e = st.top();//出栈元素e
st.pop();
}
else//e是‘(’退出循环
break;
}
st.push(c[i]);//将‘+’或‘-’进栈
i++;//继续扫描其他字符
break;
case'*'://判定为‘*’或‘/’号
case'/':
while (!st.empty())栈不空循环
{
e = st.top();//取栈顶元素e
if (e == '*' || e == '/')//将栈顶‘*’或‘/’运算符出栈并存放到s中
{
s[n++] = e;将e存放到s中
s[n++] = ' ';
e = st.top();//出栈元素e
st.pop();
}
else//e为非‘*’或‘/’运算符时退出循环
break;
}
st.push(c[i]);//将‘*’或‘/’进栈
i++;//继续扫描其他字符
break;
default://处理数字字符
while (c[i] >= '0' && c[i] <= '9')//判定为数字字符
{
s[n++] = c[i];
i++;
}
s[n++] = ' ';
}
}
while (!st.empty())//此时c扫描完毕,栈不空时循环
{
e = st.top();77出栈元素e
st.pop();
s[n++] = e;将e放到s中
s[n++] = ' ';
}
s[--n] = '\0';//给s表达式添加结束标识
cout << s;
}
只是简单的加减乘除,没有考虑到负数和小数。
1.3队列
顺序队列的结构、操作函数
顺序队列的结构
队列中数据元素的逻辑关系呈线性关系,所以队列可以像线性表一样采用顺序存储结构进行存储,即分配一块连续的存储空间来存放队列中的元素,并用两个整型变量来反映队列中元素的变化,它们分别存储队首元素和队尾元素的下标位置,分别称为队首指针(队头指针)和队尾指针。采用顺序存储结构的队列称为顺序队(sequential queue)
typedef struct
{
ElemType data[MaxSize];
int front,rear;
}SqQueue;
顺序队列的操作函数
1.初始化队列
构造一个空队列q,将front和rear指针均设置成初始状态,即一1值。算法如下:
void InitQueue(SqQueue*&q)
{
q=(SqQueue*)malloc(sizeof(SqQueue));
q->front=q->rear=-1;
}
2.销毁队列
释放队列q占用的存储空间。算法如下:
void DestroyQueue(SqQueue *&q)
{
free(q);
}
3.判断队列是否为空
若队列q为空,返回真;否则返回假。算法如下:
bool QueueEmpty(SqQueue *&q)
{
return (q->front==q->rear);
}
4.进队列
在队列q不满的条件下先将队尾指针rear增1,然后将元素e插人到该位置。算法如下:
bool enQueue(SqQueue*&q,ElemType e)
{
if(q->rear==MaxSize-1)
return false;
q->rear++;
q->data[q->rear]=e;
return true;
}
5.出队列
在队列q不空的条件下先将队头指针front增1,并将该位置的元素值赋给e。算法如下:
bool deQueue(SqQueue*&q,ElemType&e)
{
if(q->front==q->rear)
return false;
q->front++;
e=q->data[q->front];
return true;
}
环形队列的结构、操作函数
环形队列的结构
在环形队列q中设置队空条件是q->rear=q->front;队满条件是(q->rear 1) MaxSize=q-> front。而进队操作和出队操作改为分别将队尾rear和队头指针front循环进1.
typedef struct
{
ElemType data[MaxSize];
int front,rear;
}SqQueue;
环形队列的操作函数
1.初始化队列
构造一个空队列q,将front和rear指针均设置成初始状态,即0值。算法如下:
void InitQueue(SqQueue*&q)
{
q=(SqQueue*)malloc(sizeof(SqQueue));
q->front=q->rear=0;
}
2.销毁队列
释放队列q占用的存储空间。算法如下:
void DestroyQueue(SqQueue *&q)
{
free(q);
}
若队列q为空,返回真;否则返回假。算法如下:
bool QueueEmpty(SqQueue *&q)
{
return (q->front==q->rear);
}
3.判断队列是否为空
若队列q为空,返回真;否则返回假。算法如下:
bool QueueEmpty(SqQueue *&q)
{
return (q->front==q->rear);
}
4.进队列
在队列q不满的条件下先将队尾指针rear增1,然后将元素e插人到该位置。算法如下:
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;
}
5.出队列
在队列q不空的条件下先将队头指针front增1,并将该位置的元素值赋给e。算法如下:
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;
}
链队列的结构、操作函数
链队列的结构
1.初始化队列
构造一个空队,即创建一个链队节点,其front和rear域均置为NULL.算法如下:
void InitQueue(LinkQuNode*&q)
{
q=(LinkQuNode*)malloc(sizeof(LinkQuNode));
q->front=q->rear=NULL;
}
2.销毁队列
释放链队占用的全部存储空间,包括链队结点和所有数据结点的存储空间。算法如下:
void DestroyQueue(LinkQuNode)
{
DataNode *pre=q->front,*p;
if(pre!=NULL)
{
p=pre->next;
while(p!=NULL)
{
free(pre);
pre-p;
p=p->next;
}
free(pre);
}
free(q)
}
3.判断队列是否为空
若队列为空,返回真;否则返回假。算法如下:
bool QueueEmpty(LinkQuNode*q)
{
return(q->rear==NULL);
}
4.进队列
创建一个新结点用于存放元素e(由p指向它)。若原队列为空,则将链队结点的两个域均咨向结点p,否则将结点p链接到单链表的末尾,并让链队结点的rear域指向它。算法如下:
void enQueue(LinkQuNode*&q,ElemType e)
{
DataNode *p;
p=(DataNode*)malloc(sizeof(DataNode));
p->data=e;
p->next=NULL;
if(q->rear==NULL)
q->front=q->rear=p;
else
{
q->rear->next=p;
q->rear=p;
}
}
5.出队列
若原队列为空,则下溢出返回假;若原队列不空,则将首结点的data域值赋给e,并副除之,若原队列只有一个结点,则需将链队结点的两个域均置为NULL,表示队列已为空。
bool deQueue(LinkQuNode*&q,ElemType&e)
{
DataNode *t;
if(q->rear==NULL)
return false;
t=q->front;
if(q->front==q->rear)
q->front==q->rear==NULL;
else
q->front=q->front->next;
e=t->data;
free(t);
return true;
}
队列应用
pta 6.3
代码如下
int QueueLen(SqQueue Q)//队列长度
{
return Q->rear - Q->front;//初始化时front和rear都在0处
}
int EnQueue(SqQueue& Q, Person e)//加入队列
{
if (Q->rear + 1 == MAXQSIZE)//队满
{
return false;
}
Q->rear = (Q->rear + 1) % MAXQSIZE;//队尾加一
Q->data[Q->rear] = e;//将数据写入队尾
return true;
}
int QueueEmpty(SqQueue& Q)//队列是否为空
{
return (Q->rear == Q->front);
}
int DeQueue(SqQueue& Q, Person& e)//出队列
{
if (Q->front == Q->rear)//判断是否为空队列
{
return false;
}
Q->front++;//队首节点加一
e = Q->data[Q->front];//将第一个数据输出
return true;
}
void DancePartner(Person dancer[], int num) //配对舞伴
{
int i;
Person e;
for (i = 0; i < num; i++)
{
e = dancer[i];
if (e.sex == 'F')
{
EnQueue(Fdancers, e);
}
if (e.sex == 'M')
{
EnQueue(Mdancers, e);
}
}
while (!QueueEmpty(Fdancers) && !QueueEmpty(Mdancers))
{
DeQueue(Fdancers, e);
cout << e.name;
DeQueue(Mdancers, e);
cout << ' ' << e.name;
cout << endl;
}
}
2.PTA实验作业
2.1符号配对
https://gitee.com/z202021123020/zs777/commit/02627105f630054e8ba96e055995fe43a247b0c1
2.1.1解题思路及伪代码
#include <iostream>
#include <stack>
#include <string>
using namespace std;
int main()
{
stack <char> str;//初始化
int i, length, flag = 0;
static char A[1000];
char ch;
i = 0;
scanf("%c", &A[i]);
while (!(i >= 1 && A[i] == '\n'&&A[i - 1] == '.'))//遍历整个输入来计算长度
{
i++;
scanf("%c", &A[i]);
}
length = i - 1;
i = 0;
while (i < length)//开始一一判断
{
switch (A[i])//对左符号进行判断
{
case '/':
if (i + 1 < length&&A[i + 1] == '*')//将整个/*入栈
case'('://将‘(’入栈
case'['://将‘【’入栈
case '{'://将‘{’入栈
case'*':
if (i + 1 < length&&A[i + 1] == '/')//对*/进行判断
{
if (str.empty())//对?-*/判断 如果是flag=2
ch=str.top;
if (ch == '*')//如果匹配就出栈
else//不匹配返回no
break;
case ')'://对‘)’进行判断
if (str.empty())//对?-)判断
ch = str.top();
if (ch == '(')//如果匹配就出栈
else//不匹配返回no
break;
case '}'://对‘}’进行判断
if (str.empty())//对?-}判断
ch = str.top();
if (ch == '{')//如果匹配就出栈
else//不匹配返回no
break;
case ']'://对‘】’进行判断
if (str.empty())//对?-】判断
ch = str.top();
if (ch == '[')//如果匹配就出栈
else//不匹配返回no
break;
default://其他则跳过
break;
}
if (flag == 2)
{
printf("NO\n");
printf("?-*/");
break;
}
else if (flag == 1)
{
printf("NO\n");
if (str.empty())
{
printf("?-%c", A[i]);
}
else
{
if (ch == '*')
{
printf("/*-?");
}
else
{
printf("%c-?", ch);
}
}
break;
}
i++;
}
if (flag == 0 && !str.empty())
{
printf("NO\n");
ch = str.top();
if (ch == '*')
{
printf("/*-?");
}
else
{
printf("%c-?",ch);
}
}
else if (flag == 0)
{
printf("YES\n");
}
}
2.1.2 总结解题所用的知识点
学会运用stack容器,不用单独再自己建一个栈的函数;
2.2 银行业务队列简单模拟
https://gitee.com/z202021123020/zs777/blob/master/7-6 银行业务队列简单模拟
2.2.1 解题思路及伪代码
#include <iostream>
#include <queue>
using namespace std;
int main()
{
int i, n, k;
cin >> k;//输入人数
queue<int> q1;
queue<int> q2;//创建队列
for (i = 0; i < k; i++)//输入编号
{
cin >> n;
if (n % 2)//入奇数的栈
else//入偶数的栈
}
for (i = 0; i < k; i++)
{
if (i == 0)//先对第一个数据输出处理空格
if ((i + 1) % 3)//开始对a窗口处理
{
if (!q1.empty())//判断是否处理完毕,未处理完毕打出a窗口的编号
else//处理完毕直接打出b窗口的所有编号
}
else//对b窗口处理
{
if (!q2.empty())//判断是否处理完毕,未处理完毕打出b窗口的编号
else//处理完毕直接打出b窗口的所有编号
}
}
return 0;
}
2.2.2 总结解题所用的知识点
学会运用queue容器,不用单独再自己建一个队列的函数;
3.阅读代码
3.1 题目及解题代码
class MyQueue {
private:
stack<int> inStack, outStack;
void in2out() {
while (!inStack.empty()) {
outStack.push(inStack.top());
inStack.pop();
}
}
public:
MyQueue() {}
void push(int x) {
inStack.push(x);
}
int pop() {
if (outStack.empty()) {
in2out();
}
int x = outStack.top();
outStack.pop();
return x;
}
int peek() {
if (outStack.empty()) {
in2out();
}
return outStack.top();
}
bool empty() {
return inStack.empty() && outStack.empty();
}
};
3.2 该题的设计思路及伪代码
将一个栈当作输入栈,用于压入 push 传入的数据;另一个栈当作输出栈,用于 pop 和 peek 操作。
每次 pop 或 peek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。
复杂度分析
时间复杂度:push 和 empty 为 O(1),pop 和 peek 为均摊 O(1)。对于每个元素,至多入栈和出栈各两次,故均摊复杂度为O(1)。
空间复杂度:O(n)。其中 n 是操作总数。对于有 n 次 push 操作的情况,队列中会有 n 个元素,故空间复杂度为 O(n)。
3.3 分析该题目解题优势及难点。
利用栈实现队列的功能。
难点:如何实现利用两个栈来实现数据的输入与输出。