1. 程式人生 > >迷宮(棧回溯)

迷宮(棧回溯)

迷宮介紹

在生活中我們玩過很多種走迷宮的小遊戲,遊戲給了一個迷宮,這個迷宮中只有一個入口,我們要從這個入口一直走下去,直到找到出口,這裡出口可能是一個,也可能是多個。在這裡我簡單介紹三種實現起來由易到難的迷宮。

前提說明
我們這裡的座標是x軸正半軸向下伸展,y軸正半軸向右伸展

簡單迷宮

在這裡插入圖片描述

這是一種簡單迷宮,有一個入口和一個出口,我們從入口開始按照左上右下的順序進行嘗試走迷宮,如果都走不了,就開始進行回溯,前提是我們走過的路需要提前標記,這樣回溯的時候就比較方便了。程式碼如下:

maze.h

#pragma once
#include<stdio.h>
#include<windows.h>
#define ROWS 6
#define COLS 6
#define MAX 100
typedef struct {
	int x;
	int y;
}Position;
typedef Position datatype;
typedef struct Stack{
	datatype data[MAX];
	int top;
}Stack;
void GoMaze();

maze.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"maze.h"
int maze[ROWS][COLS] = {
	{ 0,0,0,0,0,0 },
{ 0,0,1,0,0,0 },
{ 0,0,1,0,0,0 },
{ 0,0,1,1,1,0 },
{ 0,0,1,0,1,1 },
{ 0,0,1,0,0,0 }
};//迷宮
void StackInit(Stack *stack)
{
	stack->top = 0;
}
void StackPush(Stack *stack,datatype data)
{
	stack->data[stack->top] = data;
	stack->top++;
}
void StackPop(Stack *stack)
{
	stack->top--;
}
datatype StackTop(Stack *stack)
{
	return stack->data[stack->top - 1];
}
int IsExit(Position pos)
{
	if (pos.y == COLS - 1)
	{
		return 1;
	}
	else
		return 0;
}
int CanPass(Position pos)
{
	if (pos.x < 0 || pos.x >= ROWS)
	{
		return 0;
	}
	if (pos.y < 0 || pos.y >= COLS)
	{
		return 0;
	}
	if (maze[pos.x][pos.y] == 1)
	{
		return 1;
	}
	else
		return 0;
}
void PrintMaze()
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			if (maze[i][j] == 1)
			{
				printf("  ");//路用空格表示
			}
			else if (maze[i][j] == 0)
			{
				printf("■");//牆
			}
			else
			{
				printf("◎");//走過的路進行標記方便回溯
			}
		}
		printf("\n");
	}
}
void GoMaze()
{
	Stack stack;
	StackInit(&stack);
	Position entry = { 5,2 };
	Position pos = entry;
	Position nextpos = pos;

	while (1)
	{
		//標記走過的位置
		maze[pos.x][pos.y] = 2;
		system("cls");//列印前清屏
		PrintMaze();
		Sleep(300);//有間隔效果看起來會好一點
		StackPush(&stack, pos);
		//當前是否走到出口
		if (IsExit(pos))
		{
			printf("找到出口了!\n");
			return;
		}
		//沒有走到出口,按照左上右下的順序進行嘗試
		nextpos.y -= 1;
		if (CanPass(nextpos))
		{
			pos = nextpos;
				continue;
		}
		nextpos = pos;
		nextpos.x -= 1;
		if (CanPass(nextpos))
		{
			pos = nextpos;
			continue;
		}
		nextpos = pos;
		nextpos.y += 1;
		if (CanPass(nextpos))
		{
			pos = nextpos;
			continue;
		}
		nextpos = pos;
		nextpos.x += 1;
		if (CanPass(nextpos))
		{
			pos = nextpos;
			continue;
		}
		//如果都走不了,出棧
		StackPop(&stack);
		pos = StackTop(&stack);
		StackPop(&stack);
	}
}
void test()
{
	
	GoMaze();
}

在這裡插入圖片描述

多通路迷宮(不帶環)

在這裡插入圖片描述

這是第二種迷宮,有一個入口,但是有兩個出口。如果還是用第一種簡單迷宮的方法,很顯然當它找到第一個出口的時候就會停下來,那麼我們如果想要把所有的路走找到,當找到第一個出口的時候我們把出口改成牆繼續找第二個出口,知道棧裡面一個元素都沒有的時候就結束。程式碼如下:

maze.h

#pragma once
#include<stdio.h>
#include<windows.h>
#define ROWS 6
#define COLS 6
#define MAX 100
typedef struct {
	int x;
	int y;
}Position;
typedef Position datatype;
typedef struct Stack{
	datatype data[MAX];
	int top;
}Stack;
void GoMaze();

maze.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"maze.h"
int maze[ROWS][COLS] = {
{ 0,0,0,0,0,0 },
{ 0,0,1,1,1,1 },
{ 0,0,1,0,0,0 },
{ 0,0,1,0,0,0 },
{ 0,0,1,1,1,1 },
{ 0,0,1,0,0,0 }
};
void StackInit(Stack *stack)
{
	stack->top = 0;
}
void StackPush(Stack *stack,datatype data)
{
	stack->data[stack->top] = data;
	stack->top++;
}
void StackPop(Stack *stack)
{
	stack->top--;
}
datatype StackTop(Stack *stack)
{
	return stack->data[stack->top - 1];
}
int StackEmpty(Stack *stack)
{
	if (stack->top == 0)
	{
		return 1;
	}
	else
		return 0;
}
int IsExit(Position pos)
{
	if (pos.y == COLS - 1)
	{
		return 1;
	}
	else
		return 0;
}
int CanPass(Position pos)
{
	if (pos.x < 0 || pos.x >= ROWS)
	{
		return 0;
	}
	if (pos.y < 0 || pos.y >= COLS)
	{
		return 0;
	}
	if (maze[pos.x][pos.y] == 1)
	{
		return 1;
	}
	else
		return 0;
}
void PrintMaze()
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			if (maze[i][j] == 1)
			{
				printf("  ");
			}
			else if (maze[i][j] == 0)
			{
				printf("■");
			}
			else
			{
				printf("◎");
			}
		}
		printf("\n");
	}
}
void GoMaze()
{
	Stack stack;
	StackInit(&stack);
	Position entry = { 5,2 };
	Position pos = entry;
	Position nextpos = pos;

	while (1)
	{
		//標記走過的位置
		maze[pos.x][pos.y] = 2;
		system("cls");//列印前清屏
		PrintMaze();
		Sleep(300);//有間隔效果看起來會好一點
		StackPush(&stack, pos);
		//當前是否走到出口
		if (IsExit(pos))
		{
			printf("找到出口了!\n");
			maze[pos.x][pos.y] = 0;
			goto BACK;
		}
		//沒有走到出口,按照左上右下的順序進行嘗試
		nextpos.y -= 1;
		if (CanPass(nextpos))
		{
			pos = nextpos;
				continue;
		}
		nextpos = pos;
		nextpos.x -= 1;
		if (CanPass(nextpos))
		{
			pos = nextpos;
			continue;
		}
		nextpos = pos;
		nextpos.y += 1;
		if (CanPass(nextpos))
		{
			pos = nextpos;
			continue;
		}
		nextpos = pos;
		nextpos.x += 1;
		if (CanPass(nextpos))
		{
			pos = nextpos;
			continue;
		}
BACK:
		//如果都走不了,回溯
		StackPop(&stack);
		if (StackEmpty(&stack))
		{
			printf("結束\n");
			return;
		}
		pos = StackTop(&stack);
		StackPop(&stack);
	}
}
void test()
{
	
	GoMaze();
}

在這裡插入圖片描述

多通路迷宮(帶環)

在這裡插入圖片描述

這是第三種迷宮,仍然是多通路,不同的是,這次帶了環,和該如何是好呢?這時候我們可以遞迴的思想,利用遞迴的思想在回溯的時候可以不用判斷之前走過的路了。程式碼如下:

maze.h

#pragma once
#include<stdio.h>
#include<windows.h>
#define ROWS 6
#define COLS 6
#define MAX 100
typedef struct {
	int x;
	int y;
}Position;
typedef Position datatype;
typedef struct Stack{
	datatype data[MAX];
	int top;
}Stack;
void GoMazeR();

maze.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"maze.h"
int maze[ROWS][COLS] = {
{ 0,0,0,0,0,0 },
{ 0,1,1,1,1,0 },
{ 0,1,0,0,1,0 },
{ 0,1,0,0,1,0 },
{ 0,1,1,1,1,1 },
{ 0,1,0,0,0,0 }
};
void StackInit(Stack *stack)
{
	stack->top = 0;
}
void StackPush(Stack *stack,datatype data)
{
	stack->data[stack->top] = data;
	stack->top++;
}
void StackPop(Stack *stack)
{
	stack->top--;
}
datatype StackTop(Stack *stack)
{
	return stack->data[stack->top - 1];
}
int StackEmpty(Stack *stack)
{
	if (stack->top == 0)
	{
		return 1;
	}
	else
		return 0;
}
int IsExit(Position pos)
{
	if (pos.y == COLS - 1)
	{
		return 1;
	}
	else
		return 0;
}
int CanPass(Position pos)
{
	if (pos.x < 0 || pos.x >= ROWS)
	{
		return 0;
	}
	if (pos.y < 0 || pos.y >= COLS)
	{
		return 0;
	}
	if (maze[pos.x][pos.y] == 1)
	{
		return 1;
	}
	else
		return 0;
}
void PrintMaze()
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			if (maze[i][j] == 1)
			{
				printf("  ");
			}
			else if (maze[i][j] == 0)
			{
				printf("■");
			}
			else
			{
				printf("◎");
			}
		}
		printf("\n");
	}
}
void GoMazeR(Position pos)
{
	Position nextpos = pos;

		//標記走過的位置
		maze[pos.x][pos.y] = 2;
		system("cls");//列印前清屏
		PrintMaze();
		Sleep(300);//有間隔效果看起來會好一點
		//當前是否走到出口
		if (IsExit(pos))
		{
			maze[pos.x][pos.y] = 1;
			printf("找到出口了!\n");
			return;
		}
		//沒有走到出口,按照左上右下的順序進行嘗試
		nextpos.y -= 1;
		if (CanPass(nextpos))
		{
			GoMazeR(nextpos);
		}
		nextpos = pos;
		nextpos.x -= 1;
		if (CanPass(nextpos))
		{
			GoMazeR(nextpos);
		}
		nextpos = pos;
		nextpos.y += 1;
		if (CanPass(nextpos))
		{
			GoMazeR(nextpos);
		}
		nextpos = pos;
		nextpos.x += 1;
		if (CanPass(nextpos))
		{
			GoMazeR(nextpos);
		}
		//回溯之前把走過的路清空
		maze[pos.x][pos.y] = 1;
}
void test()
{
	Position entry = { 5,1 };
	GoMazeR(entry);
}

在這裡插入圖片描述