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

BFS可回答兩類問題:node

1.從節點A出發,有前往節點B的路徑嗎?算法

2.從節點A出發,前往節點B的哪條路徑通過的節點最少?數組

BFS中會用到「隊列」的概念。隊列是一種先進先出(FIFO, first in first out)的數據結構,與棧不一樣,棧是後進先出(LIFO, last in first out)的數據結構。數據結構

還會用到「字典」的概念。字典在如今不少語言中都存在且普遍使用,字典中的元素是一組<鍵(key),值(value)>對,key的值是不能夠重複的。關於字典的詳細內容,網上有不少資料能夠查閱。app

問題描述:想從你的朋友中找出一個芒果銷售商,若是你的朋友中沒有,那麼就從朋友的朋友中查找。(這裏假設名字最後一個字母爲「m」的即爲芒果銷售商)。這樣就是從「you」這個節點出發,是否有到「Xm」這個節點的路徑的問題。測試

思路:先從你的朋友開始查找,若是朋友A是芒果銷售商,則程序結束,若是不是,則將A的朋友排到隊列中。而後檢查朋友B是否爲芒果銷售商,循環,直到找到芒果銷售商或者隊列中的朋友們都被檢查了一遍。由於會有某我的C既是A的朋友又是B的朋友,而C只須要檢查一次,所以要分配一個列表用於記錄已經檢查過哪些朋友了。spa

Python代碼:code

>>> 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#代碼:blog

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();

相關文章
相關標籤/搜索