中序線索二叉樹的建立與遍歷

MorePay發表於2018-12-22

今天看了一下嚴奶奶的資料結構,看到線索二叉樹這一塊,僅僅記錄一下自己的學習體會,,第一次寫,主要希望能理清楚自己的思緒

 首先來看一下二叉樹遍歷的實質,其實是對非線性結構進行線性化操作,使這些線性序列中有且僅有一個前驅和後繼(這裡指直接前驅和直接後繼),如

a+b*c-d-e/f 中c的前驅是*,後繼是-, 當使用二叉連結串列時只能找到節點的左孩子和右孩子的資訊,而不能直接得到該節點在任意一種序列的前驅和後繼資訊,這裡可以在每個節點的儲存結構中新增上指向前驅和後繼的兩個指標,但是考慮到儲存密度,以及在有n個節點的二叉樹裡面必定存在n+1個空指標域,我們在結構中新增兩個變數ltag和rtag 當他們為0時代表著指向的是左右孩子節點,為1代表著指向前驅、後繼 接下來主要看程式碼實現

先來看結構定義,沒啥好說的


typedef enum pointer{
	tlink,thread
}fingurePointer;

typedef struct  binNode{
	struct  binNode *lchild,*rchild;
	fingurePointer ltag,rtag;
	elemtype data;	
}thrNode,*orderTree; 

再來看生成樹的方法,採用佇列生成,輸入‘,’表示的虛節點,即沒有該節點,當然也可以用前序遍歷的遞迴方法輸入,這都無關緊要。


orderTree creatthrTree()
{
	orderTree s[100],p,root;
	
	int front=1,rear = 0;
	
	char ch = getchar();
	 
	
	while(ch!='#')
	{
		p = NULL;
		//非虛節點 
		if(ch!=',')
		{
			p = (struct  binNode *)malloc(sizeof(struct  binNode)); 
			
			p->data = ch;
			p->ltag = tlink;
			p->rtag = tlink;
			p->lchild = NULL;
			p->rchild = NULL;
		}
		s[++rear] = p; 
		if(rear==1)
			root=p;
		else{
			if(p!=NULL&&s[front]!=NULL)
			{
			
			if(rear%2==0)
				s[front]->lchild = p;
			else
				s[front]->rchild = p;
				}				
				if(rear%2==1)
				front++;			
		}
		ch = getchar();	
		}
	return root;
} 

既然想到了,就記錄一下,使用前序遍歷遞迴演算法建立二叉樹

void CreateBiTree(orderTree *T)
  {
      char ch;
      scanf("%c",&ch);
      if(ch==' ')
          *T=NULL;
      else
     {
         *T=(struct binNode *)malloc(sizeof(thrNode));
         if(!*T)
             exit(-1);
          (*T)->data=ch;
          (*T)->ltag = tlink;
          (*T)->rtag = tlink;
          CreateBiTree(&(*T)->lchild);
         CreateBiTree(&(*T)->rchild);
      }
 }

然後隨便寫一個遍歷演算法,遞迴非遞迴都行,我用的是中序遍歷遞迴形式

void visit(orderTree node)
{
	orderTree p;
	p = node;
	if(p!=NULL)
	{
	visit(p->lchild);
	printf("%d  %c   %d\n",p->ltag,p->data,p->rtag);
	visit(p->rchild);
	}
}

 

既然有遞迴了,那也順便記錄一下非遞迴的寫法,唉,自己記性實在是差到爆,不想以後手忙腳亂去翻

void visitNotRecusion(orderTree node)
{
	orderTree p,s[100];
	int top = 0;
	p = node;
	while(p!=NULL||top>0)
	{
		//左子樹 
		while(p!=NULL)
		{
			s[++top] = p;
			p = p->lchild;
		}
		p = s[top--];
		printf("%c ",p->data);
		p = p->rchild;
	}
	 printf("\n");
}

好了,萬事俱備,開始進行二叉樹---->線索二叉樹,首先,假定我們已經有一個變數pre指向前一個節點,當前節點為p,則我們要構建中序線索二叉樹,容易讓我們先寫出中序遍歷的框架出來

deal(t->lchild)

中間真正處理

deal(t->rchild)

然後來看二叉樹線索化的實質,就是將那些空指標利用起來指向前驅或後繼,所以,在當前節點p,如果p的左指標為空,我們將p的ltag改為thread,p->lchild指向pre,然後處理pre的右節點,為什麼處理pre的右節點,自己也不太理解,姑且淺顯以為因為pre儲存的是前一個節點,本節點的右節點留待下一輪進行修改或保持,而且無法直接找到當前節點的後繼,具體演算法如下

void inThreading(orderTree node)
{
	if(node)
	{
		inThreading(node->lchild);//線索化左子樹 
		
		if(!node->lchild){//左子樹為空,改變ltag,指定lchild為pre 
			node->ltag = thread;
			node->lchild = pre;
		}
		if(!pre->rchild)//右子樹為空,改變rtag,指定pre的 rchild 
		{
			pre->rtag = thread;
			pre->rchild = node;	
		}
		pre= node; 
		inThreading(node->rchild);		
	}
}

然後模擬迴圈連結串列,建立一個頭結點,將它的lchild指向根節點,rchild域指向最後一個節點我們將二叉樹串起來,這樣做的好處就是無論給定哪個節點我們都能從該節點訪問完全所有節點

Status InOrderThreading(orderTree &head,orderTree root)
{
	
	if(!(head=(struct binNode *)malloc(sizeof(struct binNode))))
		return ERROR;
	/*頭結點左孩子指向根節點,右節點指向直接前驅*/ 
	head->ltag = tlink; head->rtag = thread;
	head->rchild = head;//右指標回指
	
	if(!root) head->lchild = head;
	 else{
	 	head->lchild = root;
		 pre = head;	
		 inThreading(root);
		 pre->rchild = head;
		 pre->rtag = thread;
		 head->rchild = pre;
	 }
	 return OK;
}

然後,給出中序線索二叉樹的遍歷程式碼

Status InOderTranverse(orderTree head)
{
	orderTree p;
	p = head->lchild;//p指向根節點
	
	while(p!=head)
	{
		//左子樹 
		while(p->ltag==tlink)
			p = p->lchild;
		visitNode(p);
		//右子樹 
		while(p->rtag ==thread&&p->rchild!=head)
		{
			p = p->rchild;
			visitNode(p);	
		}
		p = p->rchild;
	} 
	 return OK;
}

 

好了,所有程式碼


#define elemtype char

#define Status int

const int ERROR = 0;

const int OK = 1;

#include "stdlib.h"
#include "stdio.h"




typedef enum pointer{
	tlink,thread
}fingurePointer;

typedef struct  binNode{
	struct  binNode *lchild,*rchild;
	fingurePointer ltag,rtag;
	elemtype data;	
}thrNode,*orderTree; 

orderTree pre;


orderTree creatthrTree()
{
	orderTree s[100],p,root;
	
	int front=1,rear = 0;
	
	char ch = getchar();
	 
	
	while(ch!='#')
	{
		p = NULL;
		//非虛節點 
		if(ch!=',')
		{
			p = (struct  binNode *)malloc(sizeof(struct  binNode)); 
			
			p->data = ch;
			p->ltag = tlink;
			p->rtag = tlink;
			p->lchild = NULL;
			p->rchild = NULL;
		}
		s[++rear] = p; 
		if(rear==1)
			root=p;
		else{
			if(p!=NULL&&s[front]!=NULL)
			{
			
			if(rear%2==0)
				s[front]->lchild = p;
			else
				s[front]->rchild = p;
				}				
				if(rear%2==1)
				front++;			
		}
		ch = getchar();	
		}
	return root;
} 

/*前序遍歷的遞迴建立樹*/
void CreateBiTree(orderTree *T)
  {
      char ch;
      scanf("%c",&ch);
      if(ch=='#')
          *T=NULL;
      else
     {
         *T=(struct binNode *)malloc(sizeof(thrNode));
         if(!*T)
             exit(-1);
          (*T)->data=ch;
          (*T)->ltag = tlink;
          (*T)->rtag = tlink;
          CreateBiTree(&(*T)->lchild);
         CreateBiTree(&(*T)->rchild);
      }
 }

void visit(orderTree node)
{
	orderTree p;
	p = node;
	if(p!=NULL)
	{
	visit(p->lchild);
	printf("%d  %c   %d\n",p->ltag,p->data,p->rtag);
	visit(p->rchild);
	}
}

void visitNotRecusion(orderTree node)
{
	orderTree p,s[100];
	int top = 0;
	p = node;
	while(p!=NULL||top>0)
	{
		//左子樹 
		while(p!=NULL)
		{
			s[++top] = p;
			p = p->lchild;
		}
		p = s[top--];
		printf("%c ",p->data);
		p = p->rchild;
	}
	 printf("\n");
}



//中序線索二叉樹 
void inThreading(orderTree node)
{
	if(node)
	{
		inThreading(node->lchild);//線索化左子樹 
		
		if(!node->lchild){//左子樹為空,改變ltag,指定lchild為pre 
			node->ltag = thread;
			node->lchild = pre;
		}
		if(!pre->rchild)//右子樹為空,改變rtag,指定pre的 rchild 
		{
			pre->rtag = thread;
			pre->rchild = node;	
		}
		pre= node; 
		inThreading(node->rchild);		
	}
}



Status InOrderThreading(orderTree &head,orderTree root)
{
	
	if(!(head=(struct binNode *)malloc(sizeof(struct binNode))))
		return ERROR;
	/*頭結點左孩子指向根節點,右節點指向直接前驅*/ 
	head->ltag = tlink; head->rtag = thread;
	head->rchild = head;//右指標回指
	
	if(!root) head->lchild = head;
	 else{
	 	head->lchild = root;
		 pre = head;	
		 inThreading(root);
		 pre->rchild = head;
		 pre->rtag = thread;
		 head->rchild = pre;
	 }
	 return OK;
}

void visitNode(orderTree p)
{
	if(p!=NULL)
	printf("%c ",p->data);	
} 

Status InOderTranverse(orderTree head)
{
	orderTree p;
	p = head->lchild;//p指向根節點
	
	while(p!=head)
	{
		//左子樹 
		while(p->ltag==tlink)
			p = p->lchild;
		visitNode(p);
		//右子樹 
		while(p->rtag ==thread&&p->rchild!=head)
		{
			p = p->rchild;
			visitNode(p);	
		}
		p = p->rchild;
	} 
	 return OK;
}

int main() 
{
	orderTree root;
	CreateBiTree(&root);  //先序遍歷的遞迴構建
	//root = 	creatthrTree();//按行構建
	visit(root);
	printf("\n-------\n");
	visitNotRecusion(root);
	orderTree head;
	Status x =	InOrderThreading(head,root);
	InOderTranverse(head);
	
}

輸入範例:ABC##D##E#F##   //採用遞迴構建樹

     逐行構建  -+/a*ef,,b-,,,,,,,,,,cd#                     採用visit和InorderTranverse得到相同的結果,a+b*c-d-e/f

如果有寫的不對的地方希望大家多多批評,指正。畢竟自己小菜雞一枚.

相關文章