广度优先搜索(BreadthFirstSearch)& 迪克斯特拉算法 (Dijkstra's algorithm)

BFS可回答两类问题:

1.从节点A出发,有前往节点B的路径吗?

2.从节点A出发,前往节点B的哪条路径经过的节点最少?

BFS中会用到“队列”的概念。队列是一种先进先出(FIFO, first in first out)的数据结构,与栈不同,栈是后进先出(LIFO, last in first out)的数据结构。

还会用到“字典”的概念。字典在现在很多语言中都存在且广泛使用,字典中的元素是一组<键(key),值(value)>对,key的值是不可以重复的。关于字典的详细内容,网上有很多资料可以查阅。

问题描述:想从你的朋友中找出一个芒果销售商,如果你的朋友中没有,那么就从朋友的朋友中查找。(这里假设名字最后一个字母为“m”的即为芒果销售商)。这样就是从“you”这个节点出发,是否有到“Xm”这个节点的路径的问题。

思路:先从你的朋友开始查找,如果朋友A是芒果销售商,则程序结束,如果不是,则将A的朋友排到队列中。然后检查朋友B是否为芒果销售商,循环,直到找到芒果销售商或者队列中的朋友们都被检查了一遍。因为会有某个人C既是A的朋友又是B的朋友,而C只需要检查一次,因此要分配一个列表用于记录已经检查过哪些朋友了。

Python代码:

>>> from collections import deque

>>> graph = {}
>>> graph["you"]=["alice","bob","claire"]
>>> graph["bob"] = ["anuj","peggy"]
>>> graph["alice"] = ["peggy"]
>>> graph["claire"]=["thom","jonny"]
>>> graph["anuj"]=[]
>>> graph["peggy"]=[]
>>> graph["thom"]=[]
>>> graph["jonny"]=[]

>>> def search(name):
    search_queue = deque()
    search_queue += graph[name]
    searched = []
    while search_queue:
        person = search_queue.popleft()
        if person not in searched:
            if person_is_seller(person):
                print (person + " is a mango seller!")
                return True
            else:
                search_queue += graph[person]
                searched.append(person)
    return False

>>> def person_is_seller(name):
    return name[-1] == ‘m‘

>>> search("you")
thom is a mango seller!
True

C#代码:

namespace Algorithms
{
    public static class BFS
    {
        public static bool BreadthFirstSearch(string name, Dictionary<string,List<string>>graph)
        {
            Queue<string> search_queue = new Queue<string>();
            foreach (var item in graph[name])
                search_queue.Enqueue(item);
            List<string> searched = new List<string>();
            while (search_queue.Count != 0)
            {
                string person = search_queue.Dequeue();
                if (!searched.Contains(person))
                {
                    if (JudgeSeller(person))
                    {
                        Console.WriteLine(person + " is a mango seller!");
                        return true;
                    }
                    else
                    {
                        foreach (var item in graph[person])
                            search_queue.Enqueue(item);
                        searched.Add(person);
                    }
                }
            }
            return false;
        }
        private static bool JudgeSeller(string name)
        {
            if (name[name.Length - 1] == ‘m‘)
                return true;
            return false;
        }
    }
}

测试:

namespace Algorithms
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, List<string>> graph = new Dictionary<string, List<string>>();
            graph["you"] = new List<string>() { "alice", "bob", "claire" };
            graph["alice"] = new List<string>() { "peggy" };
            graph["bob"] = new List<string>() { "anuj", "peggy" };
            graph["claire"] = new List<string>() { "thom", "jonny" };
            graph["anuj"] = new List<string>();
            graph["peggy"] = new List<string>();
            graph["thom"] = new List<string>();
            graph["jonny"] = new List<string>();

            if (!BFS.BreadthFirstSearch("you", graph))
            {
                Console.WriteLine("no mango seller!");
            }
            Console.Read();
        }
    }
}

Dijkstra‘s algorithm 用于计算出最短路径,但是这个算法在使用上有很多限制条件。

问题描述:从A地到B地的各种可能的路径中,哪条路径所用的时间最短。(图中的数字表示从某地到另外某地所用的时间)

图1

思路:记录从A点到其他各个节点的所需的时间,如表1所示(现在还不知道从A到B的时间,则先设置为无穷大)

D 3
C 7
B

表1

从所需时间最短的D点再出发,计算从A经过D到其他个各节点的时间,如表2所示

C 5
B 10

表2

直接前往C点需要的时间为7,而经过D点前往C点所需的时间为5,时间缩短了,则更新从A到各个点所需的时间的列表,如表3所示

D 3  
C 5
B 10  

表3

现在除了节点D之外,从A到C的时间是最短的了,则计算从A经过C再到其他节点的时间,如表4所示。

B 7

表4

现在从A经过D再经过C然后到B的时间为7,小于表3记录的到B的时间,则更新这个时间。

就这样得到了花费时间最短的路径。总结一下就是,不断的获得从起点到某点的最短时间,然后更新这个时间列表。在 Dijkstra‘s algorithm中,这些数字被称为“权重(weight)”,而带权重的图则被称为加权图(weighted graph),那么不带权重的则被称为非加权图(unweighted graph)。对于计算非加权图中的最短路径,可使用BFS,计算加权图中的最短路径,可使用 Dijkstra‘s algorithm。然而, Dijkstra‘s algorithm不适用于带环的图,即图1中的箭头如果是双向的话那么就是不适用的。此外,它还不适用于带有负权重的情况

Dijkstra算法的实现需要使用三个散列表。第一个散列表记录的是每个点的邻居及权重。第二个散列表记录的是从起点开始到每个节点的权重,第三个散列表记录的是各个节点父节点。

Python代码:

#第一个散列表,记录每个点的邻居及权重
graph={}
graph["A"]={}
graph["A"]["C"]=7
graph["A"]["D"]=3
graph["C"]={}
graph["C"]["B"]=2
graph["D"]={}
graph["D"]["C"]=2
graph["D"]["B"]=7
graph["B"]={}

#第二个散列表,记录从起点A到其他各个节点的权重
#由于节点B不是A的邻居,则A到B的权重暂设置为无穷大
costs={}
infinity = float("inf")
costs["C"]=7
costs["D"]=3
costs["B"]=infinity

#第三个散列表,用于存储节点的父节点
parents={}
parents["C"]="A"
parents["D"]="A"
parents["B"]=None

#用于记录已经处理过的节点的数组
processed=[]

#先在未处理的节点数组中找到权重最小的节点
def find_lowest_cost_node(costs):
    lowest_cost = float("inf")
    lowest_cost_node = None
    for node in costs:
        cost = costs[node]
        if cost < lowest_cost and node not in processed:
            lowest_cost = cost
            lowest_cost_node = node
    return lowest_cost_node

node = find_lowest_cost_node(costs)
while node is not None:
    cost = costs[node]
    neighbors=graph[node]
    for n in neighbors.keys():
        new_cost=cost + neighbors[n]
        if costs[n] > new_cost:
            costs[n] = new_cost
            parents[n]=node
    processed.append(node)
    node=find_lowest_cost_node(costs)

for node in costs:
    print("Node:" + node+ "  Cost:" + str(costs[node]) + "\r\n")

for node in parents:
    print("ChildNode:" + node + " ParentNode:" + parents[node] + "\r\n")

运行结果:

Node:C  Cost:5

Node:D  Cost:3

Node:B  Cost:7

ChildNode:C ParentNode:D

ChildNode:D ParentNode:A

ChildNode:B ParentNode:C

>>> 

C#代码:

public class DijkstraAlgorithm
    {
        public Dictionary<string, double> Costs { get; set; }
        public Dictionary<string, string> Parents { get; set; }
        public Dictionary<string, Dictionary<string,double>> Graph { get; set; }
        private List<string> processed = new List<string>();
        public DijkstraAlgorithm()
        {
            Costs = new Dictionary<string, double>();
            Parents = new Dictionary<string, string>();
            Graph = new Dictionary<string, Dictionary<string, double>>();
        }

        public void Dijkstra_Algorithm()
        {
            string node = FindLowestCostNode();
            while(node != null)
            {
                double cost = Costs[node];
                Dictionary<string, double> neighbors = Graph[node];
                foreach(KeyValuePair<string,double> item in neighbors)
                {
                    double new_cost = cost + item.Value;
                    if (Costs[item.Key] > new_cost)
                    {
                        Costs[item.Key] = new_cost;
                        Parents[item.Key] = node;
                    }
                }
                processed.Add(node);
                node = FindLowestCostNode();
            }
        }
        private string FindLowestCostNode()
        {
            string lowestcostnode = null;
            double lowestcost = double.PositiveInfinity;
            foreach(KeyValuePair<string,double> item in Costs)
            {
                if(item.Value < lowestcost && !processed.Contains(item.Key))
                {
                    lowestcost = item.Value;
                    lowestcostnode = item.Key;
                }
            }
            return lowestcostnode;
        }

    }

字典的初始化以及运行结果:

DijkstraAlgorithm Dalgorithm = new DijkstraAlgorithm();
            Dalgorithm.Graph["A"] = new Dictionary<string, double>();

            Dalgorithm.Graph["A"]["C"] = 7;
            Dalgorithm.Graph["A"]["D"] = 3;

            Dalgorithm.Graph["C"] = new Dictionary<string, double>();
            Dalgorithm.Graph["C"]["B"] = 2;
            Dalgorithm.Graph["D"] = new Dictionary<string, double>();
            Dalgorithm.Graph["D"]["C"] = 2;
            Dalgorithm.Graph["D"]["B"] = 7;

            Dalgorithm.Graph["B"] = new Dictionary<string, double>();

            Dalgorithm.Costs["C"] = 7;
            Dalgorithm.Costs["D"] = 3;
            Dalgorithm.Costs["B"] = double.PositiveInfinity;

            Dalgorithm.Parents["C"] = "A";
            Dalgorithm.Parents["D"] = "A";
            Dalgorithm.Parents["B"] = null;

            Dalgorithm.Dijkstra_Algorithm();

            foreach(KeyValuePair<string,double> item in Dalgorithm.Costs)
            {
                Console.WriteLine("Key : " + item.Key + "  Value : " + item.Value);
            }

            foreach(KeyValuePair<string,string> item in Dalgorithm.Parents)
                Console.WriteLine("Key : " + item.Key + "  Value : " + item.Value);

            Console.Read();

广度优先搜索(BreadthFirstSearch)& 迪克斯特拉算法 (Dijkstra's algorithm)

原文地址:https://www.cnblogs.com/larissa-0464/p/10633934.html

时间: 2024-10-06 19:57:47

广度优先搜索(BreadthFirstSearch)& 迪克斯特拉算法 (Dijkstra's algorithm)的相关文章

迪杰斯特拉算法(Dijkstra) (基础dij+堆优化) BY:优少

首先来一段百度百科压压惊... 迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题.迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. 让我来翻译一下:Dijkstra可以求出一个点到一个图中其他所有节点的最短路径,故也称对于单源最短路径的一种解法 算法实现步骤: a.初始时,只包括源点,即S = {v},v的距离为0.U包含除v以外的其他顶点,即

C# 迪杰斯特拉算法 Dijkstra

什么也不想说,现在直接上封装的方法: using System; using System.Collections.Concurrent; using System.Collections.Generic; namespace 算法 { /// <summary> /// Dijkstra /// 迪杰斯特拉算法 /// </summary> public class Dijkstra : ICloneable { /// <summary>节点集合</summa

迪克斯特拉算法简单模板

1 //这里选的是第一点作为起始点,按照所需修改 2 const int maxs = 105; 3 const int INF = 0x3f3f3f3f; 4 int N;//点的总个数 5 int edge[maxs][maxs];//点与点之间的距离关系 6 void dijkstra() 7 { 8 int dist[maxs];//用来保存其它各点到选定点的最短距离 9 bool vis[maxs];//判断该点是否已经加入到集合中了 10 memset(vis,false,sizeo

算法--迪杰斯特拉算法 Dijkstra

https: //www.bilibili.com/video/av47427754?from=search&seid=10869921304007190819 import java.util.*; public class djstl { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); // n个点 m条边 x为开始点 如1到6 1为开始点

十大基础实用算法之迪杰斯特拉算法、最小生成树和搜索算法

迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止. 基本思想 通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算). 此外,引进两个集合S和U.S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离). 初始时,S中只有起点s:U中是除s之外的顶点,并且U中顶点的路径是"起点s

普里姆算法,克鲁斯卡尔算法,迪杰斯特拉算法,弗洛里德算法

做数据结构的课程设计顺便总结一下这四大算法,本人小白学生一枚, 如果总结的有什么错误,希望能够告知指正 普里姆算法如图所示prim 找出最短的边,再以这条边构成的整体去寻找与之相邻的边,直至连接所有顶点,生成最小生成树,时间复杂度为O(n2) 克鲁斯卡尔算法如图所示kruskal 克鲁斯卡尔算法,假设连通网N=(N,{E}),则令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点 自成一个连通分量.在E中选择代价最小的边,若该边依附的定顶点落在T中不同的连通分量上,

算法之狄克斯特拉算法 --《图解算法》

2019你好!好好生活,好好工作! 狄克斯特拉算法 狄克斯特拉算法(Dijkstra )用于计算出不存在非负权重的情况下,起点到各个节点的最短距离 可用于解决2类问题: 从A出发是否存在到达B的路径:从A出发到达B的最短路径(时间最少.或者路径最少等),事实上最后计算完成后,已经得到了A到各个节点的最短路径了:其思路为: (1) 找出"最便宜"的节点,即可在最短时间内到达的节点. (2) 更新该节点对应的邻居节点的开销,其含义将稍后介绍. (3) 重复这个过程,直到对图中的每个节点都这

图算法——狄克斯特拉算法

这里有一些定义及代码取自CodeInfo的简书,链接:https://www.jianshu.com/p/b805e9d1eb5c,和heroacool的CSDN,链接:https://blog.csdn.net/heroacool/article/details/51014824,感谢两位大佬. 狄克斯特拉算法(Dijkstra )用于计算出不存在非负权重的情况下,起点到各个节点的最短距离(单源最短路问题),如果要得到整个图各个顶点之间的最短距离,则需要对整个图的每个顶点都遍历一遍狄克斯特拉算

基础算法(三)——广度优先搜索

广度优先搜索(Breadth First Search),是很多重要的图的算法的原型. 重要的作用:遍历.对于图的遍历,一般有以下的基本思想: ①从图中某个顶点V0出发,并访问此顶点: ②从V0出发,访问V0的各个未曾访问的邻接点W1,W2,-,Wk;然后,依此从W1,W2,-,Wk 出发访问各自未被访问的邻接点. ③重复②,直到全部顶点都被访问为止. [例]如图1-7,按照广度优先搜索的思想遍历这张图. 正确的方法应该是: [例]编写"连连看"的简单程序 规则是:相连不超过两个弯的相