数据结构—栈和队列的应用 Ⅱ

数据结构—栈和队列的应用 Ⅰ

栈在递归中的应用

递归的精髓在于能否将原始问题转换为属性相同但规模较小的问题。

  • 函数调用的特点:最后被调用的函数最先执行结束(LIFO)。
  • 函数调用时,需要用一个栈存储:
    ① 调用返回地址
    ② 实参
    ③ 局部变量
  • 必须注意递归模型不能是循环定义的,其必须满足下面的两个条件:
    ①递归表达式(递归体)
    ②边界条件(递归出口)

斐波那契数列:
F i b ( n ) { F i b ( n − 1 ) + F i b ( n − 1 ) , n > 1 1 , n = 1 0 , n = 0 Fib(n)\begin{cases} Fib(n-1)+Fib(n-1),n>1\\ 1,n=1\\ 0,n=0 \end{cases} \\ Fib(n)Fib(n1)+Fib(n1)n>11n=10n=0

int Fib(int n) //斐波那契数列的实现
{
	if (n == 0) return 0; //边界条件
	else if (n == 1) return 1; //边界条件
	else return Fib(n - 1) + Fib(n - 2); //递归表达式
}

在这里插入图片描述
阶乘:
f a c t o r i a l ( n ) { n × f a c t o r i a l ( n − 1 ) , n > 1 1 , n = 1 1 , n = 0 factorial (n)\begin{cases} n×factorial(n−1) ,n>1\\ 1,n=1\\ 1,n=0 \end{cases} \\ factorial(n)n×factorial(n1)n>11n=11n=0

int factorial(int n) //阶乘的实现
{
	if (n == 0 || n == 1) return 1; //边界条件
	else return n*factorial(n-1); //递归表达式
}

利用一个栈实现以下递归函数的非递归计算:
P n ( x ) { 2 x P n − 1 ( x ) − 2 ( n − 1 ) P n − 2 ( x ) , n > 1 2 x , n = 1 1 , n = 0 P_n(x)\begin{cases} 2xP_{n-1}(x)-2(n-1)P_{n-2}(x) ,n>1\\ 2x,n=1\\ 1,n=0 \end{cases} \\ Pn(x)2xPn1(x)2(n1)Pn2(x)n>12xn=11n=0

算法实现

  • 算法思想:设置一个栈用于保存n和对应的 P n ( x ) P_n(x) Pn(x)值,找中相邻元素的 P n ( x ) P_n(x) Pn(x)有题中关系。然后边出栈边计算 P n ( x ) P_n(x) Pn(x),栈空后该值就计算出来了。
#include<stdio.h>
#define MaxSize 100
double recursion(int n, double x)
{
	struct stack
	{
		int no;
		double val;
	}St[MaxSize];
	int top = -1, i;
	double fv1 = 1, fv2 = 2 * x;
	for (i = n; i >= 2; i--)
	{
		top++;
		St[top].no = i;
	}
	while (top >= 0)
	{
		St[top].val = 2 * x * fv2 - 2 * (St[top].no - 1) * fv1;
		fv1 = fv2;
		fv2 = St[top].val;
		top--;
	}
	if (n == 0) return fv1;
	return fv2;
}

int main()
{
	printf("%lf", recursion(4, 3.2));
	return 0;
}

//输出结果:1198.201600

栈在迷宫问题中的应用

  • 给定一个M×N的迷宫图,求一条从指定入口到出口的迷宫路径。假设一个迷宫图(这里M=8,N=8),其中的每个方块用空白表示通道,用阴影表示障碍物。一般情况下,所求迷宫路径是简单路径,即在求得的迷宫路径上不会重块。一个迷宫图的迷宫路径可能有多条,这些迷宫路径有长有短,这里仅仅考虑用栈求一条从指定入口到出口的迷宫路径。
  • 算法思想:为了表示迷宫,设置一个数组mg[ ],其中每个元素表示一个方块的状态,为0时表示对应方块是通道,为1时表示对应方块是障碍物(不可走)。为了算法方便,一般在迷官的外围加一条围墙。(由于迷宫四周加了一条围墙,故mg数组的行数和列数均加上2)
    在这里插入图片描述

算法实现:

#include<iostream>
using namespace std;
#include<malloc.h>
#define MaxSize 200
#define M 8
#define N 8
typedef struct Box
{
	int i; //当前方块的行号
	int j; //当前方块的列号
	int di; //di是下一个相邻可走方位的方位号
};
typedef struct StType
{
	Box data[MaxSize];
	int top;
};

bool InitStack(StType*& S)
{
	S = (StType*)malloc(sizeof(StType)); //分配一个顺序栈空间,首地址存放在s中
	if (S == NULL) return false; //内存不足,分配失败
	S->top = -1; //栈顶指针置为-1
	return true;
}

bool StackEmpty(StType* S)
{
	return S->top == -1;
}

bool Push(StType*& S, Box x)
{
	if (S->top == MaxSize - 1) return false; //栈满的情况,即栈上溢出
	S->top++; //栈顶指针增1
	S->data[S->top] = x; //元素x放在栈顶指针处
	return true;
}

bool Pop(StType*& S, Box& x)
{
	if (S->top == -1) return false; //栈为空的情况,即栈下溢出
	x = S->data[S->top]; //取栈顶元素
	S->top--; //栈顶指针减1
	return true;
}

bool GetTop(StType* S, Box& x)
{
	if (S->top == -1) return false; //栈为空的情况,即栈下溢出
	x = S->data[S->top]; //取栈顶元素
	return true;
}

void DestoryStack(StType*& S) //销毁栈
{
	free(S);
}

bool mgpath(int xi, int yi, int xe, int ye) //求解路径为(xi, yi)→ (xe, ye)
{
	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,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} };
	Box path[MaxSize], e;
	int i, j, di, i1, j1, k;
	bool find;
	StType* st; //定义栈st
	InitStack(st); //初始化栈顶指针
	e.i = xi; e.j = yi; e.di = -1; //设置e为入口
	Push(st, e); //方块e进栈
	mg[xi][yi] = -1; //将入口的迷宫值置为-1,避免重复走到该方块
	while (!StackEmpty(st)) //栈不空时循环
	{
		GetTop(st, e); //取栈顶方块e
		i = e.i; j = e.j; di = e.di;
		if (i == xe && j == ye) //找到了出口, 输出该路径
		{
			printf("一条迷宫路径如下:\n");
			k = 0;
			while (!StackEmpty(st))
			{
				Pop(st, e); //出栈方块e
				path[k++] = e; //将e添加到path数组中
			}
			while (k >= 1)
			{
				k--;
				printf("\t(%d,%d)", path[k].i, path[k].j);
				if ((k + 1) % 5 == 0) printf("\n"); //每输出5个方块后换一行
			}
			printf("\n");
			DestoryStack(st); //销毁栈
			return true; //输出一条迷宫路径后返回true
		}
		find = false;
		while (di < 4 && !find)
		{
			di++; //找方块(i,j)的下一个相邻可走方块(i1, j1)
			switch (di)
			{
			case 0: i1 = i - 1; j1 = j; break;
			case 1: i1 = i; j1 = j + 1; break;
			case 2: i1 = i + 1; j1 = j; break;
			case 3: i1 = i; j1 = j - 1; break;
			}
			if (mg[i1][j1] == 0) find = true; //找到一个相邻可走方块,设置find为真
		}
		if (find) //找到了一个相邻可走方块(i1,j1)
		{
			st->data[st->top].di = di; //修改原栈顶元素的di值
			e.i = i1; e.j = j1; e.di = -1;
			Push(st, e); //相邻可走方块e进栈
			mg[i1][j1] = -1; //将(i1,j1)迷宫值置为一1,避免重复走到该方块
		}
		else //没有路径可走,则退栈
		{
			Pop(st, e); //将栈顶方块退栈
			mg[e.i][e.j] = 0; //让退栈方块的位置变为其他路径可走方块
		}
	}
	DestoryStack(st); //销毁栈					
	return false; //表示没有可走路径,返回 false
}

int main()
{
	if (!mgpath(1, 1, M, N)) printf("该迷宫问题没有解!");
	return 0;
}

程序分析:

在这里插入图片描述

  • 运行结果:
    在这里插入图片描述
    在这里插入图片描述

队列在迷宫问题中的应用

  • 算法思想:从入口(xi,yi)开始,利用队列的特点,一层一层向外扩展查找可走的方块,直到找到出口为止,这个方法就是广度优先搜索方法。这里是引用

算法实现:

//用队列求解迷宫问题
#include <stdio.h>
#include <malloc.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 InitQueue(QuType *&q) //初始化队列
{	q=(QuType *)malloc (sizeof(QuType));
	q->front = q->rear = -1;
}
void DestroyQueue(QuType *&q)	 //销毁队列
{
	free(q);
}
bool QueueEmpty(QuType *q) //判断队列是否为空
{
	return(q->front == q->rear);
}
bool enQueue(QuType*& q, Box e) //进队列
{	
	if (q->rear==MaxSize-1)	return false; //队满上溢出,返回假
	q->rear++; //队尾增1
	q->data[q->rear] = e; //rear位置插入元素e
	return true; //返回真
}
bool deQueue(QuType*& q, Box& e) //出队列
{	if (q->front==q->rear)	return false; //队空下溢出
	q->front++;
	e = q->data[q->front];
	return true;
}

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");
}
bool mgpath1(int xi,int yi,int xe,int ye)	//搜索路径为:(xi,yi)->(xe,ye)
{
	Box e;
	int i, j, di, i1, j1;
	QuType *qu;	 //定义顺序队指针qu
	InitQueue(qu); //初始化队列qu
	e.i = xi; e.j = yi; e.pre = -1;
	enQueue(qu,e); //(xi,yi)进队
	mg[xi][yi] = -1; //将其赋值-1,以避免回过来重复搜索
	while (!QueueEmpty(qu))	//队不空且循环
	{	
		deQueue(qu, e); //出队方块e,由于不是环形队列,该出队元素仍在队列中
		i = e.i; j = e.j;
		if (i==xe && j==ye) //找到了出口,输出路径
		{	
			print(qu, qu->front);	//调用print函数输出路径
			DestroyQueue(qu); //销毁队列
			return true; //找到一条路径时返回真
		}
		for (di = 0; di < 4; di++) //循环扫描每个方位,把每个可走的方块插入队列中
		{	
			switch(di)
			{
			case 0:i1=i-1; j1=j;   break;
			case 1:i1=i;   j1=j+1; break;
			case 2:i1=i+1; j1=j;   break;
			case 3:i1=i;   j1=j-1; break;
			}
			if (mg[i1][j1]==0)
			{
				e.i = i1; e.j = j1;
				e.pre = qu->front;	//指向路径中上一个方块的下标
				enQueue(qu, e); //(i1,j1)方块进队
				mg[i1][j1] = -1; //将其赋值-1,以避免回过来重复搜索
			}
		}
     }
	DestroyQueue(qu); //销毁队列
	return false; //未找到任何路径时返回假
}

int main()
{
	mgpath1(1,1,M,N);
	return 0;
}

程序分析:

  • 这里使用的顺序队列qu不是环形队列,因为在找到出口时需要利用队列中的所有方块査找一条迷宫路径。如果采用环形队列,出队的方块可能被新进队的方块覆盖,从而无法求出迷宫路径。这里要求非环形队列qu有足够大的空间。
    在这里插入图片描述
  • 运行结果:在这里插入图片描述
    在这里插入图片描述
  • 显然这个解是最优解,也就是最短路径。

队列在计算机系统中的应用

  • 1.解决主机与外部设备之间速度不匹配的问题
  • 以主机和打印机之间速度不匹配的问题为例。
    主机输出数据给打印机打印,输出数据的速度比打印数据的速度要快得多,由于速度不匹配,若直接把输出的数据送给打印机打印显然是不行的。解决的方法是设置一个打印数据缓冲区,主机把要打印输出的数据依次写入这个缓冲区,写满后就暂停输出,转去做其他的事情。打印机就从缓冲区中按照先进先出的原则依次取出数据并打印,打印完后再向主机发出请求,主机接到请求后再向缓冲区写入打印数据。这样做既保证了打印数据的正确,又使主机提高了效率。由此可见,打印数据缓冲区中所存储的数据就是一个队列。
  • 2.解決由多用户引起的资源竞争问题
  • CPU(即中央处理器,它包括运算器和控制器)资源的竞争就是一个典型的例子。
    在一个带有多终端的计算机系统上,有多个用户需要CPU各自运行自己的程序,它们分别通过各自的终端向操作系统提出占用CPU的请求。操作系统通常按照每个请求在时间上的先后
    顺序,把它们排成一个队列,每次把CPU分配给队首请求的用户使用。当相应的程序运行结束或用完规定的时间间隔后,令其出队,再把CPU分配给新的队首请求的用户使用。这样既能满足每个用户的请求,又使CPU能够正常运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值