破“栈”式

栈的原理

在这里插入图片描述
如同图里的小汽车是排成一条直线,是线性排列,而且只能从一端进出,后进的汽车先出去,后进 先出(Last In First Out,LIFO),这就是"栈"。栈也是一种线性表,只不过它是操作受限的线性 表,只能在一端操作。 进出的一端称为栈顶(top),另一端称为栈底(base)。栈可以用顺序存储,也可以用链式存储。

在这里插入图片描述
其中,base 指向栈底,top 指向栈顶。

注意:栈只能在一端操作,后进先出,这是栈的关键特征,也就是说不允许在中间查找、取值、插入、删除等 操作,我们掌握好顺序栈的初始化、入栈,出栈,取栈顶元素等操作即可。

顺序栈的实现

//栈数据结构定义
#define MaxSize 128 //预先分配空间,这个数值根据实际需要预估确定 

typedef int ElemType; 

typedef struct _SqStack{ 
	ElemType *base; //栈底指针 
	ElemType *top; //栈顶指针 
}SqStack;

在这里插入图片描述

bool InitStack(SqStack &S) 
//构造一个空栈 S 
{ 
	//为顺序栈分配一个最大容量为 Maxsize 的空 间 
	S.base = new int[MaxSize];
	
	//空间分配失败
	if (!S.base)  return false; 
	S.top=S.base; //top 初始为 base,空栈 
	return true; 
}


在这里插入图片描述

// 插入元素 e 为新的栈顶元素
bool PushStack(SqStack &S, int e)  { 
	
	//栈满
	if (S.top-S.base == MaxSize)  return false; 
	*(S.top++) = e; /*元素 e 压入栈顶,然后栈顶指针加 1,
					等价于*S.top=e; S.top++;*/ 
	return true; 
}


出栈

出栈操作: 和入栈相反,出栈前要判断是否栈空,如果栈是空的,则出栈失败,否则将栈顶元素暂存给一个变 量,栈顶指针向下移动一个空间(top- -)。

//删除 S 的栈顶元素,暂存在变量 e 中
bool PopStack(SqStack &S, ElemType &e) {
	//栈空 
	if (S.base == S.top){  
		return false; 
	}
	
	e = *(--S.top); //栈顶指针减 1,将栈顶元素赋给 e 
	return true; 
}


获取栈顶元素
取栈顶元素和出栈不同,取栈顶元素只是把栈顶元素复制一份,栈的元素个数不变,而出栈是指栈顶元素取出, 栈内不再包含这个元素。

ElemType GetTop(SqStack &S) //返回 S 的栈顶元素,栈顶指针不变 
{ 
	if (S.top != S.base){ //栈非空 
		return *(S.top - 1); //返回栈顶元素的值,栈顶指针不变 
	}else{ 
		return -1; 
	} 
}


判断空栈

bool IsEmpty(SqStack &S){
	//判断栈是否为空 
	if (S.top == S.base){ 
		return true; 
	}else{
		return false; 
	} 
}


返回栈中元素个数

int GetSize(SqStack &S){//返回栈中元素个数 
	return (S.top-S.base); 
}


销毁栈

void DestoryStack(SqStack &S) { 
	if(S.base){ 
		free(S.base); 
		S.base = NULL; 
		S.top = NULL; 
	} 
}

栈在回溯法中的高级应用

在这里插入图片描述
这幅图熟不熟悉,是我们在推箱子实战中的练习。那么让我们通过回溯法来完成在这个小迷宫中的自动寻路。

maze.h

#pragma once 

#include<stdio.h> 
#include<stdlib.h> 
#define MAXSIZE 100 

typedef struct _Position{//迷宫坐标 
	int _x; 
	int _y; 
}Position; 

#define MaxSize 128 //预先分配空间,这个数值根据实际需要预估确定 

typedef Position ElemType; 

typedef struct _SqStack{ 
	ElemType *base; //栈底指针 
	ElemType *top; //栈顶指针 
}SqStack; 


//构造一个空栈 S
bool InitStack(SqStack &S)  
{ 
	S.base = new ElemType[MaxSize];//为顺序栈分配一个最大容量为 Maxsize 的空间
	
	//空间分配失败 
	if (!S.base) return false; 
	S.top=S.base; //top 初始为 base,空栈 
	return true; 
}


// 插入元素 e 为新的栈顶元素
bool PushStack(SqStack &S, ElemType e) {
	//栈满
	if (S.top-S.base == MaxSize)  return false; 
	*(S.top++) = e; //元素 e 压入栈顶,然后栈顶指针加 1,等价于*S.top=e; S.top++; 
	return true; 
}


 //删除 S 的栈顶元素,暂存在变量 e 中
bool PopStack(SqStack &S, ElemType &e){ 
	if (S.base == S.top){ //栈空 
		return false; 
	}
	
	e = *(--S.top); //栈顶指针减 1,将栈顶元素赋给 e 
	return true; 
}


//返回 S 的栈顶元素,栈顶指针不变
ElemType* GetTop(SqStack &S)  { 
	if (S.top != S.base){ //栈非空 
		return S.top - 1; //返回栈顶元素的地址,栈顶指针不变 
	}else{ 
		return NULL; 
	} 
}


//返回栈中元素个数 
int GetSize(SqStack &S){
	return (S.top-S.base); 
}


//判断栈是否为空
bool IsEmpty(SqStack &S){ 
	if (S.top == S.base){ 
		return true; 
	}else{
		return false; 
	} 
}


//销毁栈
void DestoryStack(SqStack &S){ 
	if(S.base){
		free(S.base); 
		S.base = NULL; 
		S.top = NULL; 
	} 
}

maze.cpp

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include "maze.h" 
#include <assert.h> 

#define ROW 6 
#define COL 6 

typedef struct _Maze{ 
	int map[ROW][COL]; 
}Maze; 


//迷宫的初始化
void InitMaze(Maze* m, int map[ROW][COL])  { 
	for (int i = 0;i< ROW ;++i) { 
		for (int j = 0; j < COL; ++j) { 
			m->map[i][j] = map[i][j]; 
		} 
	} 
}


//打印迷宫
void PrintMaze(Maze* m)  { 
	for (int i = 0; i < ROW; ++i) { 
		for (int j = 0; j < COL; ++j) { 
			printf("%d ",m->map[i][j]); 
		}
		printf("\n"); 
	}
	printf("\n"); 
}


//判断是否是有效的入口
int IsValidEnter(Maze* m, Position cur)  
{ 
	assert(m); 
	
	if ((cur._x == 0 || cur._x == ROW - 1) || 
		(cur._y == 0 || cur._y == COL - 1) && 
		(m->map[cur._x][cur._y] == 1)) 
			return 1; 
	else
			return 0; 
}


//判断当前节点的下一 个节点能否走通
int IsNextPass(Maze* m,Position cur, Position next) { 

	assert(m); 
	
	//判断 next 节点是否为 cur 的下一节点 
	if(((next._x == cur._x) && 
	   ((next._y == cur._y+1)||
	    (next._y == cur._y-1)))//在同一行上并且相邻 
	    ||((next._y == cur._y) && 
	      ((next._x == cur._x+1)||
	      (next._x == cur._x-1)))){//或在同一列上并且相邻 
	
		  //判断下一个节点是否在迷宫里面 
		  if (((next._x >= 0 && next._x < ROW) && 
		       (next._y >= 0 && next._y < COL)) &&
		       (m->map[next._x][next._y] == 1)){ 
		       		return 1; 
		  } 
	}
	return 0; 
}


//判断当前节点是 不是有效的迷宫出口
int IsValidExit(Maze* m, Position cur,Position enter)  { 
		
	assert(m); 
		
	//这里首先得保证该节点不是入口点,其次只要它处在迷宫的边界即可 
	if ((cur._x != enter._x || cur._y != enter._y) && 
	   ((cur._x == 0 || cur._x == ROW - 1) || 
		(cur._y == 0 || cur._y == COL - 1))) { 
		  return 1; 
	}else
		  return 0; 
}


//找迷宫通路
int PassMaze(Maze* m,Position enter,SqStack* s)  { 
	assert(m && IsValidEnter(m,enter) == 1); 
	
	//对给的迷宫的入口进行合法性判断
	Position cur = enter; 
	Position next; 
	
	PushStack(*s, cur); //首先将迷宫的入口压入栈中 
	m->map[cur._x][cur._y] = 2; //将入口值改为 2 
	//PrintMaze(m); 
	while (!IsEmpty(*s)) { 
		cur = *GetTop(*s); 
		//printf("cur: %d %d\n",cur._x, cur._y); 
	
		//判断当前位置是否出口
		if (IsValidExit(m,cur,enter) == 1)  return 1; 

		//尝试向左一步:看当前节点的左一个节点能不能走通 
		next = cur; 
		next._y = cur._y - 1; 
		if (IsNextPass(m, cur, next) == 1) { 
			PushStack(*s, next); 
			m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1; 
			//PrintMaze(m); 
			continue; 
		}
		//尝试向上一步:看当前节点的上一个节点能不能走通 
		next = cur; 
		next._x = cur._x - 1; 
		//next 节点能够走通时,将其压入 栈中
		if (IsNextPass(m,cur,next) == 1)  { 
			PushStack(*s,next); 
			//将 next 节点的值等于 cur 节点的值加 1 
			m->map[next._x][next._y] = m->map[cur._x][cur._y]+1; 
			//PrintMaze(m); 
			continue; 
		}
		//右:看当前节点的向右的一个节点能不能走通
		next = cur; 
		next._y = cur._y + 1; 
		if (IsNextPass(m, cur,next) == 1) { 
			PushStack(*s, next); 
			m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1; 
			//PrintMaze(m); 
			continue; 
		}
		//下:看当前节点的下一个节点能不能走通 
		next = cur; 
		next._x = cur._x + 1; 
		if (IsNextPass(m, cur,next) == 1) { 
			PushStack(*s, next); 
			m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1; 
			//PrintMaze(m); 
			continue; 
		}

		/*走到这里说明当前节点的四个方向都走不通,进行回溯,
		看前一个节点 未被遍历的方向是否还能走通*/
		Position tmp; 
		PopStack(*s, tmp); 
	}
	return 0; 
}


int main() { 
	//用二维数组描绘迷宫:1 代表通路,0 代表墙
	int map[ROW][COL] = {  0,0,1,0,0,0, 
						   0,0,1,1,1,0, 
						   0,0,1,0,0,0, 
						   0,1,1,1,1,0, 
	 				   	   0,0,1,0,1,0, 
						   0,0,0,0,1,0 };

	Maze m; 
	Position enter; 
	//迷宫入口 
	enter._x = 0; 
	enter._y = 2; 
	InitMaze(&m, map); 
	PrintMaze(&m); 
	//system("pause");
	//exit(0); 
	SqStack s; //定义栈,保存已走过的坐标轨迹,便于回溯 
	InitStack(s); //栈的初始 
	int ret = PassMaze(&m,enter,&s); //使用栈和回溯法解开迷宫 

	if(ret){ 
		printf("恭喜你!终于找到了出口~\n"); 
	}else { 
		printf("不是我笨!实在没有出口~\n"); 
	}
	PrintMaze(&m); 
	system("pause"); 
	return 0; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值