利用JS實現二叉樹遍歷演算法例項程式碼
目錄
- 前言
- 一、二叉樹
- 1.1、遍歷二叉樹
- 1.2、用表示二叉樹
- 1.3、前序遍歷演算法
- 1.4、中序遍歷演算法
- 1.5、後序遍歷演算法
- 1.6、按層遍歷演算法
- 二、演算法題
- 1.1、二叉樹的最大深度
- 1.2、二叉樹的所有路徑
- 總結
前言
在電腦科學中,樹(tree) 是一種廣泛使用的抽象資料型別(ADT),是一類非線性資料結構。樹在計算機領域得到廣泛應用,尤其二叉樹最為常用。
樹的相關概念:
- 結點:每個元素稱為結點
- 樹根:根節點
- 度:一個結點含有的子結點的個數稱為該結點的度
- 葉子節點:度為0的節點
一、二叉樹
概念:每個節點最多含有兩個子樹的樹稱為二叉樹。
1.1、遍歷二叉樹
二叉樹有兩種遍歷深度遍歷和廣度遍歷,其中深度遍歷有前序、 中序和後序三種遍歷方法。 廣度遍歷就是層次遍歷,按層的順序一層一層遍歷。
四種遍歷的主要思想:
- 前序:先訪問根,然後訪問左子樹,最後訪問右子樹,DLR。
- 中序:先訪問左子樹,然後訪問根,最後訪問右子樹,LDR。
- 後序:先後訪問左子樹,然後訪問右子樹,最後訪問根,LRD。
- 廣度:按層的順序一層一層遍歷。
例如a+b*(c-d)-e/f,該表示式用二叉樹表示:
對他分別進行遍歷:
- 前序:-+a*b-cd/ef
- 中序:a+b*c-d-e/f
- 後序:abcd-*+ef/-
- 廣度:-+/a*efb-cd
1.2、用js表示二叉樹
我們用js的物件來表示二叉樹,物件擁有三個屬性,left、value、right,分別是左子樹,值和右子樹,上面a+b*(c-d)-e/f的例子我們用js可以這樣表示。
var tree = { value: '-',left: { value: '+',left: { value: 'a' },right: { value: '*',left: { value: 'b' },right: { value: '-',left: { value: 'c' },right: { value: 'd' } } } },right: { value: '/',left: { value: 'e' },right: { value: 'd' } } }
1.3、前序遍歷演算法
前序:有兩種方法,第一種很簡單就是直接使用遞迴的辦法。
function preOrder(treeNode) { if(treeNode) { console.log(treeNode.value); // 打印出來代表訪問這個節點 preOrder(treeNode.left); preOrder(treeNode.right); } }
演算法思路很簡單,先遍歷根節點,然後遞迴遍歷左子樹,左子樹遍歷結束後,遞迴右子樹。
第二種非遞迴遍歷
function preOrder(treeNode) { if(treeNode) { var stack = [treeNode]; //將二叉樹壓入棧 while (stack.length !== 0) { treeNode = stack.pop(); // 取棧頂 document.getElementById('text').appendChild(document.createTextNode(treeNode.value)); // 訪問節點 if(treeNode.right) stack.push(treeNode.right); // 把右子樹入棧 if(treeNode.left) stack.push(treeNode.left); // 把左子樹入棧 } } }
第二種是使用棧的思想,我們都知道,棧是先進後出的一種資料結構,我們先把根節點入棧,然後取棧頂,訪問根節點,分別把右左子樹入棧,這邊必須右邊先入棧,因為我們是要先從左邊開始訪問的,所以右子樹先入棧,然後就迴圈取出棧,直到棧空。
1.4、中序遍歷演算法
中序遞迴演算法:
function InOrder(treeNode) { if(treeNode) { InOrder(treeNode.left); document.getElementById('text').appendChild(document.createTextNode(treeNode.value)); InOrder(treeNode.right); } }
和前序遞迴演算法同樣的思路,只是訪問節點位置不同
第二種:
function InOrder(node) { if(node) { var stack = []; // 建空棧 //如果棧不為空或結點不為空,則迴圈遍歷 while (stack.length !== 0 || node) { if (node) { //如果結點不為空 stack.push(node); //將結點壓入棧 node = node.left; //將左子樹作為當前結點 } else { //左子樹為空,即沒有左子樹的情況 node = stack.pop(); //將結點取出來 document.getElementById('text').appendChild(document.createTextNode(node.value)); node = node.right; //將右結點作為當前結點 } } } }
非遞迴中序演算法的思想就是,把當前節點入棧,然後遍歷左子樹,如果左子樹存在就一直入棧,直到左子樹為空,訪問但前節點,然後讓右子樹入棧。
1.5、後序遍歷演算法
第一種:遞迴遍歷演算法
function postOrder(node) { if (node) { //判斷二叉樹是否為空 postOrder(node.left); //遞迴遍歷左子樹 postOrder(node.right); //遞迴遍歷右子樹 document.getElementById('text').appendChild(document.createTextNode(node.value)); } }
第二種:非遞迴遍歷演算法
function postOrder(node) { if (node) { //判斷二叉樹是否為空 var stack = [node]; //將二叉樹壓入棧 var tmp = null; //定義快取變數 while (stack.length !== 0) { //如果棧不為空,則迴圈遍歷 tmp = stack[stack.length - 1]; //將棧頂的值儲存在tmp中 if (tmp.left && node !== tmp.left && node !== tmp.right) { //如果存在左子樹,node !== tmp.left && node !== tmp.righ 是為了避免重複將左右子樹入棧 stack.push(tmp.left); //將左子樹結點壓入棧 } else if (tmp.right && node !== tmp.right) { //如果結點存在右子樹 stack.push(tmp.right); //將右子樹壓入棧中 } else { document.getElementById('text').appendChild(document.createTextNode(stack.pop().value)); node = tmp; } } } }
這裡使用了一個tmp變數來記錄上一次出棧、入棧的結點。思路是先把根結點和左樹推入棧,然後取出左樹,再推入右樹,取出,最後取根結點。
下面是用這個演算法遍歷前面那個二叉樹的過程
stack tmp node 列印
初始 : - null -
第1輪: -+&www.cppcns.comnbsp; - -
第2輪: -+a + -
第3輪: -+ a a a
第4輪: -+* + a
第5輪: -+*b * a
第6輪: -+* b b b
第7輪: -+*- * b
第8輪: -+*-c - b
第9輪: -+*- c c c
第10輪: -+*-d - c
第11輪: -+*- d d d
第12輪: -+* - - -
第13輪: -+ * * *
第14輪: - + + +
第15輪: -/ - +
第16輪: -/e / +
第17輪: -/ e e e
第18輪: -/f / e
第19輪: -/ f&nPtlWxnykbsp; f f
第20輪: - / / /
第21輪: - - -結果:abcd-*+ef/-
1.6、按層遍歷演算法
function breadthTraversal(node) { if (node) { //判斷二叉樹是否為空 var que = [node]; //將二叉樹放入佇列 while (que.length !== 0) { //判斷佇列是否為空 node = que.shift(); //從佇列中取出一個結點 document.getElementById('text').appendChild(document.createTextNode(node.value)); //將取出結點的值儲存到陣列 if (node.left) que.push(node.left); //如果存在左子樹,將左子樹放入佇列 if (node.right) que.push(node.right); //如果存在右子樹,將右子樹放入佇列 } } }
使用陣列模擬佇列,首先將根結點歸入佇列。當佇列不為空時,執行迴圈:取出佇列的一個結點,如果該節點有左子樹,則將該節點的左子樹存入佇列;如果該節點有右子樹,則將該節點的右子樹存入佇列。
二、演算法題
1.1、二叉樹的最大深度
給定一個二叉樹,找出其最大深度。
二叉樹的深度為根節點到最遠葉子節點的最長路徑上的節點數。
比如下面這個二叉樹,返回深度3。
3
/ \
9 20
/ \
15 7const tree = {
value: 3,
left: {
value: 9
},
right: {
value: 20,
left: { value: 15 },
right: { value: 9 }
}
}
遞迴演算法:遞迴演算法的思路很簡單,先拿到左子樹最深層,再拿到右子樹最深層,取他們最大值就是樹的深度。
var maxDepth = function(root) {
if (!root) {
return 0;
}
const leftDeep = maxDepth(root.left) + 1;
const rightDeep = maxDepth(root.right) + 1;
return Math.max(leftDeep,rightDeep);
};
/*
maxDepth(root) = maxDepth(root.left) + 1 = 2
maxDepth(root.left) = maxDepth(root.left.left) + 1 = 1
maxDepth(root.left.left) = 0;
maxDepth(root) = maxDepth(root.right) + 1 = 3
maxDepth(root.right) = maxDepth(root.right.right) + 1 = 2
maxDepth(www.cppcns.comroot.right.right) = maxDepth(root.right.right.right) + 1 = 1
maxDepth(root.right.right.right) = 0
*/
1.2、二叉樹的所有路徑
給定一個二叉樹,返回所有從根節點到葉子節點的路徑。
比如:
3
/ \
9 20
/ \
15 7
返回:['3->9','3->20->15','3->20->7']
使用遞迴的方法:
var binaryTreePaths = function(root) { if (!root) return []; const res = []; function dfs(curNode,curPath) { if(!curNode.left && !curNode.right) { res.push(curPath); } if(curNode.left) { dfs(curNode.left,`${curPath}->${curNode.left.value}`) } if(curNode.right) { dfs(curNode.right,`${curPath}->${curNode.right.value}`) } } dfs(root,`${root.value}`); return res; };
總結
到此這篇關於利用JS實現二叉樹遍歷演算法的文章就介紹到這了,更多相關JS二叉樹遍歷演算法內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!