一、共同点
-
都是利用二叉链表n+1个空指针改线索
-
ltag、rtag 标记规则一样
-
`ltag=0`左孩子,`ltag=1`前驱线索
-
`rtag=0`右孩子,`rtag=1`后继线索
-
都可以不用栈、不用递归遍历
-
构造方式:按对应遍历顺序遍历树,用`pre`记录前驱
1、先序线索二叉树(先根遍历:根 → 左 → 右)
- 构造规则
按照先序:根→左→右遍历
-
先访问根
-
线索化左子树
-
线索化右子树
-
空左指针 → 前驱
-
空右指针 → 后继
-
先序线索树 遍历规律
-
首结点:根结点本身(不用往左找)
-
找后继:
-
`rtag=1` → 直接右指针就是后继
-
`rtag=0` → 先找左孩子,没有再找右孩子
-
致命缺点
先序线索二叉树,无法找父结点,容易死循环
左子树线索会指回祖先,遍历容易重复遍历
-
先序线索化代码逻辑
pre = null;
void preThread(TNode root){
if(root==null) return;//处理根 if(root.left==null){ root.left=pre; root.ltag=1; } if(pre!=null&&pre.right==null){ pre.right=root; pre.rtag=1; } pre=root; //先序:根→左→右 if(root.ltag==0) preThread(root.left); if(root.rtag==0) preThread(root.right);}
2、后序线索二叉树(后根遍历:左 → 右 → 根)
- 构造规则
按照后序:左→右→根遍历整棵树
-
先线索化左子树
-
再线索化右子树
-
最后处理当前根结点
-
空指针改前驱、后继线索
-
后序线索树 遍历规律
-
首结点:最左下最深结点
-
找前驱、后继非常麻烦
-
必须知道双亲结点才能顺利找后继
- 最大难点
后序线索二叉树,查找前驱后继极复杂
单纯靠线索走不完整,必须额外记录父结点
-
后序线索化代码逻辑
pre = null;
void postThread(TNode root){
if(root==null) return;//后序:左→右→根 postThread(root.left); postThread(root.right); //处理当前结点 if(root.left==null){ root.left=pre; root.ltag=1; } if(pre!=null&&pre.right==null){ pre.right=root; pre.rtag=1; } pre=root;}
三种线索二叉树对比
1.中序线索二叉树
-
遍历最简单
-
找前驱后继最简单
-
不会死循环
- 先序线索二叉树
-
构造简单
-
找后继简单,找前驱极难
-
易死循环
- 后序线索二叉树
-
构造麻烦
-
找前驱后继都很难
-
必须双亲指针辅助
总结
-
只有中序线索二叉树遍历最方便、最常用
-
先序:根最先访问,首结点是根
-
后序:根最后访问,首尾都要深入子树找
-
先序、后序线索树不适合快速查找前后结点
-
三种线索树空间复杂度都是 O(1),不用栈