深入學習二叉樹(02)線索二叉樹


1.產生背景

現在有一棵節點數目為 n 的二叉樹,采用二叉鏈表的形式存儲。對於每個節點均有指向左右孩子的兩個指針域。而節點為 n 的二叉樹一共有 n-1 條有效分支路徑。那么二叉鏈表中一共

存在2n-(n-1) = n+1 個空指針域。那么這些空指針域造成了空間浪費。

如圖:所示一棵二叉樹一共有10個節點,空指針有 11 個

 

 

 此外,當對二叉樹進行中序遍歷時可以得到二叉樹的中序列。例如上圖中二叉樹中序遍歷結果為 HDIBJEAFCG ,那么得知 A 的前序節點為 E ,后繼節點為 F。但是這種關系的獲得是建立在

完成遍歷后得到的,那么是否在遍歷之前就記錄下前驅和后繼的關系呢? 那么在尋找前驅與后繼節點時,將大大的提升效率。

2.線索化

現將某節點的空指針域指向該節點的前序后繼,定義規則如下

若結點的左子樹為空,則該結點的左孩子指針指向其前驅結點。
若結點的右子樹為空,則該結點的右孩子指針指向其后繼結點。

這種指向前驅和后繼的指針稱為線索。將一棵普通二叉樹以某種次序遍歷,並添加線索的過程稱為線索化,按規則將上圖線索化后如下圖

 

 

 圖中黑色點畫線為指向后繼的線索,紫色虛線為指向前驅的線索。可以看出通過線索化,即解決了空間浪費的問題,又解決了前驅后繼的記錄問題。

2.1 線索化帶來的問題

經過上述的線索化后,可以將一棵二叉樹線索化為一棵線索二叉樹,那么新的問題會產生,我們如何區分一個節點的 指針域是指向孩子還是前驅后繼節點呢?

為了解決這個問題,現在需要添加標志位 ltag , rtag 並定義規則如下

ltag為0時,指向左孩子,為1時指向前驅
rtag為0時,指向右孩子,為1時指向后繼

添加 ltag 和 rtag 屬性的節點結構如下

 

 轉變為如下的二叉樹

 3.實現線索二叉樹

3.1 中序線索二叉樹

為了方便構建一棵二叉樹,這里使用完全二叉樹構建線索二叉樹,如下圖是一棵完全二叉樹

 

構建完全二叉樹參考:https://www.cnblogs.com/baizhuang/p/11612961.html

接下來需要對此二叉樹進行線索化

static TreeNode *pre = NULL;
void initTag(TreeNode *p){
    if(p!=NULL){ 
        initTag(p->lchild); // 遞歸左子樹
        if(p->lchild==NULL){
            p->lchild=pre;
            p->lTag=1;
        } 
        if(pre!=NULL&&pre->rchild==NULL){
            pre->rchild=p;
            pre->rTag=1;
        }
        pre=p;
        initTag(p->rchild);// 遞歸右子樹
    }
}

線索化后如下圖

 

 方便觀察,控制台輸出如下

 

 完整代碼:

#include<stdio.h>
#include<stdlib.h>
typedef struct node{
	int data;
    struct node * lchild,* rchild;
    int lTag,rTag;
}TreeNode; 

static TreeNode *pre = NULL;

TreeNode * createTree(int data[],int n,int index){
	TreeNode *root = NULL,*lchild = NULL,*rchild = NULL;
	//create lchildTree
	if(index<=n&&index*2<=n){
		lchild = createTree(data,n,index*2);
	}
	//createRigntTree
	if(index<=n&&index*2+1<=n){
		rchild = createTree(data,n,index*2+1);
	}
	//createNode
	if(index<=n){
		root = (TreeNode *)malloc(sizeof(TreeNode));
		root->data = data[index];
		root->lchild = lchild;
		root->rchild = rchild;
		root->lTag = root->rTag = 0;	
	}
	//return root 
	return root;
}

void print(TreeNode *root){	
	if(root->lchild!=NULL){
		print(root->lchild);
	}
    printf("lchild:%10d  lTag:%3d  data:%3d  rTag:%3d  rchild:%10d   [root:%d]\n",root->lchild,root->lTag,root->data,root->rTag,root->rchild,root);
	if(root->rchild!=NULL){
		print(root->rchild);
	}
}

void printSearchTree(TreeNode *root){
	if(root!=NULL){
	 if(root->lTag==0){
	 	printSearchTree(root->lchild);
	 }
	printf("lchild:%10d  lTag:%3d  data:%3d  rTag:%3d  rchild:%10d   [root:%d]\n",root->lchild,root->lTag,root->data,root->rTag,root->rchild,root);
	// printf("%d",root->lTag);
	 if(root->rTag==0){
	 	printSearchTree(root->rchild);
	 }
	}
}


void initTag(TreeNode *p){
    if(p!=NULL){ 
        initTag(p->lchild); // 遞歸左子樹
        if(p->lchild==NULL){
            p->lchild=pre;
            p->lTag=1;
        } 
        if(pre!=NULL&&pre->rchild==NULL){
            pre->rchild=p;
            pre->rTag=1;
        }
        pre=p;
        initTag(p->rchild);// 遞歸右子樹
    }
}


int main(){
	int data[] ={0,3,2,5,8,4,7,6,9};
	TreeNode * root = createTree(data,8,1);
	printf("\n完全二叉樹輸出:\n");
	print(root);
	printf("\n.................\n");
	
	initTag(root); 
	printf("\n-------------------------\n");
	printf("\n線索二叉樹輸出:\n");
	printSearchTree(root);
	printf("\n-------------------------\n");
	return 0;
} 

 參考:https://www.jianshu.com/p/3965a6e424f5


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM