栈和队列

这个作业属于哪个班级 数据结构--网络2012
这个作业的地址 DS博客作业02--栈和队列
这个作业的目标 学习栈和队列的结构设计及运算操作
姓名 李兴果

0.PTA得分截图

1.本周学习总结(0-5分)

1.1 栈

画一个栈的图形,介绍如下内容:

(1)顺序栈的结构、操作函数
顺序栈:

栈空条件:s->top==-1;
栈满条件:s->top==MaxSize-1;
进栈操作:s->top++;s->data[s->top]=e;
出栈操作:e=s->data[s->top];s->top--;

共享栈:

栈空条件:top1==-1;top2==MaxSize;
栈满条件:top1==top2-1;
进栈操作:top++;data[top1]=x; top2--;data[top2]=x;
出栈操作:x=data[top1];top1--; x=data[top2];top2++; 

数据类型:

typedef struct
{
	ElemType data[MaxSize];//存放栈中元素
	int top;//栈顶指针,存放栈顶元素下标
}SqStack;

初始化栈:

void InitStack(SqStack*&s)
{
	s = (SqStack*)malloc(sizeof(SqStack));//分配顺序栈空间,首地址存放在s中
	s->top = -1;//栈顶置-1处
}

销毁栈:

void DestoryStack(SqStack*&s)
{
	free(s);
}

判断是否为空:

void StackEmpty(SqStack*s)
{
	return (s->top == -1);
}

进栈:

bool Push(SqStack*& s, ElemType e)
{
	if (s->top == MaxSize - 1)//栈满
		return false;
	s->top++;//向上移动一个位置
	s->data[s->top] = e;//放在顶指针处
	return true;
}

出栈:

bool Pop(SqStack*& s, ElemType e)
{
	if (s->top == - 1)//栈空
		return false;
      e=s->data[s->top] ;//取栈顶元素
	s->top--;//移动
	return true;
}

取栈顶元素:

bool GetTop(SqStack*& s, ElemType e)
{
if (s->top == - 1)//栈空
		return false;
    e=s->data[s->top] ;//取栈顶元素
     return true;
}

(2)链栈的结构、操作函数
条件:

栈空条件:s->next==NULL;
栈满条件:无栈满
进栈操作:p->next=e;
    p->next=s->next;//p结点插入作为首节点
    s->next=p;
出栈操作: p=s->next;//p指向首结点
  e=p->data;//取首结点值
   s->next=p->next;//删除首结点
free(p);

类型:

typedef struct linknode
{
Elem Type data;//数据域
struct linknode;//指针域
}LinkStNode;//结点类型

初始化栈:

void IniStack(LiStack &s)
{
   s=new LiNode;//新建头结点
    s->next=NULL;//初始化为空
}             

销毁链栈:

void DestroyStack(LiStack *&s)
{
LiStack node;
while(s!=NULL)
{
   node=s;
    s=s->next;//遍历
  delete node;
}
}  

判断栈是否为空:

bool StackEmpty(LiStack* s)
{
return (s->next==NULL);
}              

进栈:

void Push(LiStack*& s, ElemType *e)
{
  LiStack p;
    p=new LiNode;//新建结点
   p->next=e;
   p->next=s->next;//p结点插入作为首节点
   s->next=p;
}

出栈:

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;//删除首结点
free(p);
return true;
}

取栈顶元素:

bool GetTop(LiStack*& s, ElemType& e)
{
if(s->next==NULL)
  return false;
  e=s->next->data;
return false;
}

1.2 栈的应用

表达式

  • 中缀表达式
    例如:1+3*3 最常用的表达式
  • 后缀表达式
    例如:1+23的后缀表达式为 123+ 没有括号 已经考虑了运算符号的优先级 越放在前面的运算符越优先执行
  • 前缀表达式
    例如: 1+23的前缀 +123

(1)中缀转后缀表达式
转换时需要从左到右扫描算数表达式,遇到操作数直接存放到后缀表达式
1.优先级比栈顶运算符高入栈
2.低或相等,一直出栈到栈顶为空或者更高,写入后缀表达式

读入数时,将其放入到输出中,操作符不立即输出,将其放入栈中
如果我们见到一个右括号,那么我们就从栈中弹出栈元素直至遇到一个左括号,左右括号都只被弹出而不输出
如果见到任何其他的符号+ * (,那么从栈中弹出栈元素,直至发现优先级更低的元素为止, 但是有一个例外: 除非是在处理一个)的时候,否则绝不从栈中移除(,+的优先级最低,(的优先级最高,当从栈中弹出元素的工作完成后,我们再将其操作符压入栈中。
最后,如果读到输入的末尾,我们将栈元素弹出直至该栈变成空栈,将符号写到输出中
(2)举例:

输入为a + b * c + (d * e + f)*g
 读a,输出
读到+,入栈
到b,输出
到*,因为*优先级比+高,将至压入栈中
到+,因为*高于+,弹出*,栈中下一个元素+ 与当前+一样,输出栈中这个+,再将刚刚的+入栈
读到(,优先级最高,入栈
读到*,因为只有遇到)时才会弹出,所以*继续压入栈中
读到e,输出
读到+,弹出*并输出,+压入
到f输出
读到)后,将到(之间元素全部输出
到* 压入
到g输出
到末尾后,栈中还剩* +  直接输出

1.3 队列

画一个队列的图形,介绍如下内容

(1)顺序队列的结构、操作函数

条件

队空条件:q->front==q->rear;
队满条件:q->raer=MaxSize-1;
进队操作:q->rear++;q->data[q->rear]=e;
出队操作:q->front++;e=q->data[q->front];

数据类型:

typedef struct
{
Elem Type data[MaxSize];
int front,rear;//头尾
}SqQueue;

初始化队列:

void InitQueue(SqQueue &q)
(  q=new Queue;
   q->front=q->rear=-1;//初始位置为-1
}

销毁队列:

    void Destory(SqQueue &q) 
{
  delete q;
}   

判断队列是否为空:

bool QueueEmpty(SqQueue q)
{
return (q->front==q->rear);
}   

进队列:

bool enQueue(SqQueue &q,ElemType e)
{
//此时下标从-1开始,先增一再入值
  if(q->rear+1==MaxSize)
    return false;
   q->rear=q->rear+1;//队尾加1
   q->data[q->rear]=e;//rear插入元素e
    return true;
}  

出队列:

booldenQueue(SqQueue &q,ElemType e)
{
if(q->front==q->rear)
  return false;
    q->front=q->front+1;
    e=q->data[q->data];
return true;

(2)环形队列的结构、操作函数

初始化:

void InitQueue(SqQueue &q)
(  q=new Queue;
   q->front=q->rear=0;//开始时在同一位置当存入数时。front指向数的前一个位置,rear在尾部位置
}

销毁队列:

    void Destory(SqQueue &q) 
{
  delete q;
}   

判断队列是否为空:

bool QueueEmpty(SqQueue q)
{
return (q->front==q->rear);
}   

进队列:

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;
}  

出队列:

booldenQueue(SqQueue &q,ElemType e)
{
if(q->front==q->rear)//空队时
  return false;
    e=q->data[q->front];//取值
  q->front=(q->front+1)%MaxSize;//循环增一
return true;

(3)链队列的结构、操作函数

条件:

队空条件:q->rear==NULL;
队满条件:不考虑
进队操作:q->data=e;p->rear->data=q;p->next=q;
出队操作:p->front=p->front->next;e=q->data;

数据类型:

typedef struct
{
    QNode *front;//头指针
   QNode *rear;//尾指针
}LinkQueue;
typedef struct qnode
{
   Elem data;//数据元素
  struct qnode *next;
}QNode;

链队初始化:

Status InitQueue(LinkQueue &Q)
{
Q.front=Q.rear=new QNode;
if(!Q.front)
exit(OVERFLOW);
Q.front->next=NULL;
return OK;
}

或者

void InitQueue(LinkQuNode *&q)
{
    q=(LinkQueNode *)malloc(sizeof(LinkQueNode));//创建链队结点
    q->front=q->reaar=NULL;
}

判断链队是否为空:

Status QueueEmpty(LinkQueue Q)
{
return (Q.front==Q.rear)//队空
}

求链队队头元素:

Status GetQueue (LinkQueue Q,QElemType &e)
{
    if(Q.front==Q.rear)
        retuen ERROR;
     e=Q.front->next->data;//取元素
    return OK;
}

链队列入队:

Status EnQueue (LinkQueue &Q,QElemType e)
{
       p=new QNode;//新建结点
     if(!p)
    exit(OVERFLOW)
    P->data=e;
   p->next=NULL;
      Q.rear->next=p;//尾指针指向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;//结点指向p下一个
  if(Q.rear==p)//最后一个元素被删除,改队尾
     Q.rear=Q.front;
    delete p;//删除结点p
   return OK;
}        

(4)队列应用,要有具体代码操作。

  • 求解报数问题
 1.问题描述:
 n人站成一排,数1 2 1 2...数1的同学出列,2
的同学靠右直到第n个人出列
 2.数据组织:
  使用环形队列
 3.设计运算算法:
 先将n个人编号进队,反复执行以下操作,直到队列为空
 出队一个元素  输出其编号
 若队列不为空,再出队一个元素,将刚出列的元素入队
void number(int n)
{
	int i;
	ElemType e;
	SqQueue* q;
	InitQueue(q);//初始化
	for (i = 1; i <= n; i++)
		enQueue(q, i);//进队
	while (!QueueEmpty(q))
	{
		deQueue(q, e);//出
		if (!!QueueEmpty(q))
		{
			deQueue(q, e);
			enQueue(q, e);//将刚出的进队
		}
	}
	DestoryQueue(q);//销毁
}

(2)求解迷宫问题
使用链队解决
1.数据组织

//使用一个顺序队qu保存走过的方块
typedef struct
{
	int i, j;//方块的位置
	int pre;//本路径中上一方块在队列中的下标
}Box;
typedef struct
{
	Box data[MaxSize];
	int front, rear;//队头队尾指针
}QuType;
// 此处qu不是环形队列,找出口时需要利用队列中所有方块找一条迷宫路径,

2.设计运算算法

搜索路径(xi,yi)->(xe,ye)从入口进队,队不为空时循环,出队一个方块,找其相邻方块,使其进队,将他们的pre都置为front



2.PTA实验作业(4分)

2.1 符号配对

2.1.1 解题思路及伪代码
从头遍历:
1.遇到左符号:入栈;
2.遇到右符号:若此时栈空则表示当前右符号缺少与之匹配的左符号,跳出循环;栈不空,则与栈顶元素进行配对,若配对成功则栈顶符号出栈,否则跳出循环。
遍历结束后,若栈空(除上述栈空的情况外)则说明全部符号均配对成功




2.1.2 总结解题所用的知识点
注意第一个用例中/*/的判断,并且注意右字符与栈内字符不匹配时的情况。 右字符与栈内字符不匹配时有两种:栈内为空,则此右字符缺失左字符;栈不为空,则栈内左字符缺失右字符;

2.2 银行业务队列简单模拟

2.2.1 解题思路及伪代码

解题思路:把偶数的数字存到B窗口,将奇数的数字存到A窗口,是先进先出,采用队列,将偶数的放在B队列,将奇数的放在A队列
  然后每输出两个A队列中的元素
 再输出一个B队列中的元素;
还有一个点就是,最后是A队列中的元素结尾还是B队列中的元素结尾,主要是控制空格的问题

伪代码:

创建结构体
 data[1002]用于存队列的数据; 
 定义 rear来记录从尾部插入了多少个元素; 
  front计数队头,方便取出队头的元素; 
   实现队列的push用法;传入类型为 queue 的指针 q;传入要入队的数tmp; 
    q->tail ++/每入队一个元素,尾部计数器+1; 
       将元素入队; 
   函数pop(queue *q) //取出队列的数字,从前端取出;此时要利用head记数 
       返回队头的元素
   void init(queue *q) 初始化 
        将尾和头记数均初始化为-1
    notEmpty(queue *q)判断队列是否为空
 如果尾和头的记数一样,则队列为空
 
 用countA记录入A队列的个数;用countB  记录入B队列的个数

     queue A定义
    queue B定义 
     初始化A队列,并要用引用符号
     初始化B队列,并用引用符号 
    输入m,
   push(&b,c)如果是偶数,则进b队列
     countB相应+1
  否则A
  分情况,注意最后到底是A队列的数结尾还是B队列的数结尾
     if countA大于两倍countB,是A队列的元素结尾,则A最后一个元素后面不能加空格
      当A队列不空或者B队列不空时; 
    ifA 不为空
    输出
      ifB 不为空
    输出
  countA小于等于两倍countB,是B队列的元素结尾,则B最后一个元素后面不能加空格
当A队列不空或者B队列不空时
    ifA 不为空
    输出 
     输出一个b队列的元素,注意b队列最后一个元素的后面不能加空格
              





3.1迷宫问题

求出出口到入口的路径


3.2 该题的设计思路及伪代码
思路:从上一个节点开始,任意找下一个能走的点,当找不到能走的点时,退回上一个点寻找是否有其他方向的点。
  使用栈存储当前路径。后进先出,方便回退到上一个点

typedef struct
{
    定义 i,j//方块的位置
   pre//本路径中上一方块在队列中的下标
} Box;//方块类型
    Box data[MaxSize];
    int front,rear;     //队头指针和队尾指针
               //定义顺序队类型
  print(QuType qu,int front) //从队列qu中输出路径
           //反向找到最短路径,将该路径上的方块的pre成员设置成-1
   
    while (k!=0);
    printf("迷宫路径如下:\n");
    
    while (k<=front)  //正向搜索到pre为-1的方块,即构成正向的路径
     //每输出每5个方块后换一行
       mgpath(int xi,int yi,int xe,int ye)搜索路径为:(xi,yi)->(xe,ye)
          //定义顺序队
      //(xi,yi)进队
       //将其赋值-1,以避免回过来重复搜索
    while (qu.front!=qu.rear && !find)  //队列不为空且未找到路径时循环
                     //出队,由于不是环形队列,该出队元素仍在队列中
             //找到了出口,输出路径
            print(qu,qu.front)调用print函数输出路径
            return(1)找到一条路径时返回1
        for (di=0; di<4; di++)扫描每个方位,把每个可走的方块插入队列中
    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指向路径中上一个方块的下标

3.3 分析该题目解题优势及难点。
总结算法就是:创建一个空栈,首先将入口位置进栈。当栈不空时循环:获取栈顶元素,寻找下一个可走的相邻方块,如果找不到可走的相邻方块,说明当前位置是死胡同,进行回溯(就是讲当前位置出栈,看前面的点是否还有别的出路)
  使用栈来解决迷宫问题,虽然实现起来比较简单,但是它的路径并不是最短的,很可能会绕远,如果想走最短路径可以使用队列来做

posted @ 2021-04-05 21:57  Li-xinguo★  阅读(182)  评论(1编辑  收藏  举报