DS博客作业02--栈和队列

0.PTA得分截图

1.本周学习总结

1.1 栈

栈(stack)是限定仅在表尾进行插入或者删除的线性表


栈和线性表类似,也有两种存储表示方法顺序栈和链栈

顺序栈的结构、操作函数

结构

#define MAXSIZE 50		
typedef struct
{
    ElemType data[MaxSize];			
    int top;				
}SqStack;

顺序栈的初始化
void InitStack(SqStack *&s)
{
     s = (Stack)malloc(sizeof(struct SNode));
    s->Top = -1;
}
顺序栈的判空
bool StackEmpty(SqStack *S)
{
     return S->top==-1;
}
顺序栈的销毁
void DesStack(SqStack *&S)
{
     free(s);
}
顺序栈的进栈
bool Push(SqStack *&S, ElementType X)
{

    if (S->Top == MaxSize-1 )
    {
        return false;
    }
    S->top++; //top先自增
    S->Data[S->Top] = X;
    return true;
}
顺序栈的出栈
bool Pop(SqStack *&S,ElemTypr &e)
{

    if (S->top == -1)//栈空
    {
        return ERROR;
    }    
    e = S->Data[S->top];  
    S->top--;  
    return e;
}

链栈的结构和操作函数

结构

typedef struct linknode
{ElemType data;//数据域
struct linknode *next;//指针域
} LinkSLNode;
链栈的初始化
void InitStack( LinkStNode&s)
{
s= (LinkStNode * )malloc( sizeof(LinkStNode));
s-> next=NULL;
}
链栈的判空
bool StackEmpty(SqStack *S)
{
     return S->top==-1;
}
链栈的销毁

释放链栈占用的全部空间结点

void DesStack(SqStack *&S)
{
     free(s);
}
链栈的进栈

新建一个结点,用于存放元素e(由p指向它),然后将其插入头节点之和作为新的首结点

void Push(LinkStNode * &.s, ElemType e)
{
LinkStNode * P;
p= (LinkStNode * )malloc(sizeof(LinkStNode));//新建结点p
p-> data=e;//存放元素e
p→>next=s-> next;//将P结点插人作为首结点
s-> next= p;
}
链栈的出栈
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;
}

1.2 栈的应用

1.符号配对

2.表达式转换

3.迷宫问题

基本思路(利用栈结构储存走的位置)
首先判断该点是否为入口,若是则把该位置的数值改为2,然后入栈,分别判断该点的上下左右是否可以走通,若可以则把可走通的位置改为前一个位置上的数值加一,若不能则判断下一个方向,都走不通则返回;当找到可走通的位置并修改完数值后,把该位置入栈,判断该位置是否为出口,若为出口,则把栈中元素的的个数与存放最短路径的栈中的元素的个数进行比较,若该栈中元素个数较少,则更新存放最短路径的栈;若不为出口则继续操作;然后递归调用这个函数(从判断上下左右是否可以走通开始),把可走通的位置传进函数中,当函数走回最外层递归时,则遍历完所有位置,程序结束,分别返回存放最短路径的栈中的元素

1.3 队列

队列的特性:
在队尾插入元素,在队首删除元素。
FIFO(先进先出),就向排队取票一样。

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

顺序队列的结构

typedef struct
{ElemType data[MaxSize] ;//存放队中元素
int front, rear;//队头和队尾指针
}SqQueue;
顺序队列的初始化
void InitQueue( SqQueue * &.q)
{ q= (SqQueue * )malloc( sizeof(SqQueue));
q-> front=q-> rear=- 1;
}
队空的判断
void InitQueue(SqQueue * &q)
{
q= (SqQueue * malloc( sizeof(SqQueue));
q-> front=q-> rear=- 1;
}
队列的销毁
void DestroyQueue(SqQueue * &q)
{
     free(q);
}
顺序队列的进队列
bool enQueue(SqQueue * &q, ElemType e)
{ if (q-> rear== MaxSize- 1)
return false;
q-> rear++;//队尾增1
q-> data[q-> rear]=e;//rear位置插入元素e
return true;
}
顺序队列的出队列

bool deQueue(SqQueue * &q, ElemType &e)
{ if (q-> front==q-> rear)            return false;
q->front++;
e=q -> data[q -> front];
return true;
}

环形队列的结构及其操作函数

结构

typedef struct
{ElemType data[MaxSize] ;//存放队中元素
int front, rear;//队头和队尾指针
}SqQueue;
环形队列的初始化
void InitQueue( SqQueue * &.q)
{ q= (SqQueue * )malloc( sizeof(SqQueue));
q-> front=q-> rear=0;
}
环形队列队空的判断
void QueueEmpty(SqQueue * &q)
{

return(q-> front==q-> rear);
}
环形队列的销毁
void DestroyQueue(SqQueue * &q)
{
     free(q);
}
环形队列的进队列
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;
}
环形队列的出队列

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

链队列的结构、操作函数

结构

typedef struct qnode
{ElemType data;//存放元素
struct qnode * next;//下一个结点指针
} DatalNode;
链队列的初始化
void InitQueue( LinkQuNde * &.q)
{ q= (LinkQuNde * )malloc( sizeof(LinkQuNde));
q-> front=q-> rear=NULL;
}
链队列队空的判断
bool QueueEmpty(LinkQueue * &q)
{

return(q-> rear==NULL);
}
链队列的销毁
void DestroyQueue(LinkQuNode*&q)
{DataNode * pre=q > front, * p;//pre指向队首结点
if (pre!=NULL)
{
p=pre- > next;//p指向结点pre的后继结点
while (p!= NULL)
{free(pre) ;//释放pre结点
pre=p;P=P- > next;//pre、p同步后移
}
free(pre);
}
free(q);
链队列的进队列
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;//将结点p链到队尾,并将rear 指向它
q-> rear=p;
}
}
链队列的出队列
bool deQueue(LinkQuNode , &q. ElemType &e)
{
DataNode *t:
if(q-> rear== NULL)
   return false;
t=q -> front;//t指向首结点
if (q-> front==q- > rear)
     q-> front=q-> rear= NULL;
else
   q> front=q-> front-> next;
e=t-> data;
free(t);
return true;

队列应用————迷宫问题

迷宫问题是指:给定给定一个M×N的迷宫图、入口与出口、行走规则。求一条从指定入口到出口的路径。
所求路径必须是简单路径,即路径不重复。
求解思路:使用顺序队列(使用顺序队列的原因是:出队入队操作并不会删除结点,只是改变了队首队尾指针的值,最终还要通过队列中已出队节点来回溯得到路径),队列中的数据元素类型为格点坐标(i,j)和路径中上一格点在队列中的位置pre的封装。pre的设置是为了找到终点后由终点通过pre回溯到起点从而逆序打印出路径(采用递归实现)。在将一个能走的格点入队后,循环搜索它周围的四个格点,并将其中能走的入队,所以必须制定四个方向的搜索顺序(最后若有多条最短路径,则打印出哪一条由搜索顺序决定)。由于路径不重复,所以在在入队后将一个迷宫格点的值赋为-1

#include<iostream>
#include<stdlib.h>
using namespace std;
const int MaxSize = 100;
typedef struct
{
	int i,j;//迷宫块坐标 
	int pre;//当前路径中前一方块在队列中的位置 
	
}Box;
typedef struct Queue
{
	Box data[MaxSize];
	int rear,front;//front指向当前队头的前一元素,rear指向队尾 
}SqQueue;
 
//全局数组maze表示迷宫
const int M=4,N=4;
int maze[M+2][N+2] = { 	{1, 1, 1, 1, 1, 1}, //迷宫示例 
						{1, 0, 0, 0, 1, 1}, 
						{1, 0, 1, 0, 0, 1}, 
						{1, 0, 0, 0, 1, 1}, 
						{1, 1, 0, 0, 0, 1}, 
						{1, 1, 1, 1, 1, 1}  };
 
bool MazePath(int xi,int yi,int xe,int ye);
void print(SqQueue q,int n);
 
int main(void)
{
	if(MazePath(1,1,4,4))
	{
		cout << "有路径,如上~" << endl; 
	}
	else
	{
		cout << "没有路径~" << endl; 
	}
	
	system("pause");
	return 0;
}
 
 
//求迷宫路径算法,xi,yi入口坐标,xe,ye出口坐标 
bool MazePath(int xi,int yi,int xe,int ye)//x表行号,y表列号 
//搜索路径(xi,yi)->(xe,ye)
{
	int i,j;
	bool find = false;//找到出口置1 
	SqQueue qu;//在栈中分配内存 
	qu.rear = qu.front = -1;
	qu.rear ++;
	qu.data[qu.rear].i = xi;
	qu.data[qu.rear].j = yi;//(xi,yi)入队
	qu.data[qu.rear].pre = -1;//表示在队列中没有位于它之前的元素,作为搜索路径时的结束条件
	maze[xi][yi] = -1;//将0置为-1,避免重复搜索
	while(qu.front != qu.rear && !find)//当队列不空且没有找到出口时循环
	{
		qu.front ++;
		i = qu.data[qu.front].i;
		j = qu.data[qu.front].j;//i表行,j表列 
		if(i==xe && j==ye)
		{
			find = true;
			print(qu,qu.front);//打印路径,从当前格点(终点)开始追溯递归打印路径 
			return true;//找到出口 
		}
		//将(i,j)周围四个格点中为路且没有走过的格点进队 
		for(int di=0;di<4;di++)//di表示查找方向,0->3顺时针旋转,分别为上右下左 
		{
			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(maze[i][j] == 0)
			{
				qu.rear ++;
				qu.data[qu.rear].i = i;
				qu.data[qu.rear].j = j;
				qu.data[qu.rear].pre = qu.front;//上一个出队元素在队列中的标号 
				maze[i][j] = -1;
			}
		} 
	}
	return false;//未找到路径返回false 
}
 
//递归打印路径 
void print(SqQueue q,int n)
{
	if(q.data[n].pre == -1)
	{
		cout << "(" << q.data[n].i << "," << q.data[n].j << ")" << endl;
		return;//return 必须写 
	}
	print(q,q.data[n].pre);
	cout << "(" << q.data[n].i << "," << q.data[n].j << ")" << endl;
}

2.PTA实验作业

2.1 符号配对

请编写程序检查C语言源程序中下列符号是否配对://、(与)、[与]、{与}。

2.1.1 解题思路及伪代码

从头遍历:
1.遇到左符号:入栈;
2.遇到右符号:若此时栈空则表示当前右符号缺少与之匹配的左符号,跳出循环;栈不空,则与栈顶元素进行配对,若配对成功则栈顶符号出栈,否则跳出循环。

初始化栈
输入str
遍历字符串
for i=0 to i=len-1
if 字符不是{【()】}
continue
if 字符为[{(
进栈
if 字符}】)
then 与对头元素配对
判断对空

代码:

2.1.2 总结解题所用的知识点

对栈的定义和各种操做
对的各种情况的判断

2.2 银行业务队列简单模拟

设某银行有A、B两个业务窗口,且处理业务的速度不一样,其中A窗口处理速度是B窗口的2倍 —— 即当A窗口每处理完2个顾客时,B窗口处理完1个顾客。给定到达银行的顾客序列,请按业务完成的顺序输出顾客序列。假定不考虑顾客先后到达的时间间隔,并且当不同窗口同时处理完2个顾客时,A窗口顾客优先输出。

2.2.1 解题思路及伪代码

思路:
把偶数的数字存到B窗口,将奇数的数字存到A窗口,是先进先出,采用队列,将偶数的放在B队列,将奇数的放在A队列
然后每输出两个A队列中的元素
再输出一个B队列中的元素;
最后对队列中剩余元素输出
伪代码:

for i=0 to n
   if (x为奇数) 入A栈
   else 入B栈
   按格式输出
if(A不空) 输出A剩余元素
if(B不空) 输出B剩余元素

代码:

2.2.2 总结解题所用的知识点

1.队列的定义和基本操作
2.对两个队列的判断
3.c++中队列模板的应用

3.阅读代码

3.1 题目及解题代码

特殊堆栈

堆栈是一种经典的后进先出的线性结构,相关的操作主要有“入栈”(在堆栈顶插入一个元素)和“出栈”(将栈顶元素返回并从堆栈中删除)。本题要求你实现另一个附加的操作:“取中值”——即返回所有堆栈中元素键值的中值。给定 N 个元素,如果 N 是偶数,则中值定义为第 N/2 小元;若是奇数,则为第 (N+1)/2 小元

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
	vector<int>v, v1;
	string opr;
	int n; cin >> n;
	vector<int>::iterator it;
	while (n--)
	{
		cin >> opr;
		if (opr.compare("Push") == 0)
		{
			int data; cin >> data;
			v.push_back(data);
			it = lower_bound(v1.begin(), v1.end(), data);
			v1.insert(it, data);
		}
		else if (opr.compare("Pop") == 0)
		{
			if (!v.empty())
			{
				it = lower_bound(v1.begin(), v1.end(), v[v.size() - 1]);
				v1.erase(it);
				cout << v.back() << endl;
				v.pop_back();
			}
			else cout << "Invalid\n";
		}
		else if (opr.compare("PeekMedian") == 0)
		{
			if (v.empty())
				cout << "Invalid\n";
			else
			{
				if (v1.size() % 2 != 0)
					cout << v1[(v1.size() - 1) / 2] << endl;
				else
					cout << v1[v1.size() / 2 - 1] << endl;
			}
		}
	}
	return 0;
}

3.2 该题的设计思路及伪代码

伪代码

	建立容器
	while (n--)
		输入字符
		if (opr.compare("Push") == 0)
        进入容器
		else if (opr.compare("Pop") == 0)
			if (!v.empty())
         输出
			else 输出 Invalid
		else if (opr.compare("PeekMedian") == 0)
			if (v.empty())
				输出 Invalid
			else
				if 长度为奇数
					输出减1后除以2位置的元素
				else
					输出除以2减1后位置的元素

3.3 分析该题目解题优势及难点。

利用vctor容器,使解题更方便

posted @ 2021-04-05 22:42  letmee  阅读(64)  评论(1编辑  收藏  举报