数据结构学习笔记 --- 栈的应用举例

这篇博客深入探讨了栈在解决实际问题中的应用,包括使用栈解决迷宫求解的两种方法、通过栈进行表达式求值,以及在hanoi塔问题和括号匹配问题中的重要作用。
摘要由CSDN通过智能技术生成

1. 引言 


本文主要讲解栈的一些应用:(1)迷宫求解 (2)表达式求值 (3)栈与递归 ——hanoi塔(4)括号匹配


2. 迷宫求解

2.1 解法1

#include <stdio.h>

// test 
#if 0

#define 	MAZE_COLUMN			7		// 迷宫的列数
#define 	MAZE_ROW			7		// 迷宫的行数

//在二维阵列中使用2表示迷宫墙壁,使用0来表示老鼠的行走路径
int m[MAZE_ROW][MAZE_COLUMN] = {
									{2, 2, 2, 2, 2, 2, 2}, 
									{2, 0, 0, 0, 0, 0, 2}, 
									{2, 0, 2, 0, 2, 0, 2}, 
									{2, 0, 0, 2, 0, 2, 2}, 
									{2, 2, 0, 2, 0, 2, 2}, 
									{2, 0, 0, 0, 0, 0, 2}, 
									{2, 2, 2, 2, 2, 2, 2}	
				  			    };
#else
	
#define 	MAZE_COLUMN			5		// 迷宫的列数
#define 	MAZE_ROW			5		// 迷宫的行数

//在二维阵列中使用2表示迷宫墙壁,使用0来表示老鼠的行走路径
int m[MAZE_ROW][MAZE_COLUMN] = {
									{2, 2, 2, 2, 2}, 
									{2, 0, 0, 0, 2}, 
									{2, 0, 2, 0, 2}, 
									{2, 0, 0, 0, 2}, 
									{2, 2, 2, 2, 2}	
				  			    };
#endif

struct PosType // 迷宫坐标位置类型
{
   	int x; // 行值
   	int y; // 列值
};

 

PosType 	begin,end;  // 迷宫的入口坐标,出口坐标
int 		curstep=1; 	// 当前足迹,初值(在入口处)为1

// 输出迷宫的解(m数组)
void Print()
{ 
  	int i, j;
  	for(i=0; i<MAZE_ROW; i++)
  	{
    	for(j=0; j<MAZE_COLUMN; j++)
      	printf("%3d", m[i][j]);
    	printf("\n");
  	}
}


// 由当前位置cur,当前步骤curstep试探下一点
void try_path(PosType cur, int curstep)
{
	int i;
	PosType next; 	// 下一个位置
	PosType direc[4] = {{0,1}, {1,0}, {0,-1}, {-1,0}};  // {行增量,列增量},移动方向依次无为东南西北
	
	// 依次试探东南西北四个方向 
	for (i = 0; i <= 3; i++)  
	{
		// 根据移动方向,给下一位置赋值
		next.x = cur.x + direc[i].x;
		next.y = cur.y + direc[i].y;

		if (m[next.x][next.y] == 0)  // 下一个位置是通道
		{
			m[next.x][next.y] = ++curstep;
			if (next.x != end.x || next.y != end.y) // 没有到达终点
				try_path(next, curstep);
			else // 到达终点
			{
				Print(); // 输出结果(出口,不再递归调用)
				printf("\n");
			}
			
			m[next.x][next.y] = 0; // 恢复为通路,以便在另一个方向试探另一条路
			curstep--;
		}
	}
}

int main()
{
	printf("请输入入口的行数,列数:");
  	scanf("%d,%d",&begin.x,&begin.y);
  	printf("请输入出口的行数,列数:");
  	scanf("%d,%d",&end.x,&end.y);
  	
  	
	printf("此迷宫从入口到出口的路径如下:\n");
	m[begin.x][begin.y] = 1; // 入口的足迹为1
	try_path(begin, 1);
  	return 0;
 }

2.2  解法2

#include "ds.h"

#define 	STACK_INIT_SIZE 	10		// 栈存储空间初始分配量
#define 	STACK_INCREMENT 	2		// 栈存储空间分配增量
#define 	MAZE_COLUMN			7		// 迷宫的列数
#define 	MAZE_ROW			7		// 迷宫的行数


struct PosType // 迷宫坐标位置类型
{
   	int x; // 行值
   	int y; // 列值
};

struct SElemType 		// 栈的元素类型
{
   	int ord; 			// 通道块在路径上的"序号"
   	PosType seat; 		// 通道块在迷宫中的"坐标位置"
   	int di; 			// 从此通道块走向下一通道块的"方向"(0~3表示东~北)
};

typedef struct SqStack
{
	SElemType 		*base;				// 在栈构造之前和销毁之后,base的值为NULL
	SElemType 		*top;				// 栈顶指针
	int 			stacksize;			// 当前已分配的存储空间,以元素为单位
}SqStack;

//在二维阵列中使用2表示迷宫墙壁,使用0来表示老鼠的行走路径
int m[MAZE_ROW][MAZE_COLUMN] = {
									{2, 2, 2, 2, 2, 2, 2}, 
									{2, 0, 0, 0, 0, 0, 2}, 
									{2, 0, 2, 0, 2, 0, 2}, 
									{2, 0, 0, 2, 0, 2, 2}, 
									{2, 2, 0, 2, 0, 2, 2}, 
									{2, 0, 0, 0, 0, 0, 2}, 
									{2, 2, 2, 2, 2, 2, 2}	
				  				   }; 

PosType 	begin,end;  // 迷宫的入口坐标,出口坐标
SqStack 	S; 			// 顺序栈
int 		curstep=1; 	// 当前足迹,初值(在入口处)为1

// 构造一个空栈S
void InitStack(SqStack &S)
{
	S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
	if (!S.base) exit(OVERFLOW);
	
	S.top = S.base;
	S.stacksize = STACK_INIT_SIZE;
}

// 若栈S为空栈,则返回TRUE,否则返回FALSE
Status StackEmpty(SqStack S)
{
	if (S.top == S.base)
		return TRUE;
	else
		return FALSE;
}


// 插入元素e为新的栈顶元素
void Push(SqStack &S, SElemType e)
{
	if (S.top - S.base >= S.stacksize)
	{
		S.base = (SElemType*)realloc(S.base, (S.stacksize + STACK_INCREMENT) * sizeof(SElemType));
		if (!S.base) exit(OVERFLOW);
		
		S.top = S.base + S.stacksize;
		S.stacksize += STACK_INCREMENT;
	}
	
	memcpy(S.top, &e, sizeof(SElemType));
	S.top++;
}

// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqStack &S, SElemType &e)
{
	if (S.top == S.base)
		return ERROR;
	
	memcpy(&e, --S.top, sizeof(SElemType));
	return OK;
}

// 从栈底到栈顶依次对栈中每个元素调用函数visit()
void StackTraverse(SqStack S, void(* visit)(SElemType))
{
	SElemType *p = S.base;
	
	while(p < S.top)
	{
		visit(*p++);
		printf("\n");
	}
	
	printf("\n");
}


	
void print(SElemType e)
{
	printf("序号:%d \t",e.ord);
	printf("坐标位置:(%d, %d) \t",e.seat.x, e.seat.y);
	printf("方向(0~3表示东~北): %d\n", e.di);
}


void Print()
{ // 输出迷宫的解(m数组)
  int i, j;
  for(i=0; i<MAZE_ROW; i++)
  {
    for(j=0;j<MAZE_COLUMN;j++)
      printf("%3d",m[i][j]);
    printf("\n");
  }
}

int Pass(PosType b)
{ 
	// 当迷宫m的b点的序号为0(可通过路径),返回1;否则,返回0
  	if(m[b.x][b.y] == 0)
    	return 1;
  	else
   	 	return 0;
}

void FootPrint(PosType a)
{ 
	// 使迷宫m的a点的值变为足迹(curstep)
  	m[a.x][a.y] = curstep;
}

void NextPos(PosType &c, int di)
{ 
	// 根据当前位置及移动方向,求得下一位置
  	PosType direc[4] = {{0,1}, {1,0}, {0,-1}, {-1,0}}; // {行增量,列增量},移动方向,依次为东南西北
  	c.x += direc[di].x;
  	c.y += direc[di].y;
}

void MarkPrint(PosType b)
{ 
	// 使迷宫m的b点的序号变为-1(不能通过的路径)
  	m[b.x][b.y] = -1;
}

Status MazePath(PosType start,PosType end) // 算法3.3
{ // 若迷宫m中存在从入口start到出口end的通道,则求得一条
  // 存放在栈中(从栈底到栈顶),并返回TRUE;否则返回FALSE

  	PosType 	curpos; // 当前位置
  	SElemType 	e; 		// 栈元素
  	InitStack(S); 		// 初始化栈
  	curpos = start; 	// 当前位置在入口
  	do
  	{ 
  		// 当前位置可以通过,即是未曾走到过的通道块
    	if(Pass(curpos))
    	{
      		FootPrint(curpos); // 留下足迹
      		e.ord  = curstep;
      		e.seat = curpos;
      		e.di   = 0;
      		Push(S,e); // 入栈当前位置及状态
      		curstep++; // 足迹加1
      		if(curpos.x == end.x && curpos.y == end.y) // 到达终点(出口)
        		return TRUE;
      		NextPos(curpos,e.di); // 由当前位置及移动方向,确定下一个当前位置
    	}
    	else
    	{ 
    		// 当前位置不能通过
      		if(!StackEmpty(S)) // 栈不空
      		{
        		Pop(S,e); // 退栈到前一位置
        		curstep--; // 足迹减1
        		while(e.di==3&&!StackEmpty(S)) // 前一位置处于最后一个方向(北)
        		{
          			MarkPrint(e.seat); // 在前一位置留下不能通过的标记(-1)
          			Pop(S,e); // 再退回一步
          			curstep--; // 足迹再减1
        		}
        		if(e.di<3) // 没到最后一个方向(北)
        		{
          			e.di++; // 换下一个方向探索
          			Push(S,e); // 入栈该位置的下一个方向
          			curstep++; // 足迹加1
          			curpos=e.seat; // 确定当前位置
          			NextPos(curpos,e.di); // 确定下一个当前位置是该新方向上的相邻块
        		}
      		}
    	}
  }while(!StackEmpty(S));
  
  return FALSE;
}

int main()
{
	printf("请输入入口的行数,列数:");
  	scanf("%d,%d",&begin.x,&begin.y);
  	printf("请输入出口的行数,列数:");
  	scanf("%d,%d",&end.x,&end.y);
  	
  	if (MazePath(begin, end))
	{
		printf("此迷宫从入口到出口的一条路径如下 \n");
		Print(); // 输出此通路
	}
	else
	{
		printf("此迷宫没有从入口到出口的路径 \n");
	}
	printf("到达终点时栈的内容: \n");
	StackTraverse(S, print);
}

3. 表达式求值

#include "ds.h"

#define 	STACK_INIT_SIZE 	10		// 存储空间初始分配量
#define 	STACK_INCREMENT 	2		// 存储空间分配增量


typedef 	char 	SElemType;

typedef struct SqStack
{
	SElemType 		*base;				// 在栈构造之前和销毁之后,base的值为NULL
	SElemType 		*top;				// 栈顶指针
	int 			stacksize;			// 当前已分配的存储空间,以元素为单位
}SqStack;

void InitStack(SqStack &S);
void DestroyStack(SqStack &S); 
void ClearStack(SqStack &S);
Status StackEmpty(SqStack S);
int StackLength(SqStack S);
Status GetTop(SqStack S, SElemType &e);
void Push(SqStack &S, SElemType e);
Status Pop(SqStack &S, SElemType &e);
void StackTraverse(SqStack S, void(* visit)(SElemType));

// 构造一个空栈S
void InitStack(SqStack &S)
{
	S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
	if (!S.base) exit(OVERFLOW);
	
	S.top = S.base;
	S.stacksize = STACK_INIT_SIZE;
}

// 销毁栈S,S不再存在
void DestroyStack(SqStack &S)
{
	free(S.base);
	S.base = NULL;
	S.top = NULL;
	S.stacksize = 0;
}

// 把S置为空栈
void ClearStack(SqStack &S)
{
	S.top = S.base;
}

// 若栈S为空栈,则返回TRUE,否则返回FALSE
Status StackEmpty(SqStack S)
{
	if (S.top == S.base)
		return TRUE;
	else
		return FALSE;
}

// 返回S的元素个数,即栈的长度
int StackLength(SqStack S)
{
	return S.top - S.base;  // not return S.stacksize;
}

// 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
Status GetTop(SqStack S, SElemType &e)
{
	if (S.top > S.base)
	{
		memcpy(&e, S.top - 1, sizeof(SElemType));
		return OK;
	}
	else
	{
		return ERROR;
	}
}

// 插入元素e为新的栈顶元素
void Push(SqStack &S, SElemType e)
{
	if (S.top - S.base >= S.stacksize)
	{
		S.base = (SElemType*)realloc(S.base, (S.stacksize + STACK_INCREMENT) * sizeof(SElemType));
		if (!S.base) exit(OVERFLOW);
		
		S.top = S.base + S.stacksize;
		S.stacksize += STACK_INCREMENT;
	}
	
	memcpy(S.top, &e, sizeof(SElemType));
	S.top++;
}

// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqStack &S, SElemType &e)
{
	if (S.top == S.base)
		return ERROR;
	
	memcpy(&e, --S.top, sizeof(SElemType));
	return OK;
}

// 从栈底到栈顶依次对栈中每个元素调用函数visit()
void StackTraverse(SqStack S, void(* visit)(SElemType))
{
	SElemType *p = S.base;
	
	while(p < S.top)
	{
		visit(*p++);
	}
	printf("\n");
}

void print(SElemType c)
{
	printf("%c ",c);
}


char Precede(SElemType t1, SElemType t2)
{
	char ret;
	switch (t2)
	{
		case '+':
		case '-':
			if (t1 == '(' || t1 == '\n')
				ret = '<';	// t1 < t2
			else
				ret = '>';	// t1 > t2
			break;
		
		case '*':
		case '/':
			if (t1 == '*' || t1 == '/' || t1 == ')')
				ret = '>'; 	// t1 > t2
			else
				ret = '<';	// t1 < t2
			break;
		
		case '(':
				if (t1 == ')')
				{
					printf("括号不匹配 \n");
					exit(ERROR);
				}
				else
					ret = '<';	// t1 < t2
				break;
			
		case ')':
				switch (t1)
				{
					case '(':
							ret = '='; 	// t1 = t2
						break;
					case '\n':
							printf("缺乏左括号 \n");
							exit(ERROR);
					default:
							ret = '>';	// t1 > t2
				}
				break;
		case '\n':
				switch (t1)
				{
					case '\n': 
							ret = '='; // t1 = t2
							break;
					case '(':
							printf("缺乏右括号\n");
							exit(ERROR);
					default:
							ret = '>';	// t1 > t2
				}
	}
	
	return ret;
}

Status In(SElemType c)
{
	switch (c)
	{
		case '+':
		case '-':
		case '*':
		case '/':
		case '(':
		case ')':
		case '\n':
				return TRUE;
		default: 
				return FALSE;
	}
}

SElemType Operate(SElemType a, SElemType theta, SElemType b)
{
	switch (theta)
	{
		case '+':	return a+b;
		case '-':	return a-b;
		case '*':	return a*b;
	}
	
	return a/b;
}

// 算术表达式求值的算符优先算法。设OPTR和OPND分别为运算符栈和运算数栈
SElemType EvaluateExpression()
{
   SqStack OPTR,OPND;
   SElemType a,b,d,x; 
   char c; // 存放由键盘接收的字符,
   char z[11]; // 存放整数字符串,
   int i; 
   InitStack(OPTR); // 初始化运算符栈OPTR和运算数栈OPND
   InitStack(OPND);
   Push(OPTR,'\n'); // 将换行符压入运算符栈OPTR的栈底(改)
   c=getchar(); // 由键盘读入1个字符到c
   GetTop(OPTR,x); // 将运算符栈OPTR的栈顶元素赋给x
   while(c!='\n'||x!='\n') // c和x不都是换行符
   {
     if(In(c)) // c是7种运算符之一
       switch(Precede(x,c)) // 判断x和c的优先权
       {
         case'<' :Push(OPTR,c); // 栈顶元素x的优先权低,入栈c
                  c=getchar();  // 由键盘读入下一个字符到c
                  break;
         case'=' :Pop(OPTR,x); // x='('且c=')'情况,弹出'('给x(后又扔掉)
                  c=getchar(); // 由键盘读入下一个字符到c(扔掉')')
                  break;
         case'>' :Pop(OPTR,x); // 栈顶元素x的优先权高,弹出运算符栈OPTR的栈顶元素给x(改)
                  Pop(OPND,b); // 依次弹出运算数栈OPND的栈顶元素给b,a
                  Pop(OPND,a);
                  Push(OPND,Operate(a,x,b)); // 做运算a x b,并将运算结果入运算数栈
       }
     else if(c>='0'&&c<='9') // c是操作数,此语句改
     {
       i=0;
       while(c>='0'&&c<='9') // 是连续数字
       {
         z[i++]=c;
         c=getchar();
       }
       z[i]=0; // 字符串结束符
       d=atoi(z); // 将z中保存的数值型字符串转为整型存于d
       Push(OPND,d); // 将d压入运算数栈OPND
     }
     else // c是非法字符,以下同
     {
       printf("出现非法字符\n");
       exit(ERROR);
     }
     GetTop(OPTR,x); // 将运算符栈OPTR的栈顶元素赋给x
   }
   Pop(OPND,x); // 弹出运算数栈OPND的栈顶元素(运算结果)给x(改此处)
   if(!StackEmpty(OPND)) // 运算数栈OPND不空(运算符栈OPTR仅剩'\n')
   {
     printf("表达式不正确\n");
     exit(ERROR);
   }
   return x;
 }

 int main()
 {
   printf("请输入算术表达式,负数要用(0-正数)表示\n");
   printf("%d\n",EvaluateExpression());
 }


4. 栈与递归 ——hanoi塔

#include <stdio.h>

int move_times = 0; 	// 全局变量, 搬动次数

// 第n块圆盘从塔座x 搬到塔座z
void move(char x, int n, char z)
{
	printf("第%i步: 将%i号圆盘从%c移到%c\n", ++move_times, n, x, z);
}

// 将塔座x上按直径由小到大且自上而下编号为1至n的n个圆盘
// 按规则搬到塔座z上,y可以做辅助塔座
void hanoi(int n, char x, char y, char z)
{
	// 出口
	if (1 == n)
	{
		move(x, 1, z);
	}
	else
	{
		hanoi(n-1, x, z, y);	// 将x上编号为1至n-1的圆盘移动y上,z做辅助塔座(降阶递归调用)
		move(x, n, z);			// 将编号为n的圆盘由x移到z上
		hanoi(n-1, y, x, z);	// 将y上编号为1至n-1的圆盘移动x上,x做辅助塔座(降阶递归调用)
	}
}

int main()
{
	int n;
	printf("3个塔座为x,y,z,圆盘最初在x上,借助y座移动z座。请输入圆盘数:");
	scanf("%d", &n);
	hanoi(n, 'x', 'y', 'z');
	return 0;
}

5. 括号匹配

#include "ds.h"

typedef char SElemType;


#define STACK_INIT_SIZE 100
#define STACK_INCREMENT  10

typedef struct {
	SElemType 		*base;
	SElemType 		*top;
	int 	  		stacksize;
}SqStack;


void InitStack(SqStack &S);
Status StackEmpty(SqStack S);
int StackLength(SqStack S);
Status GetTop(SqStack S, SElemType &e);
void Push(SqStack &S, SElemType e);
Status Pop(SqStack &S, SElemType &e);
void StackTraverse(SqStack S, void(* visit)(SElemType));


// 构造一个空栈S
void InitStack(SqStack &S)
{
	S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
	if (!S.base) exit(OVERFLOW);
	
	S.top = S.base;
	S.stacksize = STACK_INIT_SIZE;
}

// 若栈S为空栈,则返回TRUE,否则返回FALSE
Status StackEmpty(SqStack S)
{
	if (S.top == S.base)
		return TRUE;
	else
		return FALSE;
}

// 返回S的元素个数,即栈的长度
int StackLength(SqStack S)
{
	return S.top - S.base;  // not return S.stacksize;
}

// 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
Status GetTop(SqStack S, SElemType &e)
{
	if (S.top > S.base)
	{
		memcpy(&e, S.top - 1, sizeof(SElemType));
		return OK;
	}
	else
	{
		return ERROR;
	}
}

// 插入元素e为新的栈顶元素
void Push(SqStack &S, SElemType e)
{
	if (S.top - S.base >= S.stacksize)
	{
		S.base = (SElemType*)realloc(S.base, (S.stacksize + STACK_INCREMENT) * sizeof(SElemType));
		if (!S.base) exit(OVERFLOW);
		
		S.top = S.base + S.stacksize;
		S.stacksize += STACK_INCREMENT;
	}
	
	memcpy(S.top, &e, sizeof(SElemType));
	S.top++;
}

// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqStack &S, SElemType &e)
{
	if (S.top == S.base)
		return ERROR;
	
	memcpy(&e, --S.top, sizeof(SElemType));
	return OK;
}

// 从栈底到栈顶依次对栈中每个元素调用函数visit()
void StackTraverse(SqStack S, void(* visit)(SElemType))
{
	SElemType *p = S.base;
	
	while(p < S.top)
	{
		visit(*p++);
	}
	printf("\n");
}

void print(SElemType c)
{
	printf("%d ",c);
}

// 检查括号是否匹配
void bracketcheck()
{
	SqStack S;
	SElemType str[80], *p, e;
	InitStack(S);
	printf("请输入带括号(()、[] 和 {})的表达式\n");
	gets(str);
	p = str;
	while (*p)
	{
		switch (*p)
		{
			case '(':
			case '[':
			case '{':
				Push(S, *p++);
				break;
				
			case ')':
			case ']':
			case '}':
				if (!StackEmpty(S))
				{
					Pop(S, e);
					if ( !(( '(' == e && ')'== *p )
						 ||( '[' == e && ']'== *p )
						 ||( '{' == e && '}'== *p ))
						)
					{
						// 出现 3 种情况以外时
						printf("NO 左右括号不配对\n");
						exit(ERROR);
					}
				}
				else // 栈空
				{
					printf("miss ( 缺乏左括号\n");
					exit(ERROR);
				}
			default:
				p++; //其他字符不做处理,指针后移
		}
	}
	
	if (StackEmpty(S))
		printf("yes 括号匹配\n");
	else
		printf("no 缺乏右括号\n");
}

int main()
{
	bracketcheck();
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值