【算法系列之线索化二叉树,前序线索化、中序线索化、后序线索化以及遍历~】
- 1.何謂線索化二叉樹
- 2.線索化二叉樹的本質
- 3.線索化二叉樹的存儲結構
- 4.構建線索化二叉樹
- 4.1.先序線索化
- 4.2.中序線索化
- 4.3.后序線索化
- 5.遍歷線索化二叉樹
- 5.1.先序遍歷 先序線索化二叉樹
- 5.2.中序遍歷 中序線索化二叉樹
- 5.3.后序遍歷 后序線索化二叉樹
- 6.線索化二叉樹的優勢與劣勢
1.何謂線索化二叉樹
🏴???一般的二叉樹的葉子節點的指針都造成了浪費,對于有n個節點的二叉樹,其中就有n+1個空鏈域;所以我們可以將空鏈域存放某種遍歷次序下該節點的前驅節點和后繼節點,這些指針被成為線索,加上線索的二叉樹稱為線索化二叉樹。
🍁例如上圖中二叉樹,它的前序遍歷之后的結果為[1,2,4,8,9,5,10,3,6,7],那么對于葉子節點8的前驅節點就是4、后繼節點就是9。對于葉子節點7比較例外,它沒有后繼節點。
2.線索化二叉樹的本質
💌二叉樹遍歷的本質是將一個非線性結構,轉換為線性結構,使得每個節點都有唯一前驅和后繼節點(第一個節點無前驅,最后一個節點無后序),這是二叉樹遍歷的本質;對于二叉樹的某一個節點而言,找到它的后繼節點是非常容易的,但它的前驅節點只有在遍歷中才能得到。為了解決這種問題,有兩種方法。
🍅1.增加向前的指針,這種方式增加了存儲開銷,不可取。
🍅2.利用空鏈指針實現,這也就是線索化。
3.線索化二叉樹的存儲結構
🏴???對于二叉樹中的某個節點,我們怎么知道它指向的是左右指針還是前驅后繼節點,所以需要加入兩個變量leftType和rightType,以leftType為例,當leftType ==0 時指向的是左指針,當leftType == 1時指向的是前驅節點。
| 數據 | 左指針 | 右指針 | 左指針指向類型 | 右指針指向類型 |
4.構建線索化二叉樹
📚對于構建線索化二叉樹本質上也就是遍歷二叉樹,在遍歷過程中,增加檢測當前節點的左右節點是否為空;將它們改為指向前驅和后繼節點的線索,為了實現這個過程,需要添加一個pre指針,將pre指針每次指向上次訪問的節點。然后就可以使用pre指向它的前驅,使用當前訪問的節點指向pre也就是后繼。
4.1.先序線索化
/*** 前序線索化二叉樹* 類似于中序線索化二叉樹* 思路:當滿足條件temp.left ==null時,開始進行遞歸回溯,此時使用pre指針每次都在temp指針的前一個節點** @param temp*/public void preThreadedBinaryTree(HeroNode temp) {if (temp == null) {return;}if (temp.left == null) {temp.left = pre;temp.leftType = 1;}if (pre != null && pre.right == null && pre.left != temp) {pre.right = temp;pre.rightType = 1;}pre = temp;if (temp.leftType == 0) { //防止陷入無限循環當中preThreadedBinaryTree(temp.left);}if (temp.rightType == 0)preThreadedBinaryTree(temp.right);}📑需要注意的是需要判斷leftType和rightType是否等于0,因為前面已經進行了部分線索化,所以不進行判斷就會陷入無線循環當中。
4.2.中序線索化
/*** 中序線索化二叉樹,對于葉子節點,左右指針都為null,所以為了避免浪費,將其分別保存它的前驅和后繼節點的地址* 對于[1,2,3,4,5,6,7],中序遍歷之后是[4,2,5,1,6,3,7],對于葉子節點5,它的左右指針都為null,所以它的左指針應該存儲它的前驅節點2的地址,* 它的右指針應該存儲1節點的地址** @param cur 根指針*/public void infixThreadedBinaryTree(HeroNode cur) {if (cur == null) {return;}infixThreadedBinaryTree(cur.left);if (cur.left == null) {cur.left = pre;cur.leftType = 1;}if (pre != null && pre.right == null) {pre.right = cur;pre.rightType = 1;}pre = cur;infixThreadedBinaryTree(cur.right);}4.3.后序線索化
/*** 后序線索化二叉樹** @param node root*/public void sufThreadedBinaryTree(HeroNode node) {if (node == null) {return;}if (node.leftType == 0) {sufThreadedBinaryTree(node.left);}if (node.rightType == 0) {sufThreadedBinaryTree(node.right);}if (node.left == null) {node.left = pre;node.leftType = 1;}if (pre != null && pre.right == null) {pre.right = node;pre.rightType = 1;}pre = node;}5.遍歷線索化二叉樹
📚對于普通的遍歷二叉樹已經不適用與線索化二叉樹了,因為每個節點要么指向它的左右子節點,要么指向前驅后繼節點,所以使用null作為判斷條件已經不再合適;根據前文增加的leftType和rightType可以作為遞歸終止條件。
5.1.先序遍歷 先序線索化二叉樹
public void preThreadedErgodic(HeroNode cur) {if (cur == null) {return;}if (cur.leftType == 1 || cur.rightType == 1) {System.out.print(cur.id + "->");return;}System.out.print(cur.id + "->");preThreadedErgodic(cur.left);preThreadedErgodic(cur.right);}5.2.中序遍歷 中序線索化二叉樹
/*** 中序遍歷 中序線索化二叉樹** @param cur*/public void infixThreadedErgodic(HeroNode cur) {if (cur == null) {return;}if (cur.leftType == 1) {System.out.print(cur.id + "->");return;}infixThreadedErgodic(cur.left);System.out.print(cur.id + "->");infixThreadedErgodic(cur.right);}5.3.后序遍歷 后序線索化二叉樹
/*** 遍歷 后序列線索化二叉樹* @param cur*/public void sufThreadedErgodic(HeroNode cur) {if (cur == null || cur.rightType == 1) {if (cur != null) {System.out.print(cur.id + "->");}return;}sufThreadedErgodic(cur.left);sufThreadedErgodic(cur.right);if (cur.leftType == 1) {System.out.print(cur.id + "->");return;}System.out.print(cur.id + "->");}6.線索化二叉樹的優勢與劣勢
🎈優勢
💦(1)利用線索二叉樹進行中序遍歷時,不必采用堆棧處理,速度較一般二叉樹的遍歷速度快,且節約存儲空間。
💦(2)任意一個結點都能直接找到它的前驅和后繼結點
🎈劣勢
💦(1)結點的插入和刪除麻煩,且速度也較慢。
💦(2)線索子樹不能共用。
總結
以上是生活随笔為你收集整理的【算法系列之线索化二叉树,前序线索化、中序线索化、后序线索化以及遍历~】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【❤️算法系列之顺序二叉树的实现(前序遍
- 下一篇: 算法系列之赫夫曼树的精解【构造流程及原理