二叉樹三種深度遍歷方法和實現


三種遍歷的基本思想

先序遍歷:根節點 -> 左子樹 -> 右子樹

中序遍歷:左子樹 -> 根節點 -> 右子樹

后序遍歷:左子樹 -> 右子樹 -> 根節點

如,以下二叉樹遍歷:

 

先序遍歷結果:1 2 4 5 7 8 3 6

中序遍歷結果:4 2 7 5 8 1 3 6

后序遍歷結果:4 7 8 5 2 6 3 1

 

二叉樹基本結構

1 function TreeNode(val) {
2 
3   this.val = val;
4 
5   this.left = this.right = null;
6 
7 }

一、先序遍歷

  • 遞歸實現:先處理當前節點,然后遞歸處理左子節點,最后遞歸處理右子節點
1 function preVisitRecursive(root) {
2     if(root) {
3         console.log(root.val);       //處理當前節點
4         preVisit(root.left);         //遞歸處理左子節點
5         preVisit(root.right);        //遞歸處理右子節點
6     }
7 }
  • 非遞歸實現:使用棧(后進先出)來保存返回需要處理的節點
 1 function preVisit(root) {
 2     let curNode = root, treeNode = [];
 3     while(curNode !== null  || treeNode.length > 0) {
 4         if(curNode!== null) {
 5             console.log(curNode.val);      //處理當前節點
 6             treeNode.push(curNode);        
 7             curNode = curNode.left;        //指向左子節點做同樣處理
 8         } else {
 9             curNode = treeNode.pop();      //棧頂元素出棧
10             curNode = curNode.right;       //指向右子節點
11         }
12     }
13 }

二、中序遍歷

  • 遞歸實現:先遞歸處理左子節點,再處理當前節點,最后遞歸處理右子節點
1 function midVisitRecursive(root) {
2     if(root) {
3         preVisit(root.left);         //遞歸處理左子節點
4         console.log(root.val);       //處理當前節點    
5         preVisit(root.right);        //遞歸處理右子節點
6     }
7 }
  • 非遞歸實現:使用棧(后進先出)來保存返回需要處理的節點
 1 function midVisit(root) {
 2     let curNode = root, treeNode = [];
 3     while(curNode !== null  || treeNode.length > 0) {
 4         if(curNode!== null) {
 5             treeNode.push(curNode);        
 6             curNode = curNode.left;        //指向左子節點做同樣處理
 7         } else {
 8             curNode = treeNode.pop();      //棧頂元素出棧
 9             console.log(curNode.val);      //處理當前節點
10             curNode = curNode.right;       //指向右子節點
11         }
12     }
13 }

三、后序遍歷

  • 遞歸實現:先遞歸處理左子節點,然后遞歸處理右子節點,最后處理當前節點
1 function postVisitRecursive(root) {
2     if(root) {
3         preVisit(root.left);         //遞歸處理左子節點    
4         preVisit(root.right);        //遞歸處理右子節點
5         console.log(root.val);       //處理當前節點
6     }
7 }
  • 非遞歸實現:需要保證左子節點和右子節點都被訪問 且 右子節點在左子節點訪問之后 才能訪問根節點

1)對於任一結點P,將其入棧,然后沿其左子樹一直往下搜索,直到搜索到沒有左孩子的結點,此時該結點出現在棧頂,但是此時不能將其出棧並訪問,因為其右孩子還未被訪問。

    所以接下來按照相同的規則對其右子樹進行相同的處理,當訪問完其右孩子時,該結點又出現在棧頂,此時可以將其出棧並訪問。這樣就保證了正確的訪問順序。

    可以看出,在這個過程中,每個結點都兩次出現在棧頂,只有在第二次出現在棧頂時,才能訪問它。因此需要多設置一個變量標識該結點是否是第一次出現在棧頂。

 1 function postVisit(root) {
 2     let curNode = root, treeNode = [];
 3     while(curNode !== null || treeNode.length > 0) {
 4     {
 5             if(curNode !== null)
 6             {
 7                 curNode.isFirst = true;
 8                 treeNode.push(curNode);  //當前節點入棧
 9                 curNode = curNode.left;  //指向left,進行步驟1處理
10             } else {
11                 curNode = treeNode.pop();  //指向棧頂元素
12                 if(curNode.isFirst) {      //表示第一次出棧頂
13                     curNode.isFirst = false;
14                     treeNode.push(curNode);
15                     curNode =curNode.right
16                 } else {                 //第二次出棧頂
17                     console.log(curNode.val);
18                     curNode = null;     //進行步驟2
19                 }
20             }
21     }
22     }    
23 }
View Code

覺得不錯的另外的實現

 

2)要保證左子節點和右子節點都被訪問之后才能訪問根節點。對於任一節點P,先將其入棧。如果其不存在左子節點和右子節點,可直接訪問;亦或其存在左子節點或右子節點,且其左子節點和右子節點已被訪問過,則可直接訪問該節點;其他情況下,則將其右子節點和左子節點依次入棧,這樣保證了出棧時左子節點在右子節點之前被訪問,根節點在子節點之后被訪問

 1 function postVisit(root) {
 2     let treeNode = [], 
 3             curNode,             //當前節點
 4             pre = null;          //上一次訪問的節點
 5     treeNode.push(root);
 6     while(treeNode.length > 0) {
 7         curNode = treeNode[treeNode.length - 1];
 8         // 若當前節點沒有左子節點和右子節點或者孩子節點均被訪問過
 9         if((curNode.left === null && curNode.right === null) || (pre && (pre === curNode.left || pre === curNode.right))) {
10             console.log(curNode.val);
11             treeNode.pop();
12             pre = curNode;
13         } else {
14             if(curNode.right) {
15                 treeNode.push(curNode.right);
16             }
17             if(curNode.left) {
18                 treeNode.push(curNode.left);
19             }
20         }
21     }
22 }
View Code

 四、二叉樹與數組相結合

從二叉搜索樹(BST)中找出是否存在兩個節點的和等於某個值k

 1 /**
 2  * @param {TreeNode} root
 3  * @param {number} k
 4  * @return {boolean}
 5  */
 6 var findTarget = function(root, k) {
 7   let treeNode = [], numsArr = [], curNode = root;
 8   while(curNode !== null || treeNode.length > 0) {
 9     if(curNode !== null) {
10       treeNode.push(curNode);
11       curNode = curNode.left;
12     } else {
13       curNode = treeNode.pop();
14       numsArr.push(curNode.val);
15       curNode = curNode.right;
16     }
17   }
18   let m = 0, n = numsArr.length - 1, sum = numsArr[m] + numsArr[n];
19   while(m < n) {
20     if(sum === k) return true;
21     sum < k ? m++ : n--;
22     sum = numsArr[m] + numsArr[n];
23   }
24   return false
25 };

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM