数据结构---迷宫问题(不相交集、广度优先搜索)

1.生成迷宫

采用不相交集生成迷宫,主要思路:

<1>生成n*n个迷宫房间;

<2>不断拆房间的墙,直到入口和出口连通(不相交集)。

注意:为了产生迷宫效果,拆掉的不应该有规律,所以应该是随机产生一个房间号,再随机拆掉一堵墙。

//迷宫房间格数
//
int m = 50, n = 50;
//迷宫房间大小
//
int size = 10;
//迷宫起点距离窗口左上角距离d*size
//
int d = 10;
//窗口尺寸
//
int width = 700;
int height = 700;

Bitmap image;
public void Paint_Maze()
{
            image = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(image);
            
            //渲染背景
            //
            g.FillRectangle(new SolidBrush(Color.White), 0, 0, width, height);
            
            //绘制迷宫房间
            //
            for (int i = 0; i < m; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    g.DrawRectangle(new Pen(new SolidBrush(Color.Black),1), new Rectangle((i + d) * size, (j + d) * size, size, size));
                }
            }

            //绘制入口、出口,其中加一是为了拆掉墙时,在与其相交的墙上不会留下断开的现象
            //
            g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(d * size, d * size), new Point((d + 1) * size-1, d * size));    //入口横墙
            g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(d * size, d * size), new Point(d * size, (d + 1) * size-1));    //入口竖墙
            g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(((int)((m * n - 1) % n) + d) * size+1, ((int)((m * n - 1) / n) + 1 + d) * size), new Point(((int)((m * n - 1) % n) + 1 + d) * size, ((int)((m * n - 1) / n) + 1 + d) * size));    //出口横墙
            g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(((int)((m * n - 1) % n) + 1 + d) * size, ((int)((m * n - 1) / n) + d) * size+1), new Point(((int)((m * n - 1) % n) + 1 + d) * size, ((int)((m * n - 1) / n) + 1 + d) * size));    //出口竖墙

            //不相交集
            //
            ADT adt = new ADT();
            adt.InitSet(m * n);
            Random random = new Random();
            while (adt.Find(0) != adt.Find(m * n - 1))
            {
                //随机产生一个房间号
                //
                int room_a = random.Next(m * n);
                List<int> list=new List<int>();
                //检查房间a相邻四间房子a-n(上)、a+n(下)、a-1(左)、a+1(右)是否存在
                //
                if (room_a - n >= 0)
                {
                    list.Add(room_a - n);
                }
                if (room_a + n < m * n)
                {
                    list.Add(room_a + n);
                }
                if (room_a - 1 >= ((int)(room_a / n)) * n)
                {
                    list.Add(room_a - 1);
                }
                if (room_a + 1 < ((int)(room_a / n) + 1) * n)
                {
                    list.Add(room_a + 1);
                }
                //随机产生一个相邻的房间
                //
                int index = random.Next(list.Count);
                int room_b = list[index];
                //检查房间是否连通
                //
                if (adt.Find(room_a) == adt.Find(room_b))
                    continue;
                else
                {
                    int root_a = adt.Find(room_a);
                    int root_b = adt.Find(room_b);
                    //记录哪个房间序号小,靠近入口
                    //
                    int room_min = Math.Min(room_a, room_b);
                    //定义即将拆掉的墙的坐标
                    //
                    int x1, x2, y1, y2;
                    //相差1,拆竖墙;否则拆横墙
                    //
                    if (Math.Abs(room_a - room_b) == 1)
                    {
                        //拆竖墙
                        if (room_a == room_min)     //room_a在room_b前面
                        {
                            x1 = ((int)(room_a % n) + 1+d) * size;
                            y1 = ((int)(room_a / n) + d) * size + 1;
                            x2 = x1;
                            y2 = ((int)(room_a / n) + 1 + d) * size - 1;
                        }
                        else
                        {
                            x1 = ((int)(room_b % n) + 1+d) * size;
                            y1 = ((int)(room_b / n) + d) * size + 1;
                            x2 = x1;
                            y2 = ((int)(room_b / n) + 1 + d) * size - 1;
                        }
                    }
                    else    //拆横墙
                    {
                        if (room_a == room_min)     //room_a在room_b上面
                        {
                            x1 = ((int)(room_a % n) + d) * size + 1;
                            y1 = ((int)(room_a / n)+1+d) * size;
                            x2 = ((int)(room_a % n) + 1 + d) * size - 1;
                            y2 = y1;
                        }
                        else
                        {
                            x1 = ((int)(room_b % n)+d) * size+1;
                            y1 = ((int)(room_b / n) + 1+d) * size;
                            x2 = ((int)(room_b % n) +1+ d) * size - 1;
                            y2 = y1;
                        }
                    }

                    //将room_a和room_b连通
                    //
                    adt.Union(root_a, root_b);
                    g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(x1, y1), new Point(x2, y2));
                }

            }

            //显示图像
            //
            pictureBox1.Image = image;
            image.Save("maze.bmp");
        }

生成的迷宫(50*50):

《数据结构---迷宫问题(不相交集、广度优先搜索)》

2.解迷宫

生成完迷宫后,自然会想怎么去解迷宫,在学完广度优先搜索算法,这个问题便迎刃而解。

思路:找出每个房间与入口(或出口)的路径,具体算法参照广度优先搜索算法,在此不详述。

<1>检测迷宫的连通性

        private Graphic_List DetectMaze(Bitmap bitmap)
        {
            int house_num=m*n;
            Graphic_List g_list = new Graphic_List(house_num, true);
            Vertex cur_vertex;
            Color c = new Color();
            bool IsConnect = true;

            //横向连通检测
            //
            for (int j = 0; j < n; j++)    
            {
                cur_vertex = new Vertex((j*n).ToString());
                g_list.AddVertex(cur_vertex);            //将左边第一个房间添加至邻接表中
                for (int i = 1; i < m; i++)
                {
                    IsConnect = true;
                    cur_vertex = new Vertex((j*n+i).ToString());
                    g_list.AddVertex(cur_vertex);

                    for (int k = 0; k < size; k++)
                    {
                        c = bitmap.GetPixel(k + (int)((d + i - 0.5) * size), (int)((d + j + 0.5) * size));
                        if (c.G == 0)                   //根据G分量判断是否连通
                        {
                            IsConnect = false;
                            break;
                        }
                    }
                    if (IsConnect)                     //有问题,不能只添加单向边
                    {
                        g_list.AddDirectedEdge(new Vertex((j * n + i - 1).ToString()), new Vertex((j * n + i).ToString()));
                        g_list.AddDirectedEdge(new Vertex((j * n + i).ToString()), new Vertex((j * n + i - 1).ToString()));
                    }
                }
            }

            //纵向连通检测
            //
            for (int i = 0; i < m; i++)               //横向计数器
            {
                for (int j = 1; j < n; j++)           //纵向计数器
                {
                    IsConnect = true;
                    cur_vertex = new Vertex((j * m + i).ToString());

                    for (int k = 0; k < size; k++)
                    {
                        c = bitmap.GetPixel((int)((d + i + 0.5) * size), (int)((d + j - 0.5) * size) + k);
                        if (c.G == 0)                  //判断是否连通
                        {
                            IsConnect = false;
                            break;
                        }
                    }
                    if (IsConnect)
                    {
                        g_list.AddDirectedEdge(new Vertex(((j - 1) * m + i).ToString()), new Vertex((j * m + i).ToString()));
                        g_list.AddDirectedEdge(new Vertex((j * m + i).ToString()), new Vertex(((j - 1) * m + i).ToString()));
                    }
                }
            }
            return g_list;
        }


<2>用于初始化广度优先搜索算法中table的函数

private void InitTable(Graphic_List g_list, Vertex start_vertex, Table[] table)
        {
            for (int i = 0; i < g_list.Items.Count; i++)
            {
                table[i] = new Table();
                table[i].Name = g_list.Items[i].Name;
                table[i].Dist = double.PositiveInfinity;
                table[i].Known = false;
                table[i].Path = null;
            }
            if (table[g_list.GetIndex(start_vertex)].Name == start_vertex.Name)
            {
                table[g_list.GetIndex(start_vertex)].Dist = 0;
            }
            else
            {
                throw new Exception("表初始化与图不一致!");
            }
        }

<3>广度优先搜索算法

        /// <summary>
        /// 广度优先搜索算法
        /// </summary>
        /// <param name="g_list">待搜索的图</param>
        /// <param name="start_vertex">搜索起点</param>
        /// <param name="table">搜索结果表</param>
        private void BFS(Graphic_List g_list, Vertex start_vertex, Table[] table)
        {
            int NumVertex = g_list.Items.Count;
            Queue<Vertex> queue;
            Vertex current_vertex, adjacent_vertex;

            queue = Queue<Vertex>.CreateQueue(NumVertex);
            Queue<Vertex>.MakeEmpty(queue);

            Queue<Vertex>.Enqueue(g_list.Items[g_list.GetIndex(start_vertex)], queue);
            while (!Queue<Vertex>.IsEmpty(queue))
            {
                current_vertex = Queue<Vertex>.Dequeue(queue);
                table[g_list.GetIndex(current_vertex)].Known = true;

                adjacent_vertex = current_vertex;
                while (adjacent_vertex.NextVertex != null)
                {
                    adjacent_vertex = adjacent_vertex.NextVertex;
                    if (table[g_list.GetIndex(adjacent_vertex)].Dist == double.PositiveInfinity)
                    {
                        table[g_list.GetIndex(adjacent_vertex)].Dist = table[g_list.GetIndex(current_vertex)].Dist + 1;
                        table[g_list.GetIndex(adjacent_vertex)].Path = current_vertex;
                        Queue<Vertex>.Enqueue(g_list.Items[g_list.GetIndex(adjacent_vertex)], queue);
                    }
                }
            }
        }

<4>绘制迷宫路径

private void DrawPath(Table[] table, Point in_point, Point out_point)
        {
            Bitmap bitmap = (Bitmap)pictureBox1.Image;
            Graphics g = Graphics.FromImage(bitmap);

            int cur_point_index = (int)((out_point.X - d * size) / size) + m * (int)((out_point.Y - d * size) / size);     //出口在table中的索引
            Point cur_point, forward_point;

            while (table[cur_point_index].Path != null)
            {
                string foward_point_name = table[cur_point_index].Path.Name;        //当前点的路径,前一点
                int forward_point_index = Int32.Parse(foward_point_name);           //转换成数字

                cur_point = new Point((int)((cur_point_index % m + d + 0.5) * size), (int)((cur_point_index / m + d + 0.5) * size));
                forward_point = new Point((int)((forward_point_index % m + d + 0.5) * size), (int)((forward_point_index / m + d + 0.5) * size));
                g.DrawLine(new Pen(new SolidBrush(Color.Green), 2), cur_point, forward_point);
                cur_point_index = forward_point_index;
                pictureBox1.Image = bitmap;
            }
        }

<5>其他类或函数

Table:

class Table
    {
        public string Name;
        public bool Known;
        public double Dist;
        public Vertex Path;

        public Table()
        {
            Name = null;
            Known = false;
            Dist = double.PositiveInfinity;
            Path = null;
        }

        public static int GetIndex(Table[] table,string name)
        {
            int Index = -1;
            for (int i = 0; i < table.Length; i++)
            {
                if (table[i].Name == name)
                {
                    Index = i;
                    break;
                }
            }
            return Index;
        }
    }

队列:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Maze
{
    class Queue <T>
    {
        private int Capacity;    //队列容量
        private int Size;        //当前队列中元素个数
        private int Front;       //队头
        private int Rear;        //队尾
        private T[] Data;      //队列数组

        //创建队列
        //
        public static Queue<T> CreateQueue(int numElement)
        {
            Queue<T> queue=new Queue<T>();
            queue.Capacity=numElement;
            queue.Size=0;
            queue.Front=0;
            queue.Rear=0;
            queue.Data=new T[numElement];
            return queue;
        }

        //检查队列是否为空
        //
        public static bool IsEmpty(Queue<T> queue)
        {
            return queue.Size == 0;
        }

        //检查队列是否是满的
        //
        public static bool IsFull(Queue<T> queue)
        {
            return queue.Size == queue.Capacity;
        }

        //清空队列
        //
        public static void MakeEmpty(Queue<T> queue)
        {
            queue.Size = 0;
            queue.Front = 0;
            queue.Rear = 0;        
        }

        //入队
        //
        public static void Enqueue(T X, Queue<T> queue)
        {
            if (IsFull(queue))
            {
                throw new Exception("Full queue");
            }
            else
            {
                queue.Size++;
                queue.Data[queue.Rear] = X;
                queue.Rear++;
                queue.Rear %= queue.Capacity;
            }
            
        }

        //出队
        //
        public static T Dequeue(Queue<T> queue)
        {
            if (IsEmpty(queue))
            {
                throw new Exception("Empty queue");
            }
            else
            {
                T t;
                queue.Size--;
                t=queue.Data[queue.Front];
                queue.Front++;
                queue.Front%=queue.Capacity;
                return t;
            }
        }

        //查询当前队头元素
        //
        public static T GetFront(Queue<T> queue)
        {
            if (!IsEmpty(queue))
            {
                return queue.Data[queue.Front];
            }
            else
            {
                throw new Exception("Empty queue");
            }
        }
        
        //查询当前队尾元素
        //
        public static T GetRear(Queue<T> queue)
        {
            if (!IsEmpty(queue))
            {
                return queue.Data[queue.Rear];
            }
            else
            {
                throw new Exception("Empty queue");
            }
        }

    }
}

图的表示:http://blog.csdn.net/wj080211140/article/details/21442255

解迷宫结果:

《数据结构---迷宫问题(不相交集、广度优先搜索)》

C#中指针用起来不是特别方便,在存储相邻顶和路径是用的嵌套,导致消耗内存过大,这个地方应该在整理修改一下。

对于迷宫问题,想进一步做成一个迷宫游戏或者解非本程序生成的迷宫。

    原文作者:迷宫问题
    原文地址: https://blog.csdn.net/wj080211140/article/details/21439955
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞