导航图寻路(一)

8 篇文章 1 订阅

通用算法

我们先来讲基础的算法,树形结构基本都能使用的算法。

辅助函数

辅助函数和类,为了书写方便,path使用了类似c++迭代器的设计。

int nextEdge(VertexBase v, List<bool> visited)
        {
            for (int i = 0; i < v.Edges.Count; ++i)
            {
                if (visited[v.Edges[i].To] == false)
                    return v.Edges[i].To;
            }

            return 0;
        }

public class Path
    {
        public List<VertexBase> _verts = new List<VertexBase>();
        int _curCount = 0;

        public Path()
        {
            Count = 0;
        }

        public VertexBase Begin()
        {
            _curCount = 0;
            return _verts[0];
        }

        public VertexBase Next()
        {
            return _verts[++_curCount];
        }

        public bool isEnd()
        {
            return _curCount == _verts.Count;
        }

        public void AddVertex(VertexBase v)
        {
            _verts.Add(v);
            Count++;
        }

        public int Count { set; get; }
    }

DepthFirstSearch深度优先

深度优先算法的实现,往往可以通过递归和使用栈来做(完全等效),此处我们使用栈,比递归更容易理解,而且在一个while循环中,避免了在函数中对参数的拷贝或引用。

public Path DepthFirstSearch(int begin, int end)
        {
            Path path = new Path();

            //如果begin没有下一条边,直接结束
            if (GetVertByIdx(begin).Edges.Count == 0)
                return path;

            //存放已经访问的边
            List<bool> vertsVisited = new List<bool>();
            vertsVisited.Add(true);//0号位置为不可访问
            for (int i = 1; i <= _verts.Count; ++i)
            {
                vertsVisited.Add(false);
            }

            //搜索,此处为了存放path和避免path的复制操作,不用递归改用栈
            Stack<int> pathStack = new Stack<int>();

            pathStack.Push(begin);
            vertsVisited[begin] = true;

            while (pathStack.Count != 0)
            {
                int curPos = pathStack.Peek();
                int next = nextEdge(GetVertByIdx(curPos), vertsVisited);
                if (next == 0)
                {
                    pathStack.Pop();
                }
                else if (next == end)
                {
                    pathStack.Push(GetVertByIdx(next).Index);
                    break;
                }
                else
                {
                    pathStack.Push(next);
                    vertsVisited[next] = true;
                }
            }

            if (pathStack.Count == 0)
            {
                return path;
            }

            //此处已经得到结果了,但是栈的结构是刚好相反的
            //反序列化
            Stack<int> rPath = new Stack<int>();
            while (pathStack.Count != 0)
            {
                rPath.Push(pathStack.Pop());
            }

            while (rPath.Count != 0)
            {
                path.AddVertex(GetVertByIdx(rPath.Pop()));
            }

            return path;
        }

由于我们之前对边进行了排序,所以深度优先搜索在每一层有访问优先级,但其实并非最优解(加起来的权值并非最优),注意,一定要标记已经访问过的顶点,避免重复访问造成死循环

BreadFirstSearch广度优先搜索

广度优先搜索一直不被笔者喜欢,因为它的空间消耗太大了,但是在无权图中(或者等权图),是一种最简单,也应该是最快速的最优解。

广度优先搜索也可以用递归来实现,每条路径组合保存一个path结构,直到有一条到达目标,然后return整个函数,释放所有的path。注意,c#中=传的是引用,请一定实现clone方法对path进行数据拷贝

此处我们依然使用while循环,然后用队列来实现对每一层的访问。

public Path BreadFirstSearch(int begin, int end)
        {
            if (GetVertByIdx(begin).Edges.Count == 0)
                return new Path();

            Queue<EdgeBase> edgeQueue = new Queue<EdgeBase>();

            List<int> route = new List<int>();
            route.Add(0);//0号元素置空
            for (int i = 0; i < _verts.Count; ++i)
            {
                route.Add(0);
            }

            List<bool> visited = new List<bool>();
            visited.Add(true);
            for (int i = 1; i <= _verts.Count; ++i)
            {
                visited.Add(false);
            }

            EdgeBase temp = new EdgeBase();
            temp.To = begin;
            temp.From = begin;
            edgeQueue.Enqueue(temp);
            visited[begin] = true;

            while (edgeQueue.Count != 0)
            {
                var next = edgeQueue.Dequeue();

                //这一句很关键,你可以画一个图,自己模拟一下
                route[next.To] = next.From;

                if (next.To == end)
                    break;
                else
                {
                    VertexBase v = GetVertByIdx(next.To);
                    foreach (var e in v.Edges)
                    {
                        if (visited[e.To] == false)
                        {
                            edgeQueue.Enqueue(e);
                            visited[e.To] = true;
                        }
                    }
                }
            }

            //反序列化
            Stack<int> pathStack = new Stack<int>();
            pathStack.Push(end);
            while (pathStack.Peek() != begin)
            {
                pathStack.Push(route[pathStack.Peek()]);
            }

            //序列化
            Path path = new Path();
            while (pathStack.Count != 0)
            {
                path.AddVertex(GetVertByIdx(pathStack.Pop()));
            }

            return path;
        }

和DFS(深度优先缩写)中保存路径为一个栈不同,广度优先保存的是当前所有顶点的最新来源,代码中的route,我们可能得到如下的一个route数组:

indexdata/int
00
10
21
30
42

假设我们是计算了BFS(1,4)得到的结果,那么我们可以看出,4的上一个顶点是2,2的上一个顶点是1
所以结果路径就是:

1 -> 2 -> 4

对比

我们对比两个算法,DFS能最快地找到一条路径通往目标,但是它不讨论是否能得到最短的路径,后面我们会讨论一些复杂的算法,他们通过一些对比,实现了最短路径,但是,绝大部分在性能上和DFS都有差距。
BFS能得到一条不带权的最短路径,但是假设最短的路径有n条边,那么前面的n-1层顶点,全部都被访问过,但是在很多情况下,BFS依然是很有用的算法,比如在很多等距网格中,BFS能比较快速(程序员编写速度 :-D)的完成目标。

To be continued…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值