DS博客作业02--栈和队列
0.PTA得分截图
1.本周学习总结(0-4分)
1.1.1栈的储存结构及操作
栈:限定仅在表尾(栈顶)进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
在栈中,无论是存数据还是取数据,都必须遵循"先进后出"的原则,即最先进栈的元素最后出栈。
栈的顺序储存结构
顺序栈是用顺序存储结构的栈,可以利用数组来实现其功能
typedef struct {
ElemType data[MaxSize];
int top; //存放栈顶元素在数组中的下标
}SqStack;
也可以通过指针来更加形象的和栈的操作规则想复合
typedef struct{
ElemType *base;
ElemType *top;
int MaxSize; //当前已分配的空间
}SqStack;
顺序栈的基本算法
1.初始化栈
Status InitStack(SqStack &S){
//构造一个空栈
S.base =(ElemType *)malloc(MaxSize*sizeof(ElemType));
if(!S.base) //存储分配失败
exit(0);
S.top=S.base;
S.stacksize = MaxSize;
return 1;
}//InitStack;
课本上的方法
void InitStack(SqStack &s){
s=(SqStack *)malloc(sizeof(SqStack));
s->top = 0;
}
2.进栈Push(SqStack &S,ElemType e);
int Push(SqStack &S,ElemType e){
//插入元素e为新的栈顶元素
if((S.top-S.base) >= S.stacksize){ //栈满,追加储存空间
S.base = (ElemType *)realloc(S.base,
(S.stacksize+STACKINCREASEMENT)*sizeof(ElemType));
if(!S.base)
exit(0); //存储空间分配失败
S.top = S.base +S.stacksize;
//此时stacksize还是原来的值木有变大,指向的是没有增加空间的栈顶
S.stacksize +=STACKINCREASEMENT;
}
*S.top++ = e;
return 1;
}//Push
简化点来说就是
int Push(SqStack &S,ElemType e){
//将数据元素e压入栈顶
if(S.top==MaxSize)
return 0;
S.elem[S.top]=e;
S.top++;
return 1;
}
3.出栈操作pop
int Pop(SqStack &S,ElemType &e){
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回1,否则返回0
if(S.top == S.base)
return 0;
e = *--S.top;
return 1;
}//Pop
4.取栈顶元素GetTop
int GetTop(SqStack &S,ElemType &e){
//若栈不空,则用e返回栈顶元素并返回1,否则返回0
if(S.top == S.base)
return 0;
e = *(S.top - 1);
return 1;
}//GetTOP
5.创建一个空链栈
LinkStack CreateStack()
{
LinkStack *p;
p = new LinkStack; //给p创建空间
p->count = 0;
p->top = NULL;
return p;
}
1.1.2链式存储结构
对于链栈来说,基本不存在栈满的情况,除非内存已经没有使用空间了。
对于空栈来说,链表原来的定义是头指针指向空,那么链栈的空其实就是top=NULL。
typedef struct StackNode
{
int data;//结点数据域
struct StackNode* next;//结点指针域
}StackNode,* Linktop;
//链栈的数据结构
typedef struct LinkStack
{
Linktop top; //栈顶结点,定义了一个指向上个结构体的指针
int count;//元素个数
}LinkStack;
2.进栈操作
//压栈:先将压入元素放入到链表表中,然后再将栈顶指针指向压入的元素,然后count+1.
int push(LinkStack* stack,int e)
{
if (!stack)
{
return 0;
}
StackNode* node = (StackNode*)malloc(sizeof(StackNode));
node->next = stack->top; //将元素加入链表中
node->data = e;
stack->top = node; //栈顶元素指向压入元素
stack->count++;
return 1;
}
3.出栈操作
int pop(LinkStack* stack,int *e)
{
if (!stack && stack->count)
{
return 0;
}
StackNode* node = stack->top;
*e = node->data;
stack->top = node->next; //栈顶指针指向新的栈顶元素
free(node); //释放元素空间
stack->count--;
return 1;
}
4.销毁链栈
void DestroyStack(LinkStack &s)
{
LiStack p=s,q=s->next;
while (q!=NULL)
{ free(p);
p=q;
q=p->next;
}
delete p; //此时p指向尾结点,释放其空间
}
C++中stack容器:使用该容器时需要包含相应的头文件
stack
st.empty(); //判断栈是否为空,若为空,函数返回true;否则返回false
st.top(); //访问栈顶元素
st.pop(); //栈顶元素出栈,出栈操作只是删除栈顶的元素,并不返回该元素
st.push(x); //把元素x入栈
st.size(); //访问栈中的元素个数
栈的应用
判断回文数时可借助栈来实现,初始化两个栈,分别存储最高位到最低位的各位数字以及最低位到最高位的各位数字,最后比对这两个栈是否一样
表达式符号配对时,可借助栈来实现,符合条件的元素入栈后可与其余元素进行配对
浏览网页后要执行回退操作时,使用的数据结构也是栈,遵循着后入先出的原则
1.2队列的存储结构及操作
一、队列(queue)
队列和栈一样,在实际程序的算法设计和计算机一些其他分支里,都有很多重要的应用,比如计算机操作系统对进程 or 作业的优先级调度算法,对离散事件的模拟算法,还有计算机主机和外部设备运行速度不匹配的问题解决等,很多很多。其实队列的本质还是线性表!只不过是一种特殊的或者说是受限的线性表,是这样的:
1)、限定在表的一端插入、另一端删除。 插入的那头就是队尾,删除的那头就是队头。也就是说只能在线性表的表头删除元素,在表尾插入元素。形象的说就是水龙头和水管,流水的水嘴是队头,进水的泵是队尾,管子中间不漏水不进水。这样呲呲的流动起来,想想就是这么个过程。
2)、先进先出 (FIFO结构)。显然我们不能在表(队列)的中间操作元素,只能是在尾部进,在头部出去,还可以类似火车进隧道的过程。(first in first out = FIFO 结构)
typedef struct
{
int data[MAXSIZE];
int front; //队首指针,指向队首元素
int rear; //队尾指针,指向队尾元素
}SqQueue,*Queue;
2.构造一个空队列Q
bool InitQueue(SqQueue &Q)
{
Q = new Queue; //为队列分配一个最大容量为MAXSIZE的数组空间
if (!Q->data) //存储分配失败
{
return false;
}
Q->front = Q->rear = 0; //头指针和尾指针置为零,队列为空
return true;
}
3.循环队列入队操作
bool push(Queue q,int e)
{
//插入到队尾
if((q->rear+1)%MAXSIZE==q->front)
{
return false;
}
q->data[q->rear]=e;
q->rear=(q->rear+1)%MAXSIZE;
return true;
}
3.循环队列出队操作
bool pop(Queue q)
{
if(q->front==q->rear) //队列为空,返回false
{
return false;
}
q->front =(q->front+1)%MAXSIZE;
return true;
}
4.队列的链式存储结构
int data; //结点数据域
struct QNode *next; //结点指针域
} QNode,*QueuePtr;
typedef struct {
QueuePtr front,rear; //队首、队尾指针
}LinkQueue;
5.链式队列入队操作
bool EnQueue(LinkQueue &Q,int e)
{
QueuePtr temp;
temp=new QNode;
temp->data=e;
temp->next=NULL;
Q.rear->next=temp; //尾插法插入节点
Q.rear=temp;
return true;
}
6.链式队列出队操作
bool DeQueue(LinkQueue &Q,int &e)
{
QueuePtr p;
if(Q.front==Q.rear)
{
return false;
}
p=Q.front->next;
e=p->data;
Q.front->next=p->next; //修改头结点的指针域
if(Q.rear==p) Q.rear=Q.front; //如果最后一个元素被删,队尾指针指向头结点
delete p; //释放原队头元素的空间
return true;
}
C++中queue容器:使用该容器时需要包含相应的头文件
queue
queue.empty(); //判断队列是否为空,若为空,函数返回true;否则返回false
queue.pop(); //删除队列中的队头元素
queue.front(); //访问队列的队头元素
queue.back(); //访问队列的队尾元素
queue.push(x); //把元素x插入到队列的尾部
queue.size(); //访问队列中元素的个数
1.2.谈谈你对栈和队列的认识及学习体会。
有时候感觉自己掌握了,栈和队列,可是在写的时候会遇到不同的情况,就不会处理了
明白了栈的定义,现在要实际的实际,首先是它的逻辑结构:线性表。它是线性的。
现在是它的存储结构:最常采用的是顺序存储和链式存储(见好多书或资料都说的最常采用的是顺序存储和链式存储,百度了下不常见的没有找到)。其中顺序存储用数组,链式存储用链表
对我来说,栈和队列,其实很相似,只不过是控制的位置不同。
队列:是一种限定性的线性表。这样理解比较好,学生排队买饭。有什么特点呢?当然,你先来,就先打饭,先吃饭。抽象到队列上说,有队头,队尾,要想加入(入队),只能从队尾加,想走(出队),只能从队头走。即:先进先出。
和栈一样,它常见的两种存储是顺序存储和链式存储。
用顺序存储时,会遇到这样的情况,数组并没有满,却入不了队(假溢出),原因在于队头没有在数组的0下标处。一般情况下,因为队列会存在假溢出的情况,所以采用循环队列。
2.PTA实验作业(0-2分)
选2道PTA题目,不写伪代码,只贴代码截图,并记录代码提交碰到问题及解决方法。不得选栈或队列操作(选,则为0分)选择难度越高题目得分越高。
2.1.题目1:7-3 jmu-ds-符号配对
2.1.1代码截图(注意,截图,截图,截图。不要粘贴博客上。)
2.1.2本题PTA提交列表说明。
PTA提交列表中的每个错误详细说明为什么及如何解决。
2.2 题目一开始是第二和第三个测试点过不去,我先是在主函数中若返回值为no则cout<<topc<<”no”但是返回值为no时有两种情况,所以不能这么做,就改为cout<<”no”在函数IsMatch
中进行操作
但是第二个测试点还是过不去,因为我认为即使栈不空栈顶元素是可以不存在的,因为栈顶元素出栈了,所以不存在了,之后看了老师的课件,结合stack.top()函数的实验才纠正了错误
2表达式转换
2.1.1代码截图(注意,截图,截图,截图。不要粘贴博客上。)
PTA提交列表中的每个错误详细说明为什么及如何解决。
其实这题我是很懵的,我注意了结尾空格,但是还是过不去测试点,说格式错误,其实这题无非就是找特殊情况带入代码进行调试((2+3)4-(8+2))/5 1314+25.512 -2*(+3)分别为嵌套括号,小数和负数,小数的改发即为把小数点也当作是数字存入
而带符号的表达式就是判断连续出现两个符号
3.阅读代码(0--4分)
3.1 栈的压入、弹出序列
3.1.1 该题的设计思路
先把pushed序列元素入栈,如果栈顶元素和popped序列元素相同,则持续抛出。最后如果栈为空,则为true,否则为false
算法的时间复杂度和空间复杂度:令pushed.size()=n,则时间复杂度T(n)=O(n)。空间复杂度S(n)=O(n)
代码如下
bool validateStackSequences(vector<int>& pushed, vector<int>& popped)
{
stack<int> st;
int j = 0;
for(int i=0;i<pushed.size();i++)
{
st.push(pushed[i]);
while(st.size() && st.top()==popped[j])
{
st.pop();
j++;
}
}
return st.size()==0;
}
3.1.2 该题的伪代码
初始化栈st
for (定义变量i实现对pushed序列的遍历)
{
pushed[i]元素入栈
while (栈不空且栈顶元素和popped序列元素相同)
{
栈顶元素出栈
popped序列指向下一元素
}
end while
}
若栈为空,返回true;否则返回false
3.1.3 运行结果
3.1.4分析该题目解题优势及难点。
该算法借助栈,在遍历pushed序列的过程中不断把其中元素入栈,再分别与popped序列的元素对比,若相等,则弹出栈顶元素,关键是判断最后栈是否为空。注意算法中用的容器是vector,而进行vector操作前应添加相应的头文件
3.2.1题目:列车调度
3.2.1 该题的设计思路
代码如下:
我的思路是既然火车最后要按次序输出,那么我们先模拟一个极端情况。
刚开始即有一堆有序的火车。
1 2 3 4 5 6 7 8 9
那么只需占用一条轨道即可。
稍微打乱一点后。
2 1 3 4 5 6 7 8 9
前7个数字过去没问题,到1的时候就要等一等,因为后面有比1大的数字2,而2应排在1的前面。
2此时便要独占一条新的轨道,而让它能走在1的前面。最终需要2条轨道。
再打乱一点后。
3 2 1 4 5 6 7 8 9
到1的时候我们知道要给2新开一条轨道,那3呢?显然,2还要再等一等3。所以又要给3新开一条轨道,此时在等3的数字有1和2,我们发现1和2此时都是各自轨道的末尾,新进入的3也占据一个轨道末尾。即所有需要特殊对待的数字都在轨道的末端,因为前面的数字的问题已被解决。