線索二叉樹
技術標籤:演算法JAVA演算法資料結構二叉樹指標資料結構java
一,簡介
首先,二叉樹是非線性資料結構,而遍歷序列是線形序列,二叉樹遍歷實際上是將一個非線性結構進行線性化的操作。根據線性序列的特性,除了第一個元素外,每一個節點都有唯一的前驅,除了最後一個元素外,每一個節點都有唯一的後繼。採用二叉連結串列儲存時,只記錄了左右孩子的資訊無法直接得到每個結點的前驅和後繼。
二,儲存結構
1,二叉樹採用二叉連結串列儲存時。每個結點有兩個指標域。如果二叉連結串列有n個結點,則一共有2n個指標域。而只有n-1個是實指標,其餘n+1個是空指標。
原因
因為二叉樹有n-1個分支,而每個分支對應一個實指標,如圖所示。
從下往上看,每個節點對應一個分支,而只有樹根沒有對應分支,因此分支數等於節點數減去一。
而每個分支對應一個實指標,所以有n-1個實指標。總的指標減去實指標數即為空指標數,也就是2n-n+1=n+1。
所以我們可以充分利用空指標記錄節點的前驅和後繼,從而加快查詢節點前驅和後繼的速度。
2,每個節點還是有兩個指標域。
如果結點有左孩子。則lchild指向左孩子,否剛指向其前驅。
如果結點有右孩子,則rchild指向右孩子,否則rchild指向其後繼。
那麼如何區分儲存的是左孩子還是右孩子,還是前驅和後繼呢?為了避免混淆,增加兩個標誌域ltag,rtag。
程式碼如下
public static class BNode{
String data;
BNode lchild,rchild;
int rtag,ltag;//右標誌,左標誌
}
三**,構造線索二叉樹**
1,線索二叉樹分為前序線索二叉樹,中序線索二叉樹和後序線索二叉樹。
2,二叉樹線索化的過程實際上是在遍歷過程中修改空指標的過程。利用儲存結構的空指標指向前驅或者後繼。
所以我們可以設定兩個指標,一個指標pre指向剛剛訪問的節點,另一個指標p指向當前節點。
也就是說pre指向的結點為p指向的結點的前驅,反之,p指向的結點為pre指向的結點的後繼。
3,在遍歷的過程中,如果當前節點p的左孩子為空,則該節點的lchild指向其前驅.即p.lchild=pre;
4*,演算法步驟:*
①指標p指向根節點,pre初始化為空,pre永遠指向p的前驅。
②若p非空,則
a,中序遍歷線索化p的左子樹
b,若p的左子樹為空,則給p加上左線索,即p.ltag=1。p
的左子樹指標指向pre(前驅),即p.lchild=pre。 否則令p.ltag=0;
c,如果pre非空,則判斷如果pre的右子樹為空,給pre加上右線索,即pre.rtag=1。Pre的右孩子指標指向p.即 pre.rchild=p,否則pre.rtag=0。
d,p賦值給pre,轉向pre的右子樹。
e,中序遍歷線索化p的右子樹。
③處理最後一個節點,令其後繼為空,即pre.rchiid=null,pre.rtag=1;
程式碼如下
public static void inthread(BNode p){
//中序線索化
if(p!=null){
inthread(p.lchild);//不為空則一直找到其左子(子序遍歷的左)
if(p.lchild==null){//中序遍歷的中
p.ltag=1;
p.lchild=pre;
}
else{
p.ltag=0;
}
if(pre!=null){//增加右線索
if(pre.rchild==null){//增加了
pre.rtag=1;
pre.rchild=p;
}
else{
pre.rtag=0;
}
}
pre=p;
inthread(p.rchild);//中序遍歷的右
}
}
四,線索遍歷二叉樹
1,指標p指向根結點。
2,若p非空,則重複以下操作。
(中序遍歷,左中右)
P指標沿左孩子向下,找到最左結點,他是中序遍歷的第一個結點。
訪問p節點。
沿著右線索查詢當前節點p的後繼節點並訪問,直到右線索為零或遍歷結束。
3,遍歷p的右子樹。
程式碼如下
public static void inorderthread(BNode t){
//根據中序,線索化進行遍歷
BNode p;
p=t;
while (p!=null){
//左
while (p.ltag==0){
p=p.lchild;
}
//中
System.out.print(p.data+" ");
//右
while (p.rtag==1&&p.rchild!=null){
//右線索存在
p=p.rchild;
System.out.print(p.data+" ");
}
p=p.rchild;//不是線索,轉向右節點
}
}
最後附上完整程式碼
package Tree;
import java.util.Scanner;
public class xiansuotree {
public static class BNode{
String data;
BNode lchild,rchild;
int rtag,ltag;//右標誌,左標誌
}
static BNode pre;
public static BNode creattree(BNode t){
String ch;
Scanner scanner=new Scanner(System.in);
ch=scanner.nextLine();//判斷是否有#
if(ch.equals("#")){
t=null;
}
else{
t=new BNode();
t.data=ch;//先序遍歷生成樹
t.lchild=creattree(t.lchild);
t.rchild=creattree(t.rchild);
}
return t;
}
static void inorder(BNode t){//中序遍歷
if(t!=null){
inorder(t.lchild);
System.out.print(t.data+" ");
inorder(t.rchild);
}
}
public static void inthread(BNode p){
//中序線索化
if(p!=null){
inthread(p.lchild);//不為空則一直找到其左子(子序遍歷的左)
if(p.lchild==null){//中序遍歷的中
p.ltag=1;
p.lchild=pre;
}
else{
p.ltag=0;
}
if(pre!=null){//增加右線索
if(pre.rchild==null){//增加了
pre.rtag=1;
pre.rchild=p;
}
else{
pre.rtag=0;
}
}
pre=p;
inthread(p.rchild);//中序遍歷的右
}
}
public static void creatinthread(BNode t){
pre=new BNode();
if(t!=null){
inthread(t);
//最後一個節點,其後繼為空
pre.rchild=null;
pre.rtag=1;
}
}
public static void inorderthread(BNode t){
//根據中序,線索化進行遍歷
BNode p;
p=t;
while (p!=null){
//左
while (p.ltag==0){
p=p.lchild;
}
//中
System.out.print(p.data+" ");
//右
while (p.rtag==1&&p.rchild!=null){
//右線索存在
p=p.rchild;
System.out.print(p.data+" ");
}
p=p.rchild;//不是線索,轉向右節點
}
}
public static void main(String[] args) {
System.out.println("按先序輸入二叉樹中節點的值(孩子為空時輸入#)");
BNode node=new BNode();
node=creattree(node);
//ABD##E##CF#G###
System.out.println("中序");
inorder(node);
System.out.println();
creatinthread(node);
inorderthread(node);
}
}