迷宮問題(回溯法)

一、Maze.h

#ifndef __Maze_h__
#define __Maze_h__

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <Windows.h>

#define N 6
#define Initsize 5 //初始存儲空間
#define Increment 2 //每次增量

typedef struct Pos
{
	int row;
	int col;
}Pos;

typedef Pos DataType;

typedef struct Maze
{
	int maze[N][N];
	Pos entry;
}Maze;

typedef struct Stack
{
	DataType* _array;
	size_t _size; //有效數據個數 
	size_t _capacity; //容量 
}Stack;

void StackInit(Stack *s);
void CheckCapacity(Stack* s);
void StackPush(Stack* s, DataType x);
void StackPop(Stack* s);
DataType StackTop(Stack* s);
size_t StackSize(Stack* s);
void StackDestroy(Stack* s);

void MazeInit(Maze* m);
void MazePrint(Maze* m);

int MazeCheckIsAccess(Pos pos);

void MazeGetPath(Maze* m, Pos entry, Stack *path);
void MazeGetShortPath(Maze* m, Pos entry, Stack *path, Stack *shortpath, int flag);

#endif __Maze_h__

二、Maze.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "Maze.h"

//棧初始化
void StackInit(Stack* s)
{
	assert(s);
	s->_size = 0;
	s->_capacity = Initsize;
	s->_array = (DataType*)malloc(Initsize*sizeof(DataType));
	assert(s->_array);
	memset(s->_array, 0, s->_capacity * sizeof(DataType));
}

//擴容
void CheckCapacity(Stack* s)
{
	assert(s);
	if (s->_size >= s->_capacity)
	{
		DataType* ptr = (DataType*)realloc(s->_array, (s->_capacity + Increment)*sizeof(DataType));
		assert(ptr);
		s->_array = ptr;
		s->_capacity += Increment;
	}
}

//入棧
void StackPush(Stack* s, DataType x)
{
	assert(s);
	CheckCapacity(s);
	s->_array[s->_size] = x;
	s->_size++;
}

//出棧
void StackPop(Stack* s)
{
	assert(s);
	if (s->_size == 0)
	{
		return;
	}
	else
	{
		s->_size--;
	}
}

//獲取棧頂元素
DataType StackTop(Stack* s)
{
	assert(s&&s->_size > 0);
	return s->_array[(s->_size) - 1];
}

//棧長度
size_t StackSize(Stack* s)
{
	assert(s);
	return s->_size;
}

//銷燬
void StackDestroy(Stack* s)
{
	free(s->_array);
	s->_size = 0;
	s->_capacity = 0;
}

//初始化迷宮
void MazeInit(Maze* m)
{
	assert(m);
	int a[N][N] =
	{
		{ 0, 0, 0, 0, 0, 0 },
		{ 0, 0, 1, 0, 0, 0 },
		{ 0, 0, 1, 1, 1, 0 },
		{ 0, 0, 1, 0, 1, 1 },
		{ 0, 0, 1, 1, 1, 0 },
		{ 0, 0, 1, 0, 0, 0 },
	};
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			m->maze[i][j] = a[i][j];
		}
	}
	m->entry.row = 5;
	m->entry.col = 2;
}

//打印迷宮
void MazePrint(Maze* m)
{
	assert(m);
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			printf("%d ", m->maze[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

//判斷是否爲出口,假設出口在最後一列
int MazeCheckIsAccess(Pos pos)
{
	if (pos.col == N - 1)
	{
		return 1;
	}
	return 0;
}

//單通路標記
void MarkPath(Maze** pm, Pos entry)
{
	(*pm)->maze[entry.row][entry.col] = 2;
}

//尋找單通路——只能處理單條路徑
void MazeGetPath(Maze* m, Pos entry, Stack *path)
{
	assert(m);
	if (entry.row < 0 || entry.row >= N || entry.col < 0 || entry.col >= N) //座標不合法,直接返回
	{
		return;
	}

	if (m->maze[entry.row][entry.col] == 1) //如果是路,就入棧,並進行標記
	{
		StackPush(path, entry);
		MarkPath(&m, entry);
	}
	else //否則,直接返回
	{
		return;
	}

	if (MazeCheckIsAccess(entry)) //判斷是否到達出口
	{
		printf("找到出口!\n");
		printf("出口座標:%d %d\n", StackTop(path).row, StackTop(path).col);
		printf("路徑長度爲:%d\n", StackSize(path));
		printf("路徑爲:");
		for (int i = 0; i < (int)path->_size; i++)
		{
			printf("(%d,%d) ", path->_array[i].row, path->_array[i].col);
		}
		printf("\n");
		StackPop(path); //通路上其他非出口位置是經4個方向都找過後才Pop並返回上一位置;而出口位置是不需要查找4個方向,直接Pop並返回上一位置
		MazePrint(m);
		return;
	}

	//如果沒有找到出口,分別在當前位置的4個方向繼續尋找
	//上
	Pos up_pos = entry;
	up_pos.row--;
	MazeGetPath(m, up_pos, path);
	//下
	Pos down_pos = entry;
	down_pos.row++;
	MazeGetPath(m, down_pos, path);
	//左
	Pos left_pos = entry;
	left_pos.col--;
	MazeGetPath(m, left_pos, path);
	//右
	Pos right_pos = entry;
	right_pos.col++;
	MazeGetPath(m, right_pos, path);

	StackPop(path);
	return;
}

//複製棧
void StackCopy(Stack *src, Stack *dst)
{
	assert(src&&dst);
	if (src->_size == 0)
	{
		dst->_size = 0;
	}
	else
	{
		if (dst->_capacity < src->_size)
		{
			DataType *ptr = (DataType *)realloc(dst->_array, src->_size*sizeof(DataType));
			assert(ptr);
			dst->_array = ptr;
			dst->_capacity = src->_size;
		}
		dst->_size = src->_size;
		memcpy(dst->_array, src->_array, sizeof(DataType)*src->_size);
	}
}
 
//標記通路
void MarkPath1(Maze** pm, Pos entry, int flag)
{
	(*pm)->maze[entry.row][entry.col] = flag;
}

//尋找最短路徑——可處理多條路徑
void MazeGetShortPath(Maze* m, Pos entry, Stack *path, Stack *shortpath, int flag)
{
	assert(m);
	if (entry.row < 0 || entry.row >= N || entry.col < 0 || entry.col >= N) //座標不合法,直接返回
	{
		return;
	}

	if (m->maze[entry.row][entry.col] == 1 || m->maze[entry.row][entry.col]>flag) //如果是路或者有更短的路,就入棧,並進行標記
	{
		StackPush(path, entry);
		MarkPath1(&m, entry, flag++);
	}
	else //否則,直接返回
	{
		return;
	}

	if (MazeCheckIsAccess(entry)) //判斷是否到達出口
	{
		printf("找到出口!\n");
		printf("出口座標:%d %d\n", StackTop(path).row, StackTop(path).col);
		printf("路徑長度爲:%d\n", StackSize(path));
		printf("路徑爲:");
		for (int i = 0; i < (int)path->_size; i++)
		{
			printf("(%d,%d) ", path->_array[i].row, path->_array[i].col);
		}
		printf("\n");

		if (StackSize(shortpath) == 0 || StackSize(path) < StackSize(shortpath))
		{
			StackCopy(path, shortpath); //保存最短路徑
		}

		StackPop(path); //通路上其他非出口位置是經4個方向都找過後才Pop並返回上一位置;而出口位置是不需要查找4個方向,直接Pop並返回上一位置
		MazePrint(m);
		return;
	}

	//如果沒有找到出口,分別在當前位置的4個方向繼續尋找
	//上
	Pos up_pos = entry;
	up_pos.row--;
	MazeGetShortPath(m, up_pos, path, shortpath, flag);
	//下
	Pos down_pos = entry;
	down_pos.row++;
	MazeGetShortPath(m, down_pos, path, shortpath, flag);
	//左
	Pos left_pos = entry;
	left_pos.col--;
	MazeGetShortPath(m, left_pos, path, shortpath, flag);
	//右
	Pos right_pos = entry;
	right_pos.col++;
	MazeGetShortPath(m, right_pos, path, shortpath, flag);

	StackPop(path);
	return;
}

三、Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "Maze.h"

int main()
{
	Maze m;
	MazeInit(&m);
	MazePrint(&m);

	Stack path;
	Stack shortpath;
	StackInit(&path);
	StackInit(&shortpath);

	MazeGetShortPath(&m, m.entry, &path, &shortpath, 1);

	printf("最短路徑長度爲:%d\n", StackSize(&shortpath));
	printf("路徑爲:");
	for (int i = 0; i < (int)shortpath._size; i++)
	{
		printf("(%d,%d) ", shortpath._array[i].row, shortpath._array[i].col);
	}
	printf("\n");

	StackDestroy(&path);
	StackDestroy(&shortpath);

	return 0;
}

執行結果

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章