資料結構基礎整理 樹 06 線索二叉樹(一)

2021-10-02 18:55:44 字數 3749 閱讀 9978

線索二叉樹的原理和實現**
關於二叉樹可以檢視以下文章:

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.本質 二叉樹的遍歷實質上是對乙個非線性結構進行線性化的過程,它使得每個結點 除第乙個和最後乙個 在這些線性序列中有且僅有乙個直接前驅...