0.PTA得分截图
1.本周学习总结
1.1 总结栈和队列内容
一.栈:
栈是一种只能在栈顶一端进行插入或删除操作的线性表。
栈中元素遵循先进后出的规则。元素未完全进栈时即可出栈
栈的存储结构:
1.顺序栈:
- 顺序栈结构体定义:
typedef struct
{ ElemType data[MaxSize]; //栈中数据元素
int top; //top为栈顶指针
} Stack;
typedef Stack *SqStack;
-
顺序栈四大要素:
1.栈空条件:top=-1
2.栈满条件:top=MaxSize-1
3.进栈e操作:top++; st->data[top]=e
4.退栈操作:e=st->data[top]; top--; -
顺序栈基本操作
1.初始化栈:
void CreateStack(SqStack& s)
{
s = new Stack;
s->top = -1;
}
2.判断栈空:
int EmptyStack(SqStack s)
{
if (s->top == -1)
return 1;
else
return 0;
}
3.判断栈满:
int FullStack(SqStack s)
{
if (s->top == MaxSize-1)
return 1;
else
return 0;
}
4.元素入栈:
bool Push(SqStack& s, ElemType e)
{
if (s->top == MaxSize - 1)
cout << "FULLSTACK";
return false;
s->top++; //栈顶指针增1
s->data[s->top] = e;
return true;
}
5.元素出栈:
bool Pop(SqStack &s,ElemType &e)
{
if (s->top==-1) //栈空的情况
return false;
e=s->data[s->top];
s->top--;
return true;
}
6.取栈顶:
bool GetTop(SqStack *s,ElemType &e)
{
if (s->top==-1) //判断栈空
return false;
e=s->data[s->top];//栈顶元素赋值为e
return true;
}
7.销毁栈:
void ClearStack(SqStack& s)
{
delete s;
}
8.共享栈:
*共享栈类型定义:
typedef struct
{ ElemType data[MaxSize]; //存放共享栈中元素
int top1,top2; //两个栈的栈顶指针
} DStack;
*共享栈结构体定义:
struct SNode {
ElementType *Data;
Position Top1, Top2;
int MaxSize;
};
typedef struct SNode *Stack;
*共享栈三要素:
1.栈1空:top1-1
2.栈2空:top2MaxSize
3.栈满:top1+1=top2
2.链式栈(带头节点):
- 链式栈的结构体定义:
typedef struct linknode
{ ElemType data; //数据域
struct linknode *next; //指针域
} LiNode,*LiStack;
-
链式栈四大要素:
1.栈空条件:s->next=NULL
2.栈满条件:不考虑
3.进栈e操作:结点插入到头结点后,链表头插法
4.退栈操作:取出头结点之后结点的元素并删除 -
链式栈基本操作:
1.初始化栈:
void InitStack(LiStack &s)
{ s=new LiNode;
s->next=NULL;
}
2.判断栈空:
int StackEmpty(LiStack s)
{
if (s->next == NULL)
return 1;
else
return 0;
}
3.元素入栈:
void Push(LiStack& s, ElemType e)
{
LiStack p;
p = new LiNode;
p->data = e; //新建节点p
p->next = s->next; //插入*p节点作为开始节点
s->next = p;
}
4.元素出栈:
bool Pop(LiStack& s, ElemType& e)
{
LiStack p;
if (s->next == NULL) //栈空的情况
return false;
p = s->next; //p指向开始节点,从栈顶开始出栈
e = p->data;
s->next = p->next; //删除*p节点
free(p); //释放*p节点
return true;
}
5.取栈顶元素:
bool GetTop(LiStack s,ElemType &e)
{ if (s->next==NULL) //栈空的情况
return false;
e=s->next->data;
return true;
}
6.销毁栈:
void DestroyStack(LiStack &s)
{ LiStack p;
while (s!=NULL)
{ p=s;
s=s->next;
free(p);
}
}
栈的应用:
C++模板类:stack类
1.stack
2.s.push(t):入栈元素t
3.s.top():返回栈顶元素
4.s.pop():出栈操作只是删除栈顶元素(彻底删除),并不返回该元素。
5.s1.empty(),当栈空时,返回true。
6.s1.size():访问栈中的元素个数
栈的应用
1.中缀表达式转后缀表达式(符号简化)
while (从exp读取字符ch,ch!='\0')
{ ch为数字:将后续的所有数字均依次存放到postexp中,
并以字符'#'标志数值串结束;
ch为左括号'(':将此括号进栈到Optr中;
ch为右括号')':将Optr中出栈时遇到的第一个左括号'('以前的运算符
依次出栈并存放到postexp中,然后将左括号'('出栈;
ch为'+'或'-':
出栈运算符并存放到postexp中,直到栈空或者栈顶为'(',
然后将'+'或'-'进栈;
ch为'*'或'/':
若 栈顶为'*'或'/',出栈,直到栈顶不是'*'或'/'
否则 入栈
}
若exp扫描完毕,则将Optr中所有运算符依次出栈并存放到postexp中。
2.后缀表达式求值:
while (从postexp读取字符ch,ch!='\0')
{ 若ch为数字,将后续所有数字构成一个整数存放数值栈st中。
若ch为“+”,则从数值栈st中退栈两个运数,相加后进栈st中。
若ch为“-”,则从数值栈st中退栈两个数,相减后进栈st中。
若ch为“*”,则从数值栈st中退栈两个数,相乘后进栈st中。
若ch为“/”,则从数值栈st中退栈两个数,相除后进栈st中
(若除数为零,则提示相应的错误信息)。}
若字符串postexp扫描完毕,则数值栈op中的栈顶元素就是表达式的值。
3.迷宫问题:
初始化栈st,入口方块(x1,y1)入栈
//可走方位左3右1,上0下2; bi
//方块(x,y)->(i,j)行号和列号
while 栈不为空
遍历取栈顶
if 栈顶是出口
输出栈中内容
end if
bi初始化为-1;
while 未走遍相邻方块且未找到可行下一步
bi++;
if bi为0 向上走;
else if bi为1 向右走;
else if bi为2 向下走;
else bi为3 向左走;
end if
end while
if 方块可行
记录寻找可行方块的路径存入栈中
else 退栈返回上一个位置并将现方块列为可行方块
end if
end while
二.队列:
队列是只允许在表的一端进行插入,而在表的另一端进行删除的线性表。
队列中元素遵循先进先出的原则
队列的存储结构:
1.顺序队列:
- 顺序队列结构体定义:
typedef struct
{ ElemType data[MaxSize];
int front,rear; //队首和队尾指针
}Queue;
typedef Queue *SqQueue;
-
顺序队列四大要素:
1.队空条件:front = rear(初始时front = rear=-1)
2.队满条件:rear=MaxSize-1
3.元素e进队:rear++;data[rear]=e;
4.元素e出队:front++;e=data[front]; -
顺序队列基本操作
1.初始化顺序队列:
void InitQueue(SqQueue &q)
{ q=new Queue;
q->front=q->rear=-1;
}
2.判断队列是否为空:
bool QueueEmpty(SqQueue q)
{
return(q->front==q->rear);
}
3.判断队列是否为满:
bool QueueFULL(SqQueue q)
{
return(q->rear==MaxSize-1);
}
4.进队列:
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;
}
5.出队列:
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;
}
6.销毁队列:
void DestroyQueue(SqQueue &q)
{
delete q;
}
- 为了解决队列元素假溢出的问题,我们引进循环队列的做法:
循环队列:
把数组的前端和后端连接起来,形成一个环形的顺序表,即把存储队列元素的表从逻辑上看成一个环,称为环形队列或循环队列。
- 循环队列结构体定义
typedef struct
{
ElemType data[MaxSize];
int front,rear;
} Queue;
typedef Queue *SqQueue;
-
循环队列两要素:
1.队空:frontrear
2.队满:(rear+1)%Mfront -
循环队列基本操作
1.初始化队列:
void InitQueue(SqQueue &q)
{ q=new Queue;
q->front=q->rear=0;
}
2.判断队列是否为空:
bool QueueEmpty(SqQueue q)
{
return(q->front==q->rear);
}
3.判断队列是否为满:
bool QueueFULL(SqQueue q)
{
return((q->rear+1)%MaxSize==q->front);
}
4.入队列:
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.出队列:
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;
}
6.销毁队列:
void DestroyQueue(SqQueue &q)
{
delete q;
}
7.求循环队列长度:
int QueueLength (SqQueue Q)
{
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
}
链式队列:
- 链式队列结构体定义:
typedef struct QNode{
QElemType data;
struct Qnode *next;
}Qnode, *QueuePtr;//定义节点类型
typedef struct {
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LinkQueue; //定义队列类型
-
链队列四要素:
1.队空条件:front=rear
2.队满条件:不考虑
3.进队e操作:将包含e的节点插入到单链表表尾
4.出队操作:删除单链表首数据节点 -
链队列基本操作:
1.初始化队列:
Status InitQueue (LinkQueue &Q)
{
Q.front=Q.rear=new QNode;
if(!Q.front) exit(OVERFLOW);
Q.front->next=NULL;
return OK;
}
2.判断队列是否为空:
Status QueueEmpty(LinkQueue Q)
{
return (Q.front == Q.rear);
}
3.入队:
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;
}
4.出队:
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;
}
5.取链队列的队头元素:
Status GetHead(LinkQueue Q, QElemType& e)
{
if (Q.front == Q.rear) return ERROR;
e = Q.front->next->data;
return OK;
}
6.销毁队列:
Status DestroyQueue(LinkQueue& Q)
{
while (Q.front)
{
Q.rear = Q.front->next;
free(Q.front);
Q.front = Q.rear;
}
return OK;
}
队列的应用:
C++容器:queue
q1.push(x): 将x接到队列的末端。
q1.pop():弹出队列的第一个元素,并不会返回被弹出元素的值。(front后移时队列中元素并未完全消失)
q1.front():即最早被压入队列的元素。
q1.back():即最后被压入队列的元素。
q1.empty():当队列空时,返回true。
q1.size():访问队列中的元素个数
队列的应用:
1.舞伴问题:
int QueueLen(SqQueue Q)//队列长度
{
因为是顺序队列,所以队列长度为队尾减队首
返回Q->rear - Q->front;
}
int EnQueue(SqQueue& Q, Person e)//加入队列
{
if 队满
返回0
else
元素e入队,队尾后移
}
int QueueEmpty(SqQueue& Q)//队列是否为空
{
直接返回队首队尾相等比较的结果
return Q->front == Q->rear;
}
int DeQueue(SqQueue& Q, Person& e)//出队列
{
if 队空
返回0
else
元素e出队,队首后移
}
void DancePartner(Person dancer[], int num) //配对舞伴
{
int k;
Person e;
for k = 0 to k=num-1
if 字符数组元素为男
元素入Fdancers队
else 字符数组元素为女
元素入Mdancers队
end if
end for
while 男队女队都不空
输出男女配对舞伴
end while
}
2.迷宫问题:
#include <stdio.h>
#define MaxSize 100
#define M 8
#define N 8
int mg[M+2][N+2]=
{
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
typedef struct
{ int i,j; //方块的位置
int pre; //本路径中上一方块在队列中的下标
} Box; //方块类型
typedef struct
{
Box data[MaxSize];
int front,rear; //队头指针和队尾指针
} QuType; //定义顺序队类型
void print(QuType qu,int front) //从队列qu中输出路径
{
int k=front,j,ns=0;
printf("\n");
do //反向找到最短路径,将该路径上的方块的pre成员设置成-1
{ j=k;
k=qu.data[k].pre;
qu.data[j].pre=-1;
} while (k!=0);
printf("迷宫路径如下:\n");
k=0;
while (k<MaxSize) //正向搜索到pre为-1的方块,即构成正向的路径
{ if (qu.data[k].pre==-1)
{ ns++;
printf("\t(%d,%d)",qu.data[k].i,qu.data[k].j);
if (ns%5==0) printf("\n"); //每输出每5个方块后换一行
}
k++;
}
printf("\n");
}
int mgpath(int xi,int yi,int xe,int ye) //搜索路径为:(xi,yi)->(xe,ye)
{
int i,j,find=0,di;
QuType qu; //定义顺序队
qu.front=qu.rear=-1;
qu.rear++;
qu.data[qu.rear].i=xi; qu.data[qu.rear].j=yi; //(xi,yi)进队
qu.data[qu.rear].pre=-1;
mg[xi][yi]=-1; //将其赋值-1,以避免回过来重复搜索
while (qu.front!=qu.rear && !find) //队列不为空且未找到路径时循环
{
qu.front++; //出队,由于不是环形队列,该出队元素仍在队列中
i=qu.data[qu.front].i; j=qu.data[qu.front].j;
if (i==xe && j==ye) //找到了出口,输出路径
{
find=1;
print(qu,qu.front); //调用print函数输出路径
return(1); //找到一条路径时返回1
}
for (di=0;di<4;di++) //循环扫描每个方位,把每个可走的方块插入队列中
{
switch(di)
{
case 0:i=qu.data[qu.front].i-1; j=qu.data[qu.front].j;break;
case 1:i=qu.data[qu.front].i; j=qu.data[qu.front].j+1;break;
case 2:i=qu.data[qu.front].i+1; j=qu.data[qu.front].j;break;
case 3:i=qu.data[qu.front].i, j=qu.data[qu.front].j-1;break;
}
if (mg[i][j]==0)
{ qu.rear++; //将该相邻方块插入到队列中
qu.data[qu.rear].i=i; qu.data[qu.rear].j=j;
qu.data[qu.rear].pre=qu.front; //指向路径中上一个方块的下标
mg[i][j]=-1; //将其赋值-1,以避免回过来重复搜索
}
}
}
return(0); //未找到一条路径时返回1
}
int main()
{
mgpath(1,1,M,N);
return 1;
}
3.报数问题:
数据结构:
结构体定义:
int型在队首元素前一位的front;
int型队尾rear;
整型数组Data[最大队列容量MaxSize];
顺序队列Queue;
创建新队列,数据入队
while 队列不为空
//此时front在-1的位置
while 遍历队列
//取出输出队首,front后移一位
cout << Queue->data[front + 1] << " ";
front++;
//取出偶数位置的数字再入队放在最后
e = Queue->data[front + 1];
rear++;
Queue->data[rear]=e;
end while
end while
1.2.谈谈你对栈和队列的认识及学习体会。
栈 | 队列 |
---|---|
先进后出 | 先进先出 |
出栈元素在栈中完全删除 | 出队元素在队中不完全删除 |
只能在一端插入删除 | 在队头删除队尾插入 |
线性结构 | 线性结构 |
插入删除时间复杂度O(1) | 插入删除时间复杂度O(1) |
栈和队列虽然原理不同,但都是线性结构存储数据的一种特殊计算方法,为表达式转换等问题提供了简便的做法。在学习栈和队列的过程中,用C++容器真的带给了我巨大的便利,在做题的时候感觉理解题意很难,有挺多测试点过不去,还是要拜托对搜索引擎的依赖,反思自己为什么不会做相对缜密的题。
2.PTA实验作业
2.1.题目1:7-7 银行业务队列简单模拟
2.1.1代码截图
2.1.2本题PTA提交列表说明。
Q1:在队列A判断完又进行了一次flag初始化
A1:在开始flag初始化一次即可
Q2:队列B没有判断空格的问题,忘记了顾客编号都为偶数的情况
A2:在队列B中也进行数字后空格的判断,防止有顾客编号都为偶数的情况出现。
2.2.题目2:6-5 jmu-ds-舞伴问题
2.2.1代码截图
2.2.2本题PTA提交列表说明。
Q1:计算队列长度时把队列当成可变的了
A1:用固定队列队尾减队首就可以求出队列长度了
Q2:在最后输出配对舞伴时两人之间的空格数搞错了
A2:把输出一个空格改为输出两个空格
3.阅读代码
3.1 题目及解题代码
题目:来源:力扣(LeetCode)
解题代码:
public class Solution {
public boolean find132pattern(int[] nums) {
if (nums.length < 3)
return false;
Stack < Integer > stack = new Stack < > ();
int[] min = new int[nums.length];
min[0] = nums[0];
for (int i = 1; i < nums.length; i++)
min[i] = Math.min(min[i - 1], nums[i]);
for (int j = nums.length - 1; j >= 0; j--) {
if (nums[j] > min[j]) {
while (!stack.isEmpty() && stack.peek() <= min[j])
stack.pop();
if (!stack.isEmpty() && stack.peek() < nums[j])
return true;
stack.push(nums[j]);
}
}
return false;
}
}
3.1.1 该题的设计思路
时间复杂度:O(N)
空间复杂度:O(N)
3.1.2 该题的伪代码
public class Solution {
public boolean find132pattern(int[] nums) {
if 数组元素少于3个
return false;
end if
Stack < Integer > stack = new Stack < >();//定义栈
定义最小数数组min[]来储存啊a[i]
min[0] = nums[0];//min数组初始化
for i = 1 to nums.length-1
min[i] = Math.min(min[i - 1], nums[i]);//求j点左侧最小的a[i]
end for
for j = nums.length - 1 to 0
if a[j]大于最小a[i]
while 栈非空且栈顶元素小于a[i]
栈顶出栈
end while
if 栈非空且栈顶元素小于a[j]
return true;
end if
a[j]入栈
end if
end for
return false;
}
}
3.1.3 运行结果
3.1.4 该题目解题优势及难点。
*优势:
分部进行比较,先比较a[i]<a[j],先假设定义一个j,寻找j左侧最小的a[i];寻找到最优a[i]后从队尾到a[j]寻找a[k],将所有a[k]放入栈中让它成栈底最大栈顶最小的倒序,a[k]<a[j]的栈顶不合理,pop出栈,寻找到a[k]>a[i],再去比较a[k]<a[j],如果可以匹配就说明有“132”模式的子序列。
*难点:
a[j]的比较寻找,因为j是不确定的,只能边移边找,分部比较也挺难理解;
3.2 题目及解题代码
题目:来源:力扣(LeetCode)
解题代码:
class CQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
int size;
public CQueue() {
stack1 = new Stack<Integer>();
stack2 = new Stack<Integer>();
size = 0;
}
public void appendTail(int value) {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
stack1.push(value);
while (!stack2.isEmpty()) {
stack1.push(stack2.pop());
}
size++;
}
public int deleteHead() {
if (size == 0) {
return -1;
}
size--;
return stack1.pop();
}
}
3.2.1 该题的设计思路
插入元素:
时间复杂度:O(n)。插入元素时,对于已有元素,每个元素都要弹出栈两次,压入栈两次,因此是线性时间复杂度。
空间复杂度:O(n)。需要使用额外的空间存储已有元素。
删除元素:
时间复杂度:O(1)。判断元素个数和删除队列头部元素都使用常数时间。
空间复杂度:O(1)。从第一个栈弹出一个元素,使用常数空间。
3.2.2 该题的伪代码
class CQueue {
Stack<Integer> stack1;//第一个栈
Stack<Integer> stack2;//辅助栈
int size;//队列元素数
public CQueue() {
stack1初始化
stack2初始化
size = 0;//队列元素数开始为0
}
public void appendTail(int value) {
while stack1不为空
将stack1中新插入的元素push进stack2中
end while
新元素value入栈stack1
while stack2非空
将stack2中的元素再全部返回stack1中
end while
size++;
}
public int deleteHead() {
if 队列为空
return -1;
end if
队列元素-1
return stack1.pop();
}
}
3.2.3 运行结果
3.2.4 该题目解题优势及难点
*优势:
它是用第一个栈来储存新插入的元素,然后将第一个栈中的元素放入第二个栈中,最后把第二个栈中的元素全部返回到第一个栈中,使新插入的元素成为栈底,其他的元素顺序和输入的时候相同,符合队列的规则。
*难点:
第二个栈给第一个栈做辅助栈,两个栈之间的元素倒换很麻烦。