(C++)資料結構實驗二——迷宮問題

DeSt!ny發表於2020-11-11

BFS演算法迷宮

#include <iostream>
#include <string>
//#include <queue>
#include "QueueBottom.h"
#include <fstream>
using namespace std;
#include <iomanip>
#include <Windows.h>

#include <time.h>
clock_t start,finish;
   
   
//改變字型顏色
void setColour(int x){
	HANDLE h = GetStdHandle (-11);
	SetConsoleTextAttribute(h,x);
}

//四個方向
int maze[100][100], past_footprint[100][100];
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};

//儲存最終的路徑的陣列
int final_array[100][100];

int printmaze[100][100];
int second_printmaze[100][100];

int whole_step;
struct point
{
	int x;
	int y;
	int step;
};
queue<point> now_footprint;
queue<point> second_footprint;
void printMaze(int& endx, int& endy, int& startx, int& starty, int& length, int& width, string mapname)
{
	ifstream first(mapname, ios::in);
	if (!first.is_open())
	{
		cout << "Error: opening file fail" << endl;
		system("pause");
		exit(1);
	}
	while (!first.eof())
	{
		first >> length >> width ;
		for (int i = 0; i < length; i++)
			for (int j = 0; j < width; j++)
				first >> maze[i][j];
		first  >> endx >> endy >> startx >> starty;
	}
	// //之後可以改成從txt檔案中讀取
	// //輸入豎向長度x
	// cout << "Please Enter the length of maze:" << endl;
	// cin >> length ;
	
	// //輸入橫向長度y
	// cout << "Please Enter the width of maze:" << endl;
	// cin >> width ;
	
	// //輸入迷宮的形狀,1為通路,2為障礙,沒有邊框
	
	// cout << "Enter the whole maze:" << endl;
	// for (int i = 0; i < length; i++)
	// 	for (int j = 0; j < width; j++)
	// 		cin >> maze[i][j];

	// //輸入迷宮的起點	
	// cout << "Please Enter the start of maze" << endl;
	// cin >> endx >> endy ;

	// //輸入迷宮的終點
	// cout << "Please Enter the end of maze" << endl;
	// cin >> startx >> starty ;
}
void findRoad(int startx, int starty, int endx, int endy, int length, int width)
{
	//BFS
	//隊首元素初始化
	point start;
	start.x = startx;
	start.y = starty;
	start.step = 0;
	
	int tag = 1;

	now_footprint.push(start);
	past_footprint[startx][starty] = 1;

	cout << "The Map before function :" << endl;
	cout << " 1 for road; 2 for wall " << endl << endl;

	for(int m = 0; m < length;m++)
	{
		for(int n = 0; n < width;n++)
		{
			cout << maze[m][n] << "  ";
		}
		cout << endl;
	}
	cout << endl;

	//終點標誌
	int flag = 0;
	while(!now_footprint.empty())
	{
		//將該佇列的首元素的 x y 分別賦予臨時的 x y
		int x = now_footprint.front().x;
		int y = now_footprint.front().y;

		//判斷該佇列的元素是否與終點匹配
		printmaze[x][y] = now_footprint.front().step;
		whole_step = now_footprint.front().step;
		if(x==endx && y==endy)
		{
			flag = 1;
			cout << "The shortest road from start to end is : "<< now_footprint.front().step << endl ;
			cout << "It start at "<< startx << " " << starty << endl;
			cout << "It end at " << endx << " " << endy << endl;
			cout << "It start with number 0 and end with number " <<now_footprint.front().step << endl;
			break;
		}

		//探路擴充迴圈
		for(int k = 0; k <= 3; k++)
		{
			//建立臨時變數tx ty
			int tx,ty;
			//按下 右 上 左的順序去探路
			tx = x + dx[k];
			ty = y + dy[k];
			if(maze[tx][ty] == 1 && past_footprint[tx][ty] == 0)
			{
				//若地點為未探索的點,則建立一個臨時變數temp,將它入隊
				point temp;
				temp.x = tx;
				temp.y = ty;
				temp.step = now_footprint.front().step + 1;
				now_footprint.push(temp);

				//並設定為已訪問
				past_footprint[tx][ty] = 1;
			}
		}
		//將已經擴充完的首節點出隊
		
		now_footprint.pop();
		
	}
	//若找不到終點,則返回no answer
	if (flag == 0)
		cout << "No answer" << endl;

	//將出口置為特殊符號
	//因為我的vscode會亂碼所以只能暫時改成顏色識別符號 :D
	
	setColour(12);
	cout << setw(4) <<"#";
	setColour(15);

	cout << endl;
	for(int m = 0; m < length;m++)
		{
			for(int n = 0; n < width;n++)
			{
				//需要包含標頭檔案iomanip
				//讓每一個輸出的寬度都為4
				cout<<setw(4)<<printmaze[m][n];

				//下面這行程式碼可以使得輸出的字元左對齊
				//<<setiosflags(ios::left)
				//cout << printmaze[m][n] << "  ";
			}
			cout << endl;
		}
	cout << endl;

	
}
void again_findRoad(int startx, int starty, int endx, int endy, int length, int width)
{
	for(int i = 0; i <= length; i++)
		for(int j = 0; j <= width; j++)
			past_footprint[i][j] = 0;

	point start;
	start.x = endx;
	start.y = endy;
	start.step = 0;
	int printmaze[100][100];
	int tag = 1;

	second_footprint.push(start);
	past_footprint[endx][endy] = 1;

	int flag = 0;
	while(!second_footprint.empty())
	{

		int x = second_footprint.front().x;
		int y = second_footprint.front().y;

		second_printmaze[x][y] = second_footprint.front().step;
		if(x==startx && y==starty)
		{
			flag = 1;
			cout << "The shortest road from start to end is : "<< second_footprint.front().step << endl ;
			cout << "It start at "<< endx << " " << endy << endl;
			cout << "It end at " << startx << " " << starty << endl;
			cout << "It start with number 0 and end with number " <<second_footprint.front().step << endl;
			break;
		}
		for(int k = 0; k <= 3; k++)
		{
			int tx,ty;
			tx = x + dx[k];
			ty = y + dy[k];
			if(maze[tx][ty] == 1 && past_footprint[tx][ty] == 0)
			{
				point temp;
				temp.x = tx;
				temp.y = ty;
				temp.step = second_footprint.front().step + 1;
				second_footprint.push(temp);
				past_footprint[tx][ty] = 1;
			}
		}
		second_footprint.pop();
		
	}
	cout << endl;
	for(int m = 0; m < length;m++)
		{
			for(int n = 0; n < width;n++)
			{
				cout<<setw(4)<<second_printmaze[m][n];
			}
			cout << endl;
		}
	cout << endl;

}
void print_final_road(int length, int width, int step)
{
	//step = whole_step;
	for(int m = 0; m < length;m++ )
		for(int n = 0; n < width;n++ )
		{
			if(printmaze[m][n] + second_printmaze[m][n] == step)
			{
				final_array[m][n] = 1;
			}
		}

	for(int m = 0; m < length; m++ )
	{
		for(int n = 0; n < width; n++ )
		{
			// final_array[m][n] = printmaze[m][n] + second_printmaze[m][n] ;
			cout << setw(4) << final_array[m][n];
		}
		cout << endl;
	}


}
int main()
{
	//定義變數
	string mapname;
	int endx, endy, startx, starty, width, length, afterendx, afterendy, aftertag;

	//選擇地圖的號碼
	int tag;

while(1)
{
	cout << "Please Choose the Map of CityPark : "<< endl;
	cout << "Press 0 to exit"						<< endl;
	cout << "-1:ClearScreen			0:Exit" 		<< endl;
	cout << "1:Shenzhen			2:GuangZhou " << endl;
	cout << "3:BeiJing 			4:ShangHai	" << endl;
	cin >> tag;
	cout << endl;

	switch (tag)
	{
	case 1:
	{
		
   /*要計算時間的程式碼*/
   		mapname = "ShenZhenMap.txt";
		printMaze(endx, endy, startx, starty, length, width, mapname);
		start=clock();
		findRoad(endx, endy, startx, starty, length, width);
		again_findRoad(endx, endy, startx, starty, length, width);
		finish=clock();
		print_final_road(length,width,whole_step);
		double totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
   		cout<<"\ntime cost: "<<totaltime<<"seconds!"<<endl;
		break;
	}
	case 2:
	{	
		mapname = "GuangZhouMap.txt";
		printMaze(endx, endy, startx, starty, length, width, mapname);
		findRoad(endx, endy, startx, starty, length, width);
		again_findRoad(endx, endy, startx, starty, length, width);
		print_final_road(length,width,whole_step);
	

		break;
	}
	case 3:
	{	
		mapname = "BeiJingMap.txt";
		printMaze(endx, endy, startx, starty, length, width, mapname);
		findRoad(endx, endy, startx, starty, length, width);
		again_findRoad(endx, endy, startx, starty, length, width);
		print_final_road(length,width,whole_step);

		break;
	}
	case 4:
	{	
		mapname = "ShangHaiMap.txt";
		printMaze(endx, endy, startx, starty, length, width, mapname);
		findRoad(endx, endy, startx, starty, length, width);
		again_findRoad(endx, endy, startx, starty, length, width);
		print_final_road(length,width,whole_step);

		break;
	}
	case 0:
	{
		cout << "Exit" << endl;
		system("pause");
		exit(1);
	}
	case -1:
	{
		system("cls");
		break;
	}
	
	default:
		break;
	}
}
	// printMaze(endx, endy, startx, starty, length, width);
	// findRoad(endx,endy,startx,starty,length,width);
	system("pause");
	return 0;
}

BFS演算法迷宮

#include<iostream>
using namespace std;
#include <time.h>

//地圖陣列,用來儲存迷宮
int map[50][50];
bool visit[50][50];
int ans[250];
int res[250];
int tmp[250];
int k,Min,len;

//以下為增加內容
#include <fstream>
int length, width, startx, starty, endx, endy;
string mapname;

//判斷方向的陣列
int X[4]={-1,1,0,0};
int Y[4]={0,0,-1,1};

clock_t start,finish;
   


void readTxt(string mapname)
{
    ifstream first(mapname, ios::in);
	if (!first.is_open())
	{
		cout << "Error: opening file fail" << endl;
		system("pause");
		exit(1);
	}
	while (!first.eof())
	{
		first >> length >> width ;
		for (int i = 0; i < length; i++)
			for (int j = 0; j < width; j++)
				{
                    first >> map[i][j];
                    visit[i][j]=false;
                }
		first >> startx >> starty >> endx >> endy ;
	}
}
void dfs(int startx,int starty,int step,int k){

    int x = startx;
    int y = starty;

    //判斷當前格子的xy是否在0~5範圍內
    //若否則直接返回
    //這個判斷條件可以防止邊界越界的情況 如x = -1, y = 0
    if(x>=length||x<0||y>=width||y<0)return;
    
    //當該點走過,則退出該層遞迴
    if(visit[x][y])return;
    
    //當該點為牆,則退出該層遞迴
    if(map[x][y]==1)return;

    //當xy為終點座標時執行
    if(x==endx&&y==endy){
        if(step<Min){
            ans[k]=x*10+y;
            //將步數用Min儲存,用於下次第二次通路的長度比較
            Min=step;
            for(int i=0;i<=k;i++){
                res[i]=ans[i];
            }
            //全域性變數,用於列印路徑
            len=k;
        }
        return;
    }
    //將走過的地方置為true
    visit[x][y]=true;
    ans[k]=x*10+y;

    //使用遞迴來判斷某一方向是否能走
    for(int i=0;i<4;i++){
        dfs(x+X[i],y+Y[i],step+1,k+1);
    }
    
    //注意此語句非常關鍵,既然找最短,則就需要不斷  回溯,因此需要將原來訪問過的標記清除
    visit[x][y]=false;

    //回溯的時候置空,還原
    //當回溯到有分叉路的地方的時候,會走for迴圈中沒有走過的路,同時不會影響這次的尋路
    //新增該語句後就能從兩條路線中選出相對較短的一條
}
int main(){

    //cin >> length >> width >>startx >> starty >> endy >> endx;

    //設定一個極大的值,使得第一次必然進入if迴圈
    Min = 9999;

    //存入輸入的地圖
    // for(int i=0;i<length;i++){
    //     for(int j=0;j<width;j++){
    //         cin>>map[i][j];
    //         visit[i][j]=false;
    //     }
    // }

    readTxt("BeiJingMap.txt");
    //dfs執行
    start=clock();
    dfs(startx,starty,1,0);
    finish=clock();
    
    //列印地圖
    for (int i = 0; i < length; i++)
    {
		for (int j = 0; j < width; j++)
			{
                cout << map[i][j] << " ";
            }
        cout << endl;
    }
    cout << endl;
    //輸出res中儲存的座標
    for(int i=0;i<=len;i++){
        cout<<"("<<res[i]%10<<", "<<res[i]/10<<")"<< "->" ;
        if(i % 5 == 0)
            cout << endl;
    }
    double totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
    cout<<"\ntime cost: "<<totaltime<<"seconds!"<<endl;
    system("pause");
    return 0;
}
/*
10 10

0 1 0 0 0 0 0 0 0 0
0 1 0 1 1 1 0 1 1 0
0 1 0 1 0 1 0 1 0 0
0 1 0 1 0 1 0 1 0 0
0 1 0 0 0 1 0 1 1 1
0 1 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 1 0 1 1 1 0 1 1 0
0 1 0 0 0 1 0 0 1 0
0 1 1 1 1 1 0 1 1 0

0 0
9 9
*/

1.兩個迷宮均要在當前目錄下建立txt檔案
BFS是以1和2組成迷宮
DFS是以0和1組成迷宮

2.DFS沒有使用stack而只是使用了陣列,BFS中使用了queue,同時實現了底層。
程式碼如下

QueueBottom.h


#include <iostream>
using namespace std;

//模板結構體,可以適用於任何型別
template <typename T>
struct node{
    T data;
    node *next;

    //建構函式初始化next和data值
    node(){
        next=NULL;
    }

    //new建立node結點的時候可以選擇是否有初值
    node(T x){
        data=x;
        next=NULL;
    }
};

//模板類,可以適用於任何型別
template <typename T>
class queue{
private :
    node<T> *head,*tail;
    int len=0;
public :
    queue(){
        node<T> *temp=new node<T>();
        head=tail=temp;
        len=0;
    }
    
    //Q.size(); 返回該佇列的長度
    int size(){
        return len;
    }

    //Q.empty(); 返回該佇列是否為空
    bool empty(){
        if(len==0) return true;
        return false;
    }
    
    //頭元素
    T& front(){
        if(len) return head->next->data;
    }

    //尾元素
    T& back(){
        if(len) return tail->data;
    }

    void pop(){
        if(len) {
            head=head->next;
            len--;
        }
    }
    void push(const T a){
        node<T> *temp=new node<T>(a);
        tail->next=temp;
        //?
        tail = temp;
        temp = NULL;
        len++;
    }
};
// int main(){
//     // Queue<string> Q;
//     // Q.push(string("abc1"));
//     // Q.push(string("abc2"));
//     // Q.push(string("abc3"));
//     // cout<<Q.front()<<endl;
//     // cout<<Q.back()<<endl;
//     // Q.pop();
//     // cout<<Q.front()<<endl;
//     // cout<<Q.back()<<endl;
//     system("pause");
//     return 0;
// }

相關文章