線索二叉樹的原理和實現**
關於二叉樹可以檢視以下文章:
c資料結構與演算法-基礎整理-樹-02:二叉樹的建立及四種遍歷方式
在普通二叉樹中,葉結點或只有乙個孩子的結點會存在許多空指標域,這些空間已經建立了,但並沒有得到有效的利用,造成了空間的浪費。
解決辦法:
將左孩子的空指標指向父結點的前驅,並設定ltag標誌變數來指明,左孩子是指向前驅還是真正存在。
將右孩子的空指標指向父結點的後繼,並設定rtag標誌變數來指明,右孩子是指向後繼還是真實存在。
線索:指向前驅和指向後繼的指標叫線索。
線索二叉樹:加上線索的二叉樹。
線索化:對二叉樹的空指標加上線索的過程叫線索化。
與普通二叉樹不同的是,線索二叉樹的結構中多了乙個ltag和rtag的標誌變數,來指明左孩子和右孩子的身份。
此外,線索二叉樹線索化過程中,需要乙個全域性的pre指標,指向剛剛訪問過的元素。
typedef struct treenode
treenode,*bintree;//定義了這個結構體為 treenode ,這個二叉樹指標為 bintree
bintree pre;
線索二叉樹的建立與普通二叉樹的建立基本相同,不同的是,線索二叉樹每增加乙個結點,都需要把左右標誌變數初始化。
說明:二叉樹的何種建立方式與線索化,遍歷均不衝突,仍和順序建立的二叉樹本質是一樣的。
//二叉樹的建立:遞迴實現,先序遍歷的建立方法
//注意此處bt為指標的指標,因為沒有返回值,所以要把二叉樹的指標完全改變掉,需要指標的指標
//str為傳入的字串,該字串用於建立二叉樹的資料域
//n為當前建立節點資料域時使用到str的位數
//返回值為記錄二叉樹建立完畢後,使用了多少字元
int createtree(bintree *bt,char *str,int n)
else
(*bt)->data = ch;
(*bt)->ltag = 0;//注意一定要有這個初始化步驟
(*bt)->rtag = 0;
n=createtree(&(*bt)->left,str, n);//取指標的位址給此函式的第乙個引數,因為此引數為二級指標
n=createtree(&(*bt)->right, str, n);
} }return n;
}
線索化可以以多種順序進行,但線索化順序和遍歷順序只能一一對應。
//中序遍歷線索化
void inthreading(bintree bt)
if (pre && !pre->right)//當遍歷到bt時,若它的前驅pre存在,且前驅右孩子不存在,那麼前驅的右孩子指向bt,即bt為pre的後繼
pre = bt;//當準備遍歷下乙個節點時,用pre儲存當前的節點
inthreading(bt->right);//繼續中序遍歷完成線索化
}}
與中序方式線索化二叉樹相對應。
//中序遍歷線索二叉樹,非遞迴實現
void inorderthreading(bintree bt)
//首先遍歷到中序遍歷第乙個開始的節點
printf("%c -> ", p->data);//列印其資料
while (p->rtag == 1&&p->right)//如果右孩子是線索,那麼一直遍歷至右孩子,因為右孩子指向後繼
//右孩子不是線索了,說明右孩子存在,左孩子剛遍歷到了,所以接著右孩子的遍歷,然後開始下一輪的遍歷
p = p->right;
}}
在二叉樹根結點前增加乙個頭結點,其左孩子指向根結點,右孩子指向中序遍歷的最後乙個結點,這樣,二叉樹就像乙個雙向鍊錶,既可以從頭遍歷,也可以從尾遍歷。
//增加頭結點的中序線索化,head為二級指標,指向指標的指標,目的是真正改變一級指標的內容
void inorderthreading(bintree* head, bintree bt)
pre = (*head);//前指標指向頭結點
(*head)->left = bt;
inthreading(bt);//開始普通的中序線索化二叉樹
//此時二叉樹中序序列的第乙個結點的左孩子指向頭結點
//結束普通的線索化後,此時的pre指標指向最後乙個元素
pre->right = *head;
pre->rtag = 1;
(*head)->right = pre;
//現在整個二叉樹就是乙個雙向鍊錶,既可以從頭結點開始往後遍歷,也可以從中序序列最後乙個結點開始往前遍歷
}
//有頭結點的中序遍歷線索二叉樹,此時的bt是頭結點
void inorderthreading1(bintree bt)
//首先遍歷到中序遍歷第乙個開始的節點
printf("%c -> ", p->data);//列印其資料
while (p->rtag == 1 && p->right!=bt)//此處右孩子的條件應為不等於bt
//右孩子不是線索了,說明右孩子存在,左孩子剛遍歷到了,所以接著右孩子的遍歷,然後開始下一輪的遍歷
p = p->right;
}}
int main()
測試資料:ab#d##c##
1.二叉樹的空指標應該是有限的,為什麼只在這些空指標上線索就能把整棵樹線索化,以達到每個結點都能找到後繼?答:觀察之前中序遍歷的特點,從左孩子返回就列印結點資訊,從右孩子返回就結束該步調用,注意列印結點的這塊,才是真正的遍歷到了該結點,那麼是如何從左孩子返回的呢,當左孩子為空,或左孩子部分已經完成整個的函式呼叫過程,若左孩子為空,那麼剛好,左孩子指向前驅,若左孩子部分已完成函式呼叫過程,那麼左孩子是從左孩子的右孩子返回後完成整個呼叫的,依次類推,總會找到返回的根本:是從葉結點返回了,可以這樣理解,凡是遍歷到了左右孩子都存在的結點,那麼它的上乙個結點和下乙個結點一定有空的部分,即左右孩子不全存在,這樣,兩個孩子都存在的結點就會有前驅和後繼指標的存在了,再者,可以看一下它們之間的數量關係:對於乙個有n個結點的二叉樹,一共有2n個指標域,n個結點一共有n-1條分支,所以其中的2n-(n-1)=n+1個指標其實是空指標域,但其實n-1個指標就能完成樹中關係的指向(即使某個結點有兩個孩子,也能按照中序遍歷的順序往下尋找,尋找到前驅後繼),其中第乙個遍歷到的結點無前驅,最後乙個遍歷到的結點無後繼。所以,其實空指標域是足夠的。
2.什麼情況下用線索二叉樹?
答:當經常需要遍歷或查詢結點時,可以使用,它的時間複雜度遠低於遞迴的方法。
3.理解了中序遍歷的線索化後,其實前序,後序,原理也差不多。
線索二叉樹 資料結構
按照教材進行中序二叉樹線索化 線索化就是就將二叉樹的多出來的n 1個鏈域用做指向前驅和後繼用,前驅後繼指的是按中序遍歷二叉樹產生的前驅和後繼 ltag 0 有左孩子 ltag 1 無左孩子,指向前驅 rtag 0 有右孩子 rtag 1 無右孩子,指向後繼 例子 構造二叉樹 形如abc de fg ...
資料結構(線索二叉樹)
線索二叉樹的前序,中序,後序 typedef struct nodenode 前序線索二叉樹 參照中序即可 void prethread node p,node pre if pre null pre rchild null pre p if p ltag 0 prethread p lchild,...
資料結構 線索二叉樹
1.定義 在二叉樹的結點上加上線索的二叉樹稱為線索二叉樹,對二叉樹以某種遍歷方式 前序 中序 後序或層序 進行遍歷,使其變為線索二叉樹的過程稱為對二叉樹進行線索化。2.本質 二叉樹的遍歷實質上是對乙個非線性結構進行線性化的過程,它使得每個結點 除第乙個和最後乙個 在這些線性序列中有且僅有乙個直接前驅...