有一個樹形無向圖,它描述了國、省、市、區之間的層級關係,此時咱們想找圖中的某一個結點,它位於圖中的第幾層,此時你應該怎麼作?javascript
本文將以圖文的形式,詳細講解廣度優先搜索,並用JavaScript將其實現,完成上面所描述的問題,歡迎各位感興趣的開發者閱讀本文。java
廣度優先搜索是一種對圖進行搜索的算法。算法
假設咱們一開始位於某個結點(即起點),此時並不知道圖的總體結構,而咱們的目的是從起點開始順着邊搜索,直到到達指定頂點(即終點)。在此過程當中每走到一個頂點,就會判斷一次它是否爲終點。廣度優先搜索會優先從離起點近的頂點開始搜索。json
本文涉及到了圖與隊列,對此不瞭解的開發者,能夠閱讀個人另外兩篇文章: 圖的認識 & 棧與隊列數組
如圖所示,A爲起點,G爲終點。一開始咱們在起點A上,此時並不知道G在哪裏。 bash
A -> B
A -> C
A -> D
B -> E
複製代碼
# 從頂點A到終點G,搜索順序以下
A -> B
A -> C
A -> D
B -> E
B -> F
C -> H
D -> I
D -> J
E -> K
F
H -> G
複製代碼
廣度優先搜索的特徵爲從起點開始,由近及遠進行普遍的搜索。所以,目標頂點離起點越近,搜索結束得就越快。數據結構
正如圖解示例所述,廣度優先搜索會從一個頂點出發,普遍搜索它的子結點,將其子結點放進候選頂點中,判斷當前頂點是否爲終點,若是不是終點則按順序取出候選頂點中的數據執行上述操做,直至找到終點爲止。函數
操做候選結點時,咱們是按順序取出候選結點,符合了數據結構:隊列的特性(先進先出)post
所以,咱們須要先實現一個隊列用於存儲候選結點測試
/** * 實現一個基礎隊列 * @constructor */
const Queue = function () {
// 使用數組初始化隊列
let items = [];
// 向隊列插入元素
this.enqueue = function (elem) {
items.push(elem);
}
// 從隊頭刪除元素
this.dequeue = function () {
return items.shift();
}
// 查看隊頭元素
this.front = function () {
return items[0];
}
// 判斷隊列是否爲空
this.isEmpty = function () {
return items.length ===0;
}
// 查看隊列長度
this.size = function () {
return items.length;
}
// 查看隊列中的元素
this.print = function () {
return items.toString();
}
}
複製代碼
咱們將上述思路轉換爲代碼
/** * 廣度優先搜索 * @param tree 要查找的樹形圖 * @param target 要查找的結點 * @returns {number} 返回目標結點在樹中的深度 */
const breadthFirstSearch = function (tree,target) {
// 實例化一個隊列
let queue = new Queue();
// 根節點到目標結點的深度
let step = 0;
// 入隊
queue.enqueue(tree);
// 遍歷隊列,直至隊列爲空,或者找到目標結點
while (!queue.isEmpty()){
step += 1;
let len = queue.size();
for (let i = 0; i < len; i++){
// 獲取隊首元素
let teamLeader = queue.front();
// 若是是目標元素則返回當前深度
if(target === teamLeader.value) return step;
// 若是不是,將下一層結點添加進隊列
if(teamLeader.children && teamLeader.children.length){
teamLeader.children.map(item=>{
queue.enqueue(item);
});
}
// 刪除遍歷過的結點
queue.dequeue();
}
}
}
複製代碼
接下來,咱們用一個例子來測試下咱們編寫的廣度優先搜索函數
以下圖所示,是一個描述了國、省、市、區的對應關係的無向圖,咱們的問題是:從圖中找到天河區在第幾層。
// 咱們用json來描述上面的無向圖
const dataTree = {
name:"國家",
value:"中國",
children:[
{
name:"省份",
value:"廣東",
children: [
{
name:"城市",
value:"廣州",
children:[
{
name:"行政區",
value:"天河區",
},
/// 其餘部分省略 ////
]
},
{
name:"城市",
value: "深圳",
children: [
{
name:"行政區",
value: "福田區"
},
/// 其餘部分省略 ////
]
}
]
},
{
name:"省份",
value:"陝西",
children: [
{
name:"城市",
value: "西安",
children: [
/// 其餘部分省略 ////
]
},
{
name:"城市",
value: "商洛",
children: [
/// 其餘部分省略 ////
]
}
]
}
]
}
複製代碼
let step = breadthFirstSearch(dataTree,"天河區");
console.log(`目標結點在圖中的第 ${step} 層`);
複製代碼