顺序栈:一种存储结构为顺序具有先进后出性质的线性表。
穷举法走迷宫:就是探索式走法,设定一个探索顺序和初始方向比如开始探索上,逆时针轮,上、左、下、右。
我的迷宫是用一个1010的字符数组存储的。
栈对与实现穷举法实现迷宫具有独特的优势。
这种优势总结为一点就是吞吐轨迹点。
怎么讲呢,从上往下第一个‘’(星号)为起点,第二个为终点。
我这里是设开始方向为右侧,按顺时针方向转。即右、下、左、上。
从起点开始探索,当探索到的空间为“ ”(空格)时该坐标点入栈,该点标记为“@”。
当探索到的空间不为“ ”(空格时)换一个方向,当所有方向都探索完毕,而仍无路可走时,该坐标点出栈并标记为“!”(感叹号)。直到找到第二个‘*’(星号),探索完毕。
我们知道,栈的特点是先进后出,所以如果直接将栈遍历输出,得到的坐标路径是反的。若想要正的就得再找建立一个栈,或者数组,将路径坐标正序输出。
在栈的结构体的设计我是这么写的
typedef struct stack {
elemtype* top;
elemtype* base;
int startsize;//初始空间 当栈的空间不足时可用以做开辟空间的标准 当你在走一个大迷宫 //的时候就排的上用场了
}stack, *pstack;
top,base,是两个元素指针,存元素的地址。
元素数据含有轨迹点坐标的信息(x,y);
一个十位数,十位上是x,个位上是y;
读取的时候用算法处理好就行了。
下面是map.h 代码
#pragma once
#include<malloc.h>
#include<Windows.h>
#define Beginsize 100
#define Incrementsize 10
#define elemtype int
char map[10][10] = { '#','#','#','#','#','#','#','#','#','#',
'#',' ',' ','#',' ',' ',' ','#',' ','#',
'#',' ',' ','#',' ',' ',' ','#',' ','#',
'#',' ',' ',' ',' ','#','#',' ',' ','#',
'#',' ','#','#','#',' ',' ',' ',' ','#',
'#',' ',' ',' ','#',' ',' ',' ',' ','#',
'#',' ','#',' ',' ',' ','#',' ',' ','#',
'#',' ','#','#','#',' ','#','#',' ','#',
'#','#',' ','*',' ',' ',' ',' ',' ','#',
'#','#','#','#','#','#','#','#','#','#', };
typedef struct stack {
elemtype* top;
elemtype* base;
int startsize;
}stack, *pstack;
int InitStack(pstack p)
{
p->base = (elemtype*)malloc(Beginsize * sizeof(elemtype));
if (!p->base) return 0;
p->top = p->base;
p->startsize = Beginsize;
}
void ClearStack(pstack p) //清空栈
{
p->top = p->base;
}
void DestroyStack(pstack p)
{
free(p);
}
int Push(pstack p, elemtype e) //入栈
{
if (p->top - p->base >= p->startsize)
{
p->base = (elemtype*)realloc(p->base, (p->startsize + Incrementsize) * sizeof(elemtype));
if (!p->base) return 0;
p->top = p->base + p->startsize;
p->startsize += Incrementsize;
}
*p->top++ = e;
}
int Pop(pstack p, elemtype* e)//出栈
{
if (p->base == p->top) return 0;
*e = *--p->top;
}
void StackPutAll(pstack p) //全栈滚蛋
{
elemtype e;
while (p->base != p->top)
{
Pop(p, &e);
printf("%d", e);
}
printf("\n");
}
int StackEmpty(pstack p) // 判断栈是否为空
{
if (p->base == p->top) return 0;
else
return 1;
}
void Gettop(pstack p, elemtype* e) // 栈顶元素的获取
{
if (StackEmpty(p))
*e = *(p->top - 1);
}
void gotoxy(int x, int y)//光标移动
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
void PutMap() //打印地图及轨迹
{
int i, j;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
printf("%c", map[i][j]);
printf("\n");
}
}
void WayExplore(pstack p) //穷举走法
{
int i = 1;
int j = 1;
elemtype a;
//int wei;
bool row=0;
while (1)
{
while (1)
{
//右走 1 右
if (map[i][j+1] == ' ')
{
a = i * 10 + (++j)*1;
Push(p, a);
map[i][j] = '@';
break;
}
//下走 2下走
if (map[i+1][j] == ' ')
{
a = (++i) * 10 + j*1;
Push(p, a);
map[i][j] = '@';
break;
}
//左走
if (map[i][j - 1] == ' ')
{
a = i * 10 + (--j)*1;
Push(p, a);
map[i][j] = '@';
break;
}
//上走
if (map[i-1][j] == ' ')
{
a = (--i) * 10 + j*1;
Push(p, a);
map[i][j] = '@';
break;
}
row = 1;
break;
}//走步
if (row == 1) //后退
{
Pop(p,&a);
i = a / 10;
a %= 10;
j = a ;
// wei = a % 10;
if (map[i][j + 1] == ' ' || map[i + 1][j] == ' ' || map[i][j - 1] == ' ' || map[i - 1][j] == ' ')
{
a = i * 10 + j * 1 ;
Push(p, a);
}
else
if(map[i][j]!='#')
map[i][j] = '!';
row = 0;
}
PutMap();
if (map[i][j + 1] =='*'|| map[i + 1][j]=='*'|| map[i][j - 1] == '*'|| map[i - 1][j] == '*')
break; //结束
Sleep(10); //延时函数 让其探索过程放慢 一种观察手段
gotoxy(0, 0); //把光标放在起始位
}
}
我执行完这个程序看到的是
这各图我觉得不好看,于是我重新折腾了一下,让这个栈输出到另一个栈里去
再创一个图,在输出新栈的同时,在新图上标出轨迹,感叹号没了,还可以看到它走一波麻溜的正确路径。得到下图
代码如下同样放在map.h中
void WayTrack(pstack p,pstack q) //轨迹重输
{
int i, j,k=0;
int e[50];
system("cls");
char NewMap[10][10] = { '#','#','#','#','#','#','#','#','#','#',
'#','*',' ','#',' ',' ',' ','#',' ','#',
'#',' ',' ','#',' ',' ',' ','#',' ','#',
'#',' ',' ',' ',' ','#','#',' ',' ','#',
'#',' ','#','#','#',' ',' ',' ',' ','#',
'#',' ',' ',' ','#',' ',' ',' ',' ','#',
'#',' ','#',' ',' ',' ','#',' ',' ','#',
'#',' ','#','#','#',' ','#','#',' ','#',
'#','#',' ','*',' ',' ',' ',' ',' ','#',
'#','#','#','#','#','#','#','#','#','#', };
elemtype a;
while (p->top!=p->base)
{
Pop(p, &a);
Push(q, a);
} //顺序倒置
while (q->top != q->base)
{
gotoxy(0, 0);
Sleep(100);
Pop(q, &a);
e[k++] = a;
i = a / 10;
a %= 10;
j = a ;
NewMap[i][j] = '@';
for (int num = 0; num < 10; num++)
{
for (int numc = 0; numc < 10; numc++)
printf("%c", NewMap[num][numc]);
printf("\n");
}
}
for (int num=0; num < k; num++)
{
i = e[num]/ 10;
e[num] %= 10;
j = e[num] ;
printf("(%d,%d)->", i, j);
}
printf("\n");
}
主函数代码如下
#include<stdio.h>
#include "map.h"
int main()
{
elemtype e;
stack Sa;
stack Sa2;
InitStack(&Sa);
InitStack(&Sa2);
WayExplore(&Sa);
WayTrack(&Sa, &Sa2);
system("pause");
return 0;
}
我这里用了另外两个函数,一个是Sleep()函数,在windows.h头文件中作用是放慢探索过程让我们可以更好地观察算法的正确与否。Sleep(1000)=1s;(Sleep的S是大写)
另一个是光标移动函数
void gotoxy(int x, int y)//光标移动
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
光标移动函数顾名思义就是改变光标的位置
由于我要实现的让程序的探索过程可视化,所以每走一步就需要打印出一个图。
开始我用的刷屏函数,但是刷屏函数用起来很不好看,它会让屏幕一闪一闪。
而改变光标位置就不会有这个问题了。
光标移动函数原理
链接里的函数和上面这个是一样的,只不过上面的更清爽些。
欢迎留言讨论!