1、線索二叉樹的原理html
經過考察各類二叉鏈表,無論兒叉樹的形態如何,空鏈域的個數老是多過非空鏈域的個數。準確的說,n各結點的二叉鏈表共有2n個鏈域,非空鏈域爲n-1個,但其中的空鏈域卻有n+1個。以下圖所示。web
所以,提出了一種方法,利用原來的空鏈域存放指針,指向樹中其餘結點。這種指針稱爲線索。svg
記ptr指向二叉鏈表中的一個結點,如下是創建線索的規則:函數
(1)若是ptr->lchild爲空,則存放指向中序遍歷序列中該結點的前驅結點。這個結點稱爲ptr的中序前驅;ui
(2)若是ptr->rchild爲空,則存放指向中序遍歷序列中該結點的後繼結點。這個結點稱爲ptr的中序後繼;spa
顯然,在決定lchild是指向左孩子仍是前驅,rchild是指向右孩子仍是後繼,須要一個區分標誌的。所以,咱們在每一個結點再增設兩個標誌域ltag和rtag,注意ltag和rtag只是區分0或1數字的布爾型變量,其佔用內存空間要小於像lchild和rchild的指針變量。結點結構以下所示。.net
其中:
3d
(1)ltag爲0時指向該結點的左孩子,爲1時指向該結點的前驅;指針
(2)rtag爲0時指向該結點的右孩子,爲1時指向該結點的後繼;code
(3)所以對於上圖的二叉鏈表圖能夠修改成下圖的養子。
2、線索二叉樹結構實現
二叉線索樹存儲結構定義以下:
-
/* 二叉樹的二叉線索存儲結構定義*/
-
typedef
enum{Link, Thread}PointerTag;
//Link = 0表示指向左右孩子指針;Thread = 1表示指向前驅或後繼的線索
-
-
typedef
struct BitNode
-
{
-
char data;
//結點數據
-
struct BitNode *lchild, *rchild;
//左右孩子指針
-
PointerTag Ltag;
//左右標誌
-
PointerTag rtal;
-
}BitNode, *BiTree;
線索化的實質就是將二叉鏈表中的空指針改成指向前驅或後繼的線索。因爲前驅和後繼信息只有在遍歷該二叉樹時才能獲得,因此,線索化的過程就是在遍歷的過程當中修改空指針的過程。
中序遍歷線索化的遞歸函數代碼以下:
-
BiTree pre;
//全局變量,始終指向剛剛訪問過的結點
-
//中序遍歷進行中序線索化
-
void InThreading(BiTree p)
-
{
-
if(p)
-
{
-
InThreading(p->lchild);
//遞歸左子樹線索化
-
//===
-
if(!p->lchild)
//沒有左孩子
-
{
-
p->ltag = Thread;
//前驅線索
-
p->lchild = pre;
//左孩子指針指向前驅
-
}
-
if(!pre->rchild)
//沒有右孩子
-
{
-
pre->rtag = Thread;
//後繼線索
-
pre->rchild = p;
//前驅右孩子指針指向後繼(當前結點p)
-
}
-
pre = p;
-
//===
-
InThreading(p->rchild);
//遞歸右子樹線索化
-
}
-
}
中間部分代碼作了這樣的事情:
由於此時p結點的後繼尚未訪問到,所以只能對它的前驅結點pre的右指針rchild作判斷,if(!pre->rchild)表示若是爲空,則p就是pre的後繼,因而pre->rchild = p,而且設置pre->rtag = Thread,完成後繼結點的線索化。如圖:
if(!p->lchild)表示若是某結點的左指針域爲空,由於其前驅結點剛剛訪問過,賦值了pre,因此能夠將pre賦值給p->lchild,並修改p->ltag = Thread(也就是定義爲1)以完成前驅結點的線索化。
完成前驅和後繼的判斷後,不要忘記當前結點p賦值給pre,以便於下一次使用。
有了線索二叉樹後,對它進行遍歷時,其實就等於操做一個雙向鏈表結構。
和雙向鏈表結點同樣,在二叉樹鏈表上添加一個頭結點,以下圖所示,並令其lchild域的指針指向二叉樹的根結點(圖中第一步),其rchild域的指針指向中序遍歷訪問時的最後一個結點(圖中第二步)。反之,令二叉樹的中序序列中第一個結點中,lchild域指針和最後一個結點的rchild域指針均指向頭結點(圖中第三和第四步)。這樣的好處是:咱們既能夠從第一個結點起順後繼進行遍歷,也能夠從最後一個結點起順前驅進行遍歷。
遍歷代碼以下所示。
-
//t指向頭結點,頭結點左鏈lchild指向根結點,頭結點右鏈rchild指向中序遍歷的最後一個結點。
-
//中序遍歷二叉線索樹表示二叉樹t
-
int InOrderThraverse_Thr(BiTree t)
-
{
-
BiTree p;
-
p = t->lchild;
//p指向根結點
-
while(p != t)
//空樹或遍歷結束時p == t
-
{
-
while(p->ltag == Link)
//當ltag = 0時循環到中序序列的第一個結點
-
{
-
p = p->lchild;
-
}
-
printf(
"%c ", p->data);
//顯示結點數據,能夠更改成其餘對結點的操做
-
while(p->rtag == Thread && p->rchild != t)
-
{
-
p = p->rchild;
-
printf(
"%c ", p->data);
-
}
-
-
p = p->rchild;
//p進入其右子樹
-
}
-
-
return OK;
-
}
說明:
-
#include <stdio.h>
-
#include <stdlib.h>
-
-
#define ERROR 0
-
#define OK 1
-
-
typedef
enum{Link, Thread} PointerTag;
//link = 0表示指向左右孩子指針
-
//Thread = 1表示指向前驅或後繼的線索
-
typedef
struct BitNode
-
{
-
char data;
//結點數據
-
struct BitNode *lchild;
//左右孩子指針
-
struct BitNode *rchild;
-
PointerTag ltag;
//左右標誌
-
PointerTag rtag;
-
}BitNode, *BiTree;
-
-
BiTree pre;
//全局變量,始終指向剛剛訪問過的結點
-
-
//前序建立二叉樹
-
void CreateTree(BiTree *t)
-
{
-
char ch;
-
scanf(
"%c", &ch);
-
-
if(ch ==
'#')
-
{
-
*t =
NULL;
-
}
-
else
-
{
-
(*t) = (BiTree)
malloc(
sizeof(BitNode));
-
if((*t) ==
NULL)
-
{
-
return;
-
}
-
(*t)->data = ch;
-
CreateTree(&((*t)->lchild));
-
CreateTree(&((*t)->rchild));
-
}
-
}
-
-
-
//t指向頭結點,頭結點左鏈lchild指向根結點,頭結點右鏈rchild指向中序遍歷的最後一個結點。
-
//中序遍歷二叉線索樹表示的二叉樹t
-
int InOrderThraverse_Thr(BiTree t)
-
{
-
BiTree p;
-
p = t->lchild;
//p指向根結點
-
while(p != t)
-
{
-
while(p->ltag == Link)
//當ltag = 0時循環到中序序列的第一個結點
-
{
-
p = p->lchild;
-
}
-
printf(
"%c ", p->data);
//顯示結點數據,能夠更改成其餘對結點的操做
-
while(p->rtag == Thread && p->rchild != t)
-
{
-
p = p->rchild;
-
printf(
"%c ", p->data);
-
}
-
-
p = p->rchild;
//p進入其右子樹
-
}
-
-
return OK;
-
}
-
-
//中序遍歷進行中序線索化
-
void InThreading(BiTree p)
-
{
-
if(p)
-
{
-
InThreading(p->lchild);
//遞歸左子樹線索化
-
if(!p->lchild)
//沒有左孩子
-
{
-
p->ltag = Thread;
//前驅線索
-
p->lchild = pre;
//左孩子指針指向前驅,這裏是第3步
-
}
-
if(!pre->rchild)
//沒有右孩子
-
{
-
pre->rtag = Thread;
//後繼線索
-
pre->rchild = p;
//前驅右孩子指針指向後繼(當前結點p)
-
}
-
pre = p;
-
-
InThreading(p->rchild);
//遞歸右子樹線索化
-
}
-
}
-
//創建頭結點,中序線索二叉樹
-
int InOrderThread_Head(BiTree *h, BiTree t)
-
{
-
(*h) = (BiTree)
malloc(
sizeof(BitNode));
-
if((*h) ==
NULL)
-
{
-
return ERROR;
-
}
-
-
(*h)->rchild = *h;
-
(*h)->rtag = Link;
-
-
if(!t)
//若是爲NULL
-
{
-
(*h)->lchild = *h;
-
(*h)->ltag = Link;
-
}
-
else
-
{
-
pre = *h;
-
(*h)->lchild = t;
//第一步
-
(*h)->ltag = Link;
-
InThreading(t);
//找到最後一個結點
-
pre->rchild = *h;
//第四步
-
pre->rtag = Thread;
-
(*h)->rchild = pre;
//第二步
-
}
-
}
-
-
int main(int argc, char **argv)
-
{
-
BiTree t;
-
BiTree temp;
-
-
printf(
"請輸入前序二叉樹的內容:\n");
-
CreateTree(&t);
//創建二叉樹
-
InOrderThread_Head(&temp, t);
//加入頭結點,併線索化
-
printf(
"輸出中序二叉樹的內容:\n");
-
InOrderThraverse_Thr(temp);
-
-
printf(
"\n");
-
return
0;
-
}
轉自:[https://blog.csdn.net/u014492609/article/details/40477795](https://blog.csdn.net/u014492609/article/details/40477795)