數據結構與算法(13)--二叉樹

二叉樹的特點

  • 每個節點最多有兩顆子樹
  • 左右子樹有順序

特殊的樹

  • 斜樹
  • 滿二叉樹
  • 完全二叉樹

二叉樹的順序存儲結構

二叉樹的順序存儲結構就是用一組地址連續的存儲單元依次自上而下、自左向右存儲完全二叉樹上的結點元素對於一般的二叉樹,我們先轉換爲完全二叉樹進行存儲,用空值來代替。

二叉樹的鏈式存儲結構

叉樹的鏈式存儲結構就是
二叉樹每個結點最多有兩個孩子,所以設計二叉樹的結點結構時,考慮兩個指針指向該結點的兩個孩子這種結構又被稱爲二叉鏈表可以再加一個指針域來指向該結點的雙親結點,那這是三個指針的鏈表就叫三叉鏈表。

typedef struct BiTNode{
	ElemType data; //數據域
	struct BiTNode *lchild,*rchild; //指向該結點的左右孩子的指針 
}BiTNode,*BiTree; // 二叉樹結點結構

二叉樹的遍歷

  • 先序遍歷
    • 遞歸實現
      如果二叉樹爲空,則什麼也不做。
      否則:
      訪問根結點
      先序遍歷左子樹
      先序遍歷右子樹
      void PreOrder(BiTree T)
      {
      	if(T!=NUll)
      	{
      		printf("%c",T->data); //訪問根結點
      		PreOrder(T->lchild); //遞歸遍歷左子樹
      		PreOrder(T->rchild); //遞歸遍歷右子樹
      	}
      	else
      	{
      		return;
      	}
      }
      
    • 非遞歸實現
      void PreOrder(BiTree b)
      {
      	InitStack(S); //初始化一個棧
      	BiTree p = b; //工作指針p
      	while(p || !IsEmpty(S)) 
      	/*
      	**如果當前指針指向的二叉樹中一個結點不爲NULL 
      	**或者 用來存放遍歷的二叉樹結點的棧不爲空
      	*/
      	{
      		printf("%c",p->data); //先序遍歷結點,輸入根根結點
      		Push(S,p); //進棧保存
      		p = p->lchild; //指向下一個左孩子
      	}
      	//一直把所有的向左遍歷的結點進棧後
      	//開始出棧,然後把右面的進棧
      	if(!IsEmpty(S)) //
      	{
      		p = Pop(s)
      		p = p->rchild;
      	}
      }
      
  • 中序遍歷
    • 遞歸實現
      如果二叉樹爲空,則什麼也不做。
      否則:
      中序遍歷左子樹
      訪問根結點
      中序遍歷右子樹
      void InOrder(BiTree T)
      {
      	if(T!=NULL)
      	{
      		InOrder(T->lchild); //遞歸遍歷左子樹
      		printf("%c",T->data); //訪問根結點
      		InOrder(T->rchild); //遞歸遍歷右子樹
      	}
      	else
      	{
      		return;
      	}
      }
      
    • 非遞歸實現
      void Inorder(BiTree b)
      {
      	InitStack(S); //初始化一個棧
      	BITree p  = b; //工作指針p
      	while(p || !IsEmpty(S))
      	{
      		while(p)
      		{
      			Push(S,p); //把結點進棧
      			p = p->lchild;
      		}
      		p = Pop(S);
      		printf("%c",p->data);
      		p = p->rchild; // 遍歷右孩子
      	}
      }
      
  • 後序遍歷
    • 遞歸實現
      如果二叉樹爲空,則什麼也不做。
      否則:
      後序遍歷左子樹
      後序遍歷右子樹
      訪問根結點
      void PostOrder(BiTree T)
      {
      	if(T!=NULL)
      	{
      		PostOrder(T->lchild); //遞歸遍歷左子樹
      		PostOrder(T->rchild); //遞歸遍歷右子樹
      		printf("%c",T->data); //訪問根結點
      	}
      	else
      	{
      		return;
      	}
      }
      
    • 非遞歸實現
      void PostOrder(BiTree b)
      {
      	InitStack(S);
      	BiTree p = b,r = NULL; //工作指針p,輔助指針r
      	while(p || !IsEmpty)
      	{
      		// 1.從根結點到最左下角的左子樹都要入棧
      		if(p)
      		{
      			Push(S,p);
      			p = p->lchild;
      		}
      		//2.返回棧頂的兩種情況
      		else
      		{
      			GetTop(S,p); //取棧頂元素,不是出棧
      			//右子樹還未訪問,而且右子樹不空,第一次棧頂
      			if(p->rchild && p->rchild!=r))
      			{
      				p = p->rchild; //把右子樹遍歷
      			}
      			//右子樹已經訪問過了,或者未空,接下來就是出棧操作
      			else
      			{
      				Pop(S,p); //如果訪問過
      				printf("%c",p->data);
      				r = p; //指向訪問過的右子樹根結點
      				p = NULL; // 使p爲空從而繼續訪問棧頂
      			}
      		}
      	}
      }
      
  • 層次遍歷
    若樹爲空,則什麼都不做。直接返回
    否則從樹的第一層開始訪問,從上而下逐層遍歷
    在同一層中,按從左到右的順序對結點逐個訪問。
    訪問完一個結點之後,把它的孩子存起來,而且先訪問的結點
    它的孩子也是先訪問。
    void LevelOrder(BiTree n)
    {
    	InitQueue(Q);
    	BiTree p;
    	EnQueue(Q,b); // 根結點入隊
    	while(!IsEmpty(Q)) //隊列不空循環
    	{
    		DeQueue(Q,p); //隊頭元素出隊
    		printf("%c",p->data); 
    		if(p->lchild!=NULL)
    		{
    			EnQueue(Q,p->lchild); //把左子樹進隊列
    		}
    		if(p->rchild != NULL)
    		{
    			EnQueue(Q,p->rchild); //把右子樹進隊列
    		}
    	}
    }
    

線索二叉樹

指向前驅和後繼的指針稱爲線索,加上線索的二叉鏈表就稱爲線索鏈表,相應的二叉樹就稱爲線索二叉樹。
對二叉樹以某種次序遍歷使其變成線索二叉樹的過程稱爲線索化。
通過在二叉鏈表結點的結構基礎上增加兩個標誌位 ltag 和 rtag 來進行表標識結點的左右指針是指向孩子結點還是指向前前驅後繼結點。

  • ltag==0 ——> lchild指向該結點左孩子
  • ltag==1 ——> lchild指向該結點前驅結點
  • rtag==0 ——> rchild指向該結點右孩子
  • rtag==1 ——> rchild指向該結點後繼結點是

修改下二叉樹的結點的結構

typedef struct ThreadNode{
	ElemType data;
	struct ThreadNode *lchild,*rchild;
	int ltag,rtag;
}ThreadNode,*ThreadTree; //線索鏈表

中序遍歷對二叉樹線索的遞歸算法

//
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章