數據結構編程題系列(四)樹

3.樹

3.1 二叉樹

3.1.1鏈式結構

3.1.1.1 遞歸算法
構建二叉樹

1.給出某二叉樹先序遍歷數組A[1...n]和中序遍歷數組B[1...n],編寫算法創建該二叉樹的二叉鏈表。node

​ (1)根據先序序列肯定樹的根節點算法

​ (2)根據根節點在中序序列中劃分出的二叉樹的左右子樹所包含的結點,而後根據左右子樹的結點在先序序列中的次序能夠肯定子樹的根結點,即回到步驟(1)數組

​ 重複直到每一個子樹僅有一個節點爲止。post

Bitree PreInCreat(ElemType pre[],ElemType in[],int L1,int H1,int L2,int H2) {	//l1和h1爲先序的第一和最後一個結點下標,l2,h2爲中序的第一和最後一個結點下標
    //初始時,l1=l2=1,h1=h2=n
    int L_len,R_len;
    Bitree root=(BiTNode*)malloc(sizeof(BiTNode));		//建根節點
    root->data=pre[L1];								//根節點的數據
    for(i=L2;in[i]!=root->data;i++);					//查找根所在位置
    L_len=i-L2;					//左子樹的長度(結點個數)
    R_len=H2-i;					//右子樹的長度(結點個數)
    if(L_len)				//遞歸創建左子樹
    	root->lchild=PreInCreat(pre,in,L1+1,L1+L_len,L2,L2+L_len-1);
    else					//左子樹爲空
        root->lchild=NULL;
    if(R_len)				//遞歸創建右子樹
    	root->rchild=PreInCreat(pre,in,H1-R_len+1,H1,H2-R_len+1,H2);
    else					//右子樹爲空
        root->rchild=NULL;
    return root;			//返回根結點 
}
複製代碼
Bitree PreInCreat(ElemType *pre,ElemType *in,int A_len) {	//A_len 是當前樹的長度
    int n=A_len;
    Bitree *p; int k;
    if(n<=0) return NULL;
    Bitree root=(BiTNode*)malloc(sizeof(BiTNode));
    root->data=*pre;
    for(p=in;p<in+n;p++)
        if(*p==*pre) break;		//找到根節點
    k=p-in;			//得到左子樹的長度
    root->lchild=PreInCreat(pre+1,in,k);	//創建左子樹
    root->rchild=PreInCreat(pre+1+k,in+1+k,n-1-k);		//創建右子樹
}
複製代碼

2.給出某二叉樹後序遍歷數組A[1...n]和中序遍歷數組B[1...n],編寫算法創建該二叉樹的二叉鏈表。ui

Bitree PostInCreat(ElemType post[],ElemType in[],int L1,int H1,int L2,int H2) {
	int L_len,R_len;
    BiTree root = (BiTree)malloc(sizeof(BTNode));
    root->data = post[H1];
    int i;
    for(i=L2;in[i]!=post[H2];i++);
    L_len = i - L2;
    R_len = H2 - i;
    if(L_len)    //遞歸創建左子樹
    {
        PostInCreat(post,in,L1,L1+L_len-1,L2,i-1);
    }else
        root->lchild = NULL;
    if(R_len)	//遞歸創建右子樹
    {
        PostInCreat(post,in,H2-R_len-1,H2-1,i+1,H2);
    }else
        root->rchild = NULL;
    return root;
}
複製代碼

3.給出某二叉樹層次遍歷數組A[1...n]和中序遍歷數組B[1...n],編寫算法創建該二叉樹的二叉鏈表。spa

​ 解:按層次遍歷,第一個節點爲根,據此將中序遍歷分爲左右兩個部分,若左子樹不空,則層次遍歷的第二個節點爲左子樹的根,若左子樹爲空,則第二個節點爲右子樹的根,對其左右子樹也進行相應的分析。設計

BiTree LevelInCreat(ElemType level[],in[],int L1,int H1,int L2,int H2) //level,in分別爲層次遍歷和中序遍歷,L1爲層次遍歷頭,H1爲尾,L2,H2相似 {
    BiTree bt=new(BiNode);
    bt->data=level[L1];	//層次遍歷中第一個節點爲根
    for(i=L2;i<=H2;i++)
        if(level[L1]==in[i])  break;	//找到中序中的根所在位置
    if(i==L2)	bt->lchild=NULL;	//二叉樹無左子樹則填空
    else{
        for(int k=L1+1;k<=H1;k++)	//找層次遍歷中左子樹根節點
        {	for(int j=L2;j<i;j++)	//將左子樹的層次遍歷存儲起來
        		if(level[k]==in[j])	
                    level1[++ii]=level[k];	//ii初值爲-1
         	bt->lchild=LevelInCreat(level1,in,0,ii,L1,i-1);//生成左子樹
        }
    }
    //右子樹操做與左子樹相似
    if(i==H2)	bt->rchild=NULL;
    else{
        for(k=L1+1;k<=H1;k++)
            for(j=i+1;j<=H2;j++)
            	if(level[k]==in[j])
                    level1[++ii]=level[k];	//ii初值爲-1
        bt->rchild=LevelInCreat(level1,in,0,ii,i+1,H2);
    }
    return bt;
}

複製代碼

​ 解2:(此爲本身寫的較簡便版本,犧牲了時間複雜度,可是代碼較簡便)指針

思路:層次遍歷中的元素依次與中序遍歷比較,最早相同的必定是根節點,遞歸的創建二叉樹。code

BiTree LICreat(int lev[],int in[],int n) //簡易版本的創建方式,n爲中序遍歷的長度 {
    BiTree bt=(BiTree)malloc(sizeof(BiTree));
    if(!n)	//若爲空則返回NULL
        return NULL;
    int i=n;	//初始化爲n是爲了下面i==n的判斷 
    for(int* p=lev;i==n;p++)	
	//判斷中序遍歷中是否出現了層次遍歷中的元素,沒有的話i會一直走到盡頭,即爲n 
    {
        for(i=0;i<n;i++)	//若沒有相等的,則該循環結束i爲n 
        	if(*p==in[i]) break;    	
    }
    bt->data=in[i];	//根節點的值
    bt->lchild=LICreat(lev,in,i);	//遞歸創建左子樹
    bt->rchild=LICreat(lev,in+i+1,n-i-1);	//遞歸創建右子樹
    return bt;
}

複製代碼

4.滿二叉樹:遞歸

​ (1)給出一個把後序遍歷序列轉化爲先序遍歷序列的算法。

​ (2)給出一個把先序遍歷序列轉化爲後序遍歷序列的算法。

​ 解:(1)主要思想是滿二叉樹的左子樹的數量和右子樹的數量相同,先序序列的根爲其第一個數。

void PostToPre(ElemType post[],pre[],int L1,int H1,int L2,int H2) //其中L1,H1爲post的頭尾下標,同理L2,H2爲pre的頭尾下標 {
    int num=0;	//記錄左或者右子樹的結點個數
    if(H1>=L1)		//在頭尾下標正常時遞歸
    {
        pre[L2]=post[H1];//後序遍歷的根節點爲最後一個
        num=(H1-L1)/2;	//左右子樹的節點數相同
        PostToPre(post,pre,L1,L1+num-1,L2+1,L2+num);	//遞歸創建左子樹
        PostToPre(post,pre,L1+num,H1-1,L2+num+1,H2);	//遞歸創建右子樹
    }
}

複製代碼

​ 解:(2)與上面相似 王道p123 15

5.由正則二叉樹的前序序列和後續序列來肯定一棵正則二叉樹

BiTree PrePostCreate(ElemType pre[],ElemType post[],int L1,int H1,int L2,int H2) {
    BiTree p;
	if(L1<=H1)
    {
    	p=(BiTree)malloc(sizeof(BTNode));
        p->data = pre[L1];
        if(L1==H1)
        {
            p->lchild = p->rchild = NULL;
        }
        else
        {
            int i,L;
            for(i=L2;i<=H2;i++)
                if(post[i]==pre[L1+1])  //查找左子樹的根
                    break;
            L = i-L2+1;                 //左子樹節點數 
            p->lchild = PrePostCreate(pre,post,L1+1,L1+L-1,L2,i);   
			p->rchild = PrePostCreate(pre,post,L1+L+1,H1,i+1,h2-1);
        }
    }
    return p;
}

複製代碼
判斷類似(相等)

1.判斷兩個二叉樹是否類似

bool similar(BiTree T1,BiTree T2) {
    int LEFT,RIGHT;
    if(T1==NULL&&T2==NULL)
        return 1;
    else if(T1==NULL||T2==NULL)
        return 0;
    else
    {
        LEFT = similar(T1->lchild,T2->lchild);
        RIGHT= similar(T1->rchild,T2->rchild);
        return LEFT&&RIGHT;
    }
}

複製代碼

2.判斷兩個二叉樹是否相等(1800 63)

if(p==null&&q==null)
    return(1);
else if(!p&&q||p&&!q)
    return(0);
    else if(p->data!=q->data)
    return(0);
       else return(Equal(p->lchild,q->lchild)&&Equal(p->rchild,q->rchlid));

複製代碼
基礎操做

1.使用遞歸算法完成如下二叉樹操做:

​ (1)高度

​ (2)總結點個數

​ (3)交換左右子樹

​ (4)葉子節點個數

​ (5)度爲2的結點個數

​ (6)度爲1的結點個數(同上相似)

​ (7)計算每一個結點平衡因子

​ 解:(1)

int height(B) {	int hl,hr;
    if(!B)
        return 0;
 	hl=height(B->lchild);
 	hr=height(B->rchild);
 	return hl>hr?hl+1:hr+1;
}

複製代碼

​ 解:(2)

int size(B) {
        return (!B)?0:(size(B->lchild)+size(B->rchild)+1);
}

複製代碼

​ 解:(3)使用後序遍歷的思想,爲空時遞歸終點倒回

void exchange(B) {	
    if(B)	//判空(必寫,出口)
    {
        exchange(B->lchild);	//先遞歸交換左子樹
        exchange(B->rchild);	//遞歸交換右子樹
        t=B->lchild;			//交換根
        B->lchild=B->rchild;
        B->rchild=t;
        
    }
}

複製代碼
解:(4)採用遞歸算法 葉子節點個數=左子樹的葉子節點個數+右子樹的葉子節點個數,出口爲葉子節點
複製代碼
int num_leaf(B) {
    if(!B->lchild)
        if(!B->rchild)
            return 1;
    return num_leaf(B->lchild)+num_leaf(B->rchild);
}

複製代碼

​ 解:(5)

int TwoNodes(B) {
    if(!B)
        return 0;
    else if(B->lchild&&B->rchild)
        return TwoNodes(B->lchild)+TwoNodes(B->rchild)+1;
    else
        return TwoNodes(B->lchild)+TwoNodes(B->rchild);
}

複製代碼

​ 解:(7)

int Height(BiTree bt)//求二叉樹bt的深度 {
    int hl,hr;
    if (bt==null)
        return(0);
    else
    {
        hl=Height(bt->lchild);
        hr=Height(bt->rchild);
        if(hl>hr)
            return (hl+1);
        else
            return(hr+1);
    }
}// Height
void Balance(BiTree bt) //計算二叉樹bt各結點的平衡因子 {
    if(bt)
    {
        Balance(bt->lchild); //後序遍歷左子樹
        Balance(bt->rchild); //後序遍歷右子樹
        hl=Height(bt->lchild);
        hr=Height(bt->rchild);//求左右子樹的高度
        bt->bf=hl-hr; //結點的平衡因子bf 
    }   
}
複製代碼

17.複製一個二叉樹的算法

BiTree Copy(BiTree B) {	
	BiTree T;
    if(B==NULL) T=NULL;
    else{
        T=new(BiNode);
        T->data=B->data;
        T->lchild=Copy(B->lchild);
        T->rchild=Copy(B->rchild);
    }
    return T;
}
複製代碼
3.1.1.2 非遞歸算法
先序遍歷

1.先序遍歷非遞歸算法。

​ 解:藉助棧,根節點先入棧,以後棧不空循環,訪問出棧節點後入右左節點。

void PreOrder(Bitree T) {
    InitStack(S);Bitree p=T;	//藉助棧
    Push(S,p);			//根節點入棧
    while(!IsEmpty(S))		//棧空時則退出循環 
    {
        Pop(S,p);visit(p);	//出棧並訪問
        if(p->rchild)		//如有右孩子,則右孩子進棧
            Push(S,p->rchild);
        if(p->lchild)		//如有左孩子,則左孩子進棧
            Push(S,p->lchild);
    }
}
複製代碼

2.求二叉樹的先序序列遍歷的最後一個結點的指針,使用非遞歸方式,且不用棧

​ 解:從根開始的任何結點,如有右子樹,則是右子樹最右下的結點,不然爲左子樹最右下的結點。

BiNode* PreOrder_Last(BiTree B) {	
    BiNode *p=B;
    while(p)
    {//依次序來,有右孩子就向右,沒有就向左,爲葉子結點則返回
        if(p->rchild)
            p=p->rchild;
        else if(p->lchild)
            p=p->lchild;
        else
            return p;
	}
}
複製代碼
中序遍歷

1.中序遍歷非遞歸算法。

​ 解:中序遍歷第一個老是最左端的,而後右邊走一次再次走到最左端

void InOrder(Bitree T) {
    InitStack(S);Bitree p=T;	//藉助棧
    while(p||!IsEmpty(S))		//棧不空或者p不空的時候循環
    {
        if(p)			//走到最左端
        {
            Push(S,p);
            p=p->lchild;
        }
        else{			//若爲空則出棧(中間節點)並訪問,再走右邊孩子
            Pop(S,p);visit(p);
            p=p->rchild;
        }
    }
}

複製代碼
後序遍歷

1.後序遍歷非遞歸算法。

void PostOrder(Bitree T) {
    InitStack(S);Bitree p=T;	//藉助棧
    Bitree r=NULL;		//輔助指針指向最近訪問的結點
    while(p||!IsEmpty(S))		//棧不空或者p不空的時候循環
    {
        if(p)
        {
            Push(S,p);
            p=p->lchild;
        }
        else{
            Gettop(S,p);	//注意不是pop
            if(p->rchild&&p->rchild!=r)
            {
                p=p->rchild;
                push(S,p);
                p=p->lchild;
            }
            else
        }
    }
}

複製代碼

2.求二叉樹的後序遍歷的第一個結點的指針。使用非遞歸方式,且不用棧

​ 解:如有左子樹,則爲左子樹最左下的葉結點,若無左子樹,則爲右子樹上最左下的葉子節點

BiNode* PostOrder_First(BiTree B) {	
    BiNode *p=B;
    while(p)
    {//依次序來,有左孩子就向左,沒有就向右,爲葉子結點則返回
        if(p->lchild)
            p=p->lchild;
        else if(p->rchild)
            p=p->rchild;
        else
            return p;
	}
}

複製代碼

3.在二叉樹中查找值爲x的結點,試編寫算法打印值爲x的節點的全部祖先,假設值爲x的結點很少於一個。

​ 解:後序遍歷到該結點時棧中均爲其祖先(或者說路徑)

void Search(BiTree bt,ElemType x) //在二叉樹bt中,查找值爲x的結點,並打印其全部祖先 {
    typedef struct {BiTree t; int tag; }stack;//tag=0表示左子女被訪問,tag=1表示右子女被訪問
    stack s[];  //棧容量足夠大
    top=0;
    while(bt!=null||top>0)
    {
        while(bt!=null && bt->data!=x)                  //結點入棧
        {
            s[++top].t=bt;
            s[top].tag=0;
            bt=bt->lchild;
        } //沿左分枝向下
        if(bt->data==x)
        {
            printf(「所查結點的全部祖先結點的值爲:\n」);   //找到x
            for(i=1;i<=top;i++)
                printf(s[i].t->data);
            return;
        } //輸出祖先值後結束
        while(top!=0 && s[top].tag==1)
            top--;           //退棧(空遍歷)
        if(top!=0) 
        {
            s[top].tag=1;
            bt=s[top].t->rchild;
        }  //沿右分枝向下遍歷
    }
}

複製代碼

4.求一棵二叉樹的p,q的公共節點

#defined maxsize 1000
typedef struct SNode {
	BiTree t;
    int tag;
}SNode;

BiTree findCommonNode(BiTree t,BiTree p,BiTree q) {
    SNode s[maxsize],s1[maxsize];
    int top,top1,i,j;
    top=top1=0;
    BiTree bt;
    bt = t;
    while(bt||top>0)
    {
        while(bt!=null && bt!=p && bt!=q)
        {
            s[++top].t=bt;
            s[++top].tag=0;
            bt = bt->lchild;
        }
        if(bt==p)
        {
            for(i=1;i<=top;i++)
                s1[i]=s[i];
            top1 = top;
        }
        if(bt==q)
        {
            for(i=top;i>0;i--)
                for(j=top1;j>0;j--)
                    if(s1[j].t==s[i].t)
                    {
                        return s[i].t;
                    }
        }
        while(top!=0 && s[top].tag==1) top--;
        if(top!=0)
        {
            s[top].tag=1;
            bt=s[top].t->rchild;
        }
    }
    return NULL;
}

複製代碼
層次遍歷

1.層次遍歷非遞歸算法。

void LevelOrder(BiTree T) //二叉樹的層次遍歷算法 {
    InitQueue(Q);
    BiTree p;
    EnQueue(Q,T);	//根節點入隊
    while(!IsEmpty(Q))	//隊列不空則循環
    {
        DeQueue(Q,p);	//出隊列
        visit(p);	//訪問節點
        if(p->lchild)	//有左孩子則入隊
            EnQueue(p->lchild);
        if(p->rchild)	//有右孩子則入隊
            EnQueue(p->rchild);
    }
}

複製代碼

2.給出二叉樹的自下而上,從右到左的層次遍歷算法

​ 解:使用層次遍歷,每次出隊列時將該節點放入一個棧中,當層次遍歷結束時,將全部的棧中數據退棧。

3.非遞歸算法求二叉鏈表表示法的二叉樹高度。

​ 解:使用層次遍歷的算法,last指向當前層最右邊的結點,每次出隊都與last比較,若二者相等,則層數加1,並讓last指向下一層的最右結點。(注,本代碼不使用隊列的標準操做)

int Btdepth(BiTree T) {
    if(!T)
        return 0;	//判空
    int front=-1,rear=-1; 
    BiTree Q[Maxsize];//隊列初始化,注意隊列尾指針指向隊尾元素
    int last=0,level=0;
    Q[++rear]=T;	//根節點入隊
    BiTree p;
    while(front<rear)	//隊不空則循環
    {
        p=Q[++front];	//隊列元素出隊,即正在訪問的結點
        if(p->lchild)	//左孩子不空,則入隊
            Q[++rear]=p->lchild;
        if(p->rchild)	//右孩子不空,則入隊
            Q[++rear]=p->rchild;
        if(front==last)	//表明出隊列的結點爲最右元素
        {
            level++;	//層數++
            last=rear;	//將last指向下一層中最右元素
        }
    }
}

複製代碼

4.對於樹中每個元素值爲x的結點,刪去以它爲根的子樹,並釋放相應的空間。

​ 解:須要刪除該節點的父結點指向其的指針,同時遞歸的刪除該節點爲根的樹

​ 使用層次遍歷算法來找到父結點

void DeleteXTree(BiTree bt) //遞歸的刪除bt爲根的樹 {
    if(bt)
    {
        DeleteXTree(bt->lchild);
        DeleteXTree(bt->rchild);
        free(bt);
    }
}
void SearchAndDeleteX(BiTree bt,ElemType x) //刪除樹中全部節點值爲x的結點 {
    InitQueue(Q);	//藉助隊列
    if(bt)
    {
     	if(bt->data==x)	//根爲x則所有刪除
            DeleteXTree(bt);
        exit(0);
        EnQueue(bt);	//根節點入隊
        while(!IsEmpty(Q))	//隊不空則循環
        {
            DeQueue(Q,p);	//出隊
            if(p->lchild)	//左孩子非空
            {
                if(p->lchild->data==x)	//左孩子爲x
                {
                    DeleteXTree(p->lchild);	//遞歸刪除左孩子
                    p->lchild=NULL;	//左孩子指針置空
                }
                else
                    EnQueue(Q,p->lchild);	//不然左孩子入隊
            }
            //右孩子操做相同
            if(p->rchild)	//右孩子非空
            {
                if(p->rchild->data==x)	//右孩子爲x
                {
                    DeleteXTree(p->rchild);	//遞歸刪除右孩子
                    p->rchild=NULL;	//右孩子指針置空
                }
                else
                    EnQueue(Q,p->rchild);	//不然右孩子入隊
            }
        }
    }
}

複製代碼

5.求非空二叉樹的寬度(結點最多的一層的結點個數)

typedef struct{
    BiTree data[MaxSize];
    int level[MaxSize];
    int front,rear;
}Qu;
int BTWidth(BiTree b){
    BiTree p;
    int k,max,i,n;
    Qu.front=Qu.rear=-1;
    Qu.rear++;
    Qu.data[Qu.rear]=b;
    Qu.level[Qu.rear]=1;
    while(Qu.front<Qu.rear){
        Qu.front++;
        p=Qu.data[Qu.front];
        k=Qu.level[Qu.frnot];
        if(p->lchild!=NULL){
            Qu.rear++;
            Qu.data[Qu.rear]=p->lchild;
            Qu.level[Qu.rear]=k+1;
        }
        if(p->rchild!=NULL){
            Qu.rear++;
            QU.data[Qu.rear]=p->rchild;
            Qu.level[Qu.rear]=K+1;
        }
    }
    max=0,i=0;
    k=1;
    while(i<=Qu.rear){
        n=0;
        while(i<=Qu.rear&&Qu.level[i]==k){
            n++;
            k++;
        }
        k=Qu.level[i];
        if(n>max)
            max=n;
    }
    return max;
}

複製代碼

6.使用非遞歸算法交換二叉樹的左右子樹

​ 使用相似層次遍歷的方式,交換每一個結點的左右子樹

(1)使用隊列的方式

void exchange(B) {
    BiNode *p,*q;
    if(B)
    {
        EnQueue(Q,B);			//根結點入隊
        while(!QueueEmpty(Q))	//隊爲空則退出
        {
            DeQueue(Q,q);		//出隊一個
            if(p->lchild)  EnQueue(Q,p->lchild);	//如有左子樹,則入隊
            if(p->rchild)  EnQueue(Q,p->rchild);	//如有右子樹,則入隊
            q=p->rchild;			//交換左右子結點
            p=q->lchild;
            p->lchild=q;
        }
    }
    
}

複製代碼

(2)使用棧的方式

相似隊列方式,
			(1)根節點放入棧
			(2)當棧不空時,取出棧頂元素,交換它的左右子樹,並把它的左右子樹分別入棧
			(3)重複(2)直到隊列爲空

複製代碼

7.判別給定的二叉樹是不是徹底二叉樹的算法。

​ 解:使用層次遍歷的方式,藉助隊列,利用徹底二叉樹「若某結點無左子樹就不應有右子樹」的原則判斷。

具體的操做爲設置標誌位,用層次遍歷的方式,若層次遍歷中已經出現結點孩子指針爲空的結點,再次出現時則不爲徹底二叉樹。

int judge_complete_BT(B) {
    BiNode *p=B;
    int tag=0//用來標註前面的結點爲空的個數
    if(p==NULL) return 0;
    EnQueue(Q,p);	//根結點入隊
    while(!QueueEmpty(Q))	//隊爲空則退出
 	{
        DeQueue(Q,q);		//出隊一個
        if(p->lchild&&!tag)		//若前面沒有非空的結點而且左孩子存在
           EnQueue(Q,p->lchild);		//左孩子入隊
        else if(p->lchild)		//若前面已經有空結點
           return 0;			//返回0
        else
            tag=1;		
        //右孩子相同操做
         if(p->rchild&&!tag)	
           EnQueue(Q,p->rchild);		
        else if(p->rchild)		
           return 0;			
        else
            tag=1;		
     }
    return 1;
    
}

複製代碼

8.複製一個二叉樹的算法

BiTree Copy(BiTree B) //使用兩個隊列 {
    BiTree T;
    BiNode *p=B;
    //兩個頭節點入隊列
    EnQueue(Q1,p);
    EnQueue(Q2,T);
    while(!QueueEmpty(Q1)&&!QueueEmpty(Q2))
    {	
        //出隊列
        p=DeQueue(Q1);
        T=DeQueue(Q2);
        T=new(BiNode);
        T->data=p->data;
        //複製左孩子
        if(p->lchild)
        {
            EnQueue(Q1,p->lchild);
            EnQueue(Q2,T->lchild);
        }
        else
            T->lchild=NULL;
        //複製右孩子
        if(p->rchild)
        {
            EnQueue(Q1,p->rchild);
            EnQueue(Q2,T->rchild);
        }
        else
            T->rchild=NULL;     
    }
}

複製代碼

9.二叉鏈表指定某一層k上的葉子節點的個數。

​ 解:採用層次遍歷的方式

BiTree Count(BiTree bt){
    BiTree p=bt,Q[];
    int front=0,rear=1,leaf=0;
    int last=1,level=1;Q[1]=p;
    while(front<=rear){
        p=Q[++front];
        if(level==k&&!p->lchild&&!p->rchild)
            leaf++;
        if(p->lchild)
            Q[++rear]=p->lchild;
        if(p->rchild)
            Q[++rear]=p->rchild;
        if(front==last){
            level++;last=rear;
        }
        if(level>k) return (leaf);
    }
    return 0:
}

複製代碼

3.1.2 順序結構

遞歸算法

1.已知一棵二叉樹按順序存儲結構進行存儲,設計一個算法,求編號分別爲 i 和 j 的兩個結點的最近的公共祖先結點的值。

int Anchester(int i,int j) {
    while(i!=j)
       (i>j?i:j)/=2;
	return T[i];
}

複製代碼

2.已知一棵高度爲K具備n個結點的二叉樹,按順序方式存儲:

(1)編寫用先根遍歷樹中每一個結點的遞歸算法;

(2)編寫將樹中最大序號葉子結點的祖先結點所有打印輸出的算法。

int m=2^K–1;  //全局變量
void PreOrder(ElemType bt[],i ) //遞歸遍歷以順序方式存儲的二叉樹bt, i是根結點下標(初始調用時爲1)。 {
    if (i<=m)       //設虛結點以0表示
    {
        printf(bt[i]);  //訪問根結點
        if(2*i<=m && bt[2*i]!=0)
            PreOrder(bt,2*i);      //先序遍歷左子樹
        if(2*i+1<=m && bt[2*i+1]!=0)
            PreOrder(bt,2*i+1);// 先序遍歷右子樹
    }  
}//結束PreOrder
//二叉樹中最大序號的葉子結點,是在順序存儲方式下編號最大的結點 
void Ancesstor(ElemType bt[]) //打印最大序號葉子結點的所有祖先 {
    c=m;
    while(bt[c]==0)
        c--;       //找最大序號葉子結點,該結點存儲時在最後
    f=c/2;       //c的雙親結點f
    while(f!=0)  //從結點c的雙親結點直到根結點,路徑上全部結點均爲祖先結點
    {
        printf(bt[f]);
        f=f/2;
    }//逆序輸出,最老的祖先最後輸出
} 

複製代碼
非遞歸算法

1.設一棵徹底二叉樹使用順序存儲在數組bt[1..n]中,請寫出進行非遞歸的前序遍歷算法。

void PreOrder(ElemType bt,int n)//對以順序結構存儲的徹底二叉樹bt進行前序遍歷 {
    int top=0,s[];  //top是棧s的棧頂指針,棧容量足夠大
    while(i<=n||top>0)
    {
        while(i<=n)
        {
            printf(bt[i]);                   //訪問根結點;
            if(2*i+1<=n)
                s[++top]=2*i+1;    //右子女的下標位置進棧
            i=2*i; 
        }                         //沿左子女向下
        if(top>0)
            i=s[top--];
    }           //取出棧頂元素
}

複製代碼

2.已知深度爲h的二叉樹以一維數組BT(1:2h-1)做爲其存儲結構。請寫一算法,求該二叉樹中葉結點的個數.

int Leaves(int h) //求深度爲h以順序結構存儲的二叉樹的葉子結點數 {
    int BT[];
    int len=2h-1,count=0; //BT是二叉樹結點值一維數組,容量爲2h 
    for (i=1;i<=len;i++) //數組元素從下標1開始存放
        if (BT[i]!=0)      //假定二叉樹結點值是整數,「虛結點」用0填充
            if(i*2)>len) count++;         //第i個結點沒子女,確定是葉子
    		else if(BT[2*i]==0 && 2*i+1<=len && BT[2*i+1]==0)
                    count++; //無左右子女的結點是葉子
    return (count)
} 

複製代碼
徹底二叉樹

1.一顆徹底二叉樹

​ (1)由順序存儲結構轉換爲鏈式存儲結構

​ (2)由鏈式存儲結構轉換爲順序存儲結構

​ 解(1)

BiTree Creat(ElemType A[],int i,int n) //n爲數組的長度,i爲結點位置,初始爲1 {
    BiTree tree;
    if(i<=n)
    {
        tree=new(BiNode);
        tree->data=A[i];
        //創建左子樹
        if(2*i>n)	//若左子節點爲空
            tree->lchild=NULL;
        else		//不爲空則遞歸的創建左子樹
            tree->lchild=Creat(A,2*i,n);
        
        //創建右子樹
        if(2*i+1>n)	//若右子節點爲空
            tree->rchild=NULL;
        else		//不爲空則遞歸的創建右子樹
            tree->rchild=Creat(A,2*i+1,n);   
    }
    return tree;
}

複製代碼

​ 解:(2)採用遞歸方式

void Creat(int A[],int i,BiTree B) //A存放轉換後的順序存儲結構,i爲指定的位置,初始爲1 {
    if(B)
    {
        A[i]=B->data;
        //若是左孩子結點存在,則遞歸的創建左孩子
        if(B->lchild)
            Creat(A,2*i,B->lchild);
        //若是右孩子結點存在,則遞歸的創建右孩子
        if(B->rchild)
            Creat(A,2*i+1,B->rchild);
    }
}

複製代碼

​ 同時可採用非遞歸方式,使用層次遍歷的方式

void Creat(bt,A) //使用兩個隊列,隊列Q1用來存放節點,隊列Q2用來存放下標 {	
    EnQueue(Q1,bt);
    EnQueue(Q2,1);
    while(!QueueEmpty(Q1)&&!QueueEmpty(Q2))
    {
        p=DeQueue(Q1);
        i=DeQueue(Q2);
        A[i]=p->data;
        //若是存在左孩子,則將左孩子的結點和左孩子的下標存入隊列中
        if(p->lchild)
        {
            EnQueue(Q1,p->lchild);
            EnQueue(Q2,i*2);
        }
        //若是存在右孩子,則將右孩子的結點和右孩子的下標存入隊列中
        if(p->rchild)
        {
            EnQueue(Q1,p->rchild);
            EnQueue(Q2,i*2+1);
        }
    }
}

複製代碼

3.1.3 表達式樹

1.假設一個僅包含二元運算符的算術表達式以鏈表形式存儲在二叉樹BT中,寫出計算該算術表達式值的算法。

typedef struct node {ElemType  data;  
 float val;
 char optr;  //只取‘+’, ‘-’, ‘*’,‘/’
}
struct node *lchild,*rchild }BiNode,*BiTree;
float PostEval(BiTree bt) // 之後序遍歷算法求以二叉樹表示的算術表達式的值 {
    float lv,rv;
    if(bt!=null)
    {
        lv=PostEval(bt->lchild); // 求左子樹表示的子表達式的值
        rv=PostEval(bt->rchild); // 求右子樹表示的子表達式的值
        switch(bt->optr)
        {
            case ‘+’: value=lv+rv; break;
            case  ‘-’: value=lv-rv;break;
            case  ‘*’: value=lv*rv;break;
            case  ‘/’: value=lv/rv;
        }
    }
    return(value);
}

複製代碼

2.給出算法將二叉樹表示的表達式二叉樹按中綴表達式輸出,並加上相應的括號。

int Precede(char optr1,optr2) // 比較運算符級別高低,optr1級別高於optr2時返回1,相等時返回0,低於時返回-1 {
    switch(optr1)
   {
        case‘+’:case‘-’:
            if(optr2==‘+’||optr2==‘-’)return(0);
            else return(-1);
        case‘*’:case‘/’:
            if(optr1==‘*’||optr2==‘/’)return(0);
            else return(1);
    }  
} 
void InorderExp (BiTree bt) //輸出二叉樹表示的算術表達式,設二叉樹的數據域是運算符或變量名 {
    int bracket;
    if(bt)
    {
        if(bt->lchild!=null)
        {
            bracket=Precede(bt->data,bt->lchild->data)//比較雙親與左子女運算符優先 
            if(bracket==1) 
                printf(‘(’);
            InorderExp(bt->lchild);        //輸出左子女表示的算術表達式
			if(bracket==1)
                printf(‘)’);  //加上右括號
        }
		printf(bt->data);               //輸出根結點
		if(bt->rchild!=null)            //輸出右子樹表示的算術表達式
		{
            bracket=Precede(bt->data,bt->rchild->data)
			if (bracket==1)
                printf(「(」);  //右子女級別低,加括號
			InorderExp (bt->rchild);
            if(bracket==1)printf(「)」);
        }
    }
}

複製代碼

3.試運用後序遍歷二叉樹的規則,寫出對錶達式求值的算法

char ar[maxsize];//maxsize是後綴表達式所能達到的最大長度
int i=1;
void PostOrder(BiTree t )//後序遍歷二叉樹t,以獲得後綴表達式 {
    if(t)
    {
        PostOrder(t->lchild);
        PostOrder(b->rchild);
        ar[i++]=b->data; 
    }
}//結束PostOrder
void EXPVALUE() //對二叉樹表示的算術表達式,進行後綴表達式的求值 {
    ar[i]=‘\0’;      //給後綴表達式加上結束標記
    char value[];      //存放操做數及部分運算結果
    i=1; ch=ar[i++];
    while(ch!=‘\0’)
    {
        switch(ch)
        {
            case ch in op:
                opnd1=pop(value);opnd2=pop(value); //處理運算符
                push(operate(opnd2,ch,opnd1));break;
			default:      
                push(value,ch);                    //處理操做數,壓入棧中
        }
        ch=ar[i++];                  //讀入後綴表達式
    } 
    printf(value[1]);            //棧中只剩下一個操做數,即運算結束。
} //結束EXPVALUE

複製代碼

3.1.4 二叉樹遍歷雜項

1.利用葉子節點中的空指針域將全部葉子節點連接爲一個帶頭結點的雙鏈表。(1800算法57)

2.將二叉樹的葉結點按從左到右的順序連成一個單鏈表。

LinkedList head,pre=null;
LinkedList InOrder(BiTree bt){
    if(bt){
        InOrder(bt->lchild);
        if(bt->lchild==NULL&&bt->rchild==NULL)
            if(pre==NULL){
                head=bt;
                pre=bt;
            }
        else{
            pre->rchild=bt;
            pre=bt;
        }
        InOrder(bt->rchild);
        pre->rchild=NULL;
    }
    return head;
}

複製代碼

4.全部分支節點度數爲2的二叉樹稱爲正則樹,寫一個算法判斷一顆樹是不是正則樹。(遞歸)

​ 解:採用任何遞歸遍歷算法,將訪問節點的操做改成判斷度是否爲1,爲1則非二叉樹。

3.2 線索二叉樹

1。線索樹的插入問題(1800 算法75,76,77)

9.構造前序線索二叉樹

void preThread(BiTree p,BiTree &pre) {
    if(p!=NULL)
    {
        if(p->lchild==NULL)
        {
            p->lchild = pre;
            p->ltag = 1;
        }
        if(pre!=NULL && pre->rchild==NULL)
        {
            pre->rchild = p;
            pre->rtag = 1;
        }
        pre = p;
        if(p->ltag!=1)
            preThread(p->lchild,pre);
        if(p->rtag!=1)
            preThread(p->rchild,pre);
    }
}

複製代碼

10.構造中序線索二叉樹

void inThread(BiTree p,Bitree &pre) {
    if(p!=NULL)
    {
        inThread(p->lchild,pre);
        if(p->lchild==NULL)     //構造左線索
        {
            p->lchild = pre;
            p->ltag = 1;
        }
        if(pre!=NULL&&pre->rtag==0) //構造右線索
        {
            pre->rchild = p;
            pre->rtag = 1;
        }
        pre = p;
        inThread(p->rchild);
    }
}

複製代碼

11.構造後序線索二叉樹

void postThread(BiTree p,Bitree &pre) {
    if(p!=NULL)
    {
      	postThread(p->lchild,pre);
        postThread(p->rchild,pre);
        if(p->lchild==NULL)              //構造左線索
        {
            p->lchild = pre;
            p->ltag = 1;
        }
        if(pre!=NULL && pre->rtag==0)     //構造右線索
        {
            pre->rchild = p;
            pre->rtag = 1;
        }
        pre = p;
    }
    
}
複製代碼

12.前序線索二叉樹的遍歷

void preOrder(BiTree root) {
    if(root!=NULL)
    {
        BiTree p = root;
        while(p!=NULL)
        {
            while(p->ltag==0)   //遇到左孩子不存在的節點則中止
            {
                visit(p);
                p = p->lchild;
            }
            visit(p);         //訪問這個左孩子不存在的節點
            p = p->rchild;    //轉向它的後繼 繼續訪問
        }
    }
}
複製代碼

13.中序線索二叉樹的遍歷

​ 不帶頭結點

ThreadNode *Firstnode(ThreadNode *p) //中序線索二叉樹中中序序列下的第一個結點 {
    while(p->ltag==0)
        p=p->lchild;	//最左下結點
    return p;
}
ThreadNode *Nextnode(ThreadNode *p) //後繼結點 {
    if(p->rtag==0)		//無線索,則遍歷右子樹
        return Firstnode(p->rchild);
    else				//有線索,則返回線索
        return p->rchild;
}
void Inorder(ThreadNode *T) //遍歷中序線索二叉樹 {
    for(ThreadNode *p=Firstnode(T);p!=NULL;p=Nextnode(p))
        visit(p);
}

複製代碼
void Inorder(ThreadNode *T) {
    ThreadNode *p=T;	//指向正式序列的第一個值
    while(p)		//若最後指向NULL則退出
    {
        while(p->ltag==0)	//訪問中序下第一個結點
        	p=p->lchild;
        visit(p);
        while(p->rtag==1)	//有線索則訪問下一個,注意最後一個右孩子爲NULL
        {
            p=p->rchild;
            visit(p);
        }
        p=p->rchild;		//走到無線索,向右走
    }
}	

複製代碼

帶頭結點

void Inorder(ThreadNode *T) {
    ThreadNode *p=T->lchild;	//指向正式序列的第一個值
    while(p!=T)		//若最後指向頭結點則退出
    {
        while(p->ltag==0)	//訪問中序下第一個結點
        	p=p->lchild;
        visit(p);
        while(p->rtag==1&&p->rchild!=T)	//有線索則訪問下一個,注意最後一個線索指向頭結點
        {
            p=p->rchild;
            visit(p);
        }
        p=p->rchild;		//走到無線索,向右走
    }
}

複製代碼

14.後序線索二叉樹的遍歷

15.寫出在​中序線索二叉樹裏查找指定結點在後序的前驅結點的算法。

BiTree findNode(BiTree T,BiTree bt) {
    if(bt->rtag==0)                          //存在右子樹時
        return bt->rchild;
    else if(bt->ltag==0)                     //不存在右子樹 但有左子樹
        return bt->lchild;
    else if(bt->lchild==NULL)                //爲中序第一節點 因此無後序前驅結點
        return NULL;
    else                                     //其餘狀況
    {
        while(bt->ltag&&bt->lchild!=NULL)
            bt = bt->lchild;
        if(bt->ltag==0)
            return bt->lchild;
        else                                  //此種狀況爲單枝樹,bt爲葉子,因此無後序前驅結點
            return NULL;                   
    }
}

複製代碼

​ 中序線索二叉樹裏查找指定結點在前序下的後繼結點的算法

BiTree treNext(BiTree t) {
    BiTree p;
    if(!t->ltag)
        p = t->lchild;
    else if(!t->rtag)
        p = t->rchild;
    else
    {
        p = t;
        while(p&&p->rtag)
            p = p->rchild;
        if(p)
            p = p->rchild;
    }
    return p;
}

複製代碼

​ 中序線索二叉樹裏中序下的最後一個節點

BiTree inLast(BiTree t) {
    BiTree p = t;
    while(p && !p->rtag)
    	p = p->rchild;
    return p;
}

複製代碼

​ 中序下的前驅

BiTree inPrior(BiTree t) {
    BiTree p=t->lchild;
    while(p&&!p->rtag)
        p = p->rtag;
    return p;
}

複製代碼

前序線索二叉樹的前驅後繼

中序線索二叉樹的前驅後繼

後序線索二叉樹的給定結點的前驅後繼,給定結點node,前驅或者後繼儲存到x

void Post_prior(ThreadNode *node,ThreadNode *x) //後序線索樹前繼結點 {
    if(node)	//判斷空
        if(node->rflag==0)	//若是有右孩子,則右孩子必定爲前驅
            x=node->rchild;
        else
            x=node->left;	//左孩子若存在,則沒右孩子的狀況下返回左孩子,左孩子若不存在則返回前驅線索
}
void Post_next(ThreadNode *bt,ThreadNode *node,ThreadNode *x) //bt爲根,後序線索樹後繼結點 {
    x=bt;	//賦值x爲根
    if(node!=bt&&node!=NULL)
    {
        if(node->rflag)			//如有後序線索,則獲得後序線索
            x=node->rchild;
        else{					
            //沒有後序線索的狀況下
            do{
                t=x;	//從根節點(後序序列最後一個結點)
                Post_prior(t,x);	//逐漸找前驅
            }while(x!=node);	//若該節點的前驅爲node
            x=t;		//該節點爲後繼
        }
    }
}

複製代碼

3.2 樹

1.已知一棵樹的層次序列以及每一個節點的度,編寫算法構造樹的孩子-兄弟鏈表。

#define maxsize 15
void createCSTree_Degree(CSTree &T,DataType e[],int degree[],int n) {
    CSNode *pointer[maxsize];
    int i,j,d,k=0;
    for(i=0;i<n;i++)
    {
        pointer[i] = (CSTree)malloc(sizeof(CSNode));
        pointer[i]->data = e[i];
        pointer[i]->lchild = pointer[i]->rsilbling = NULL;
    }
    for(i=0;i<n;i++)
    {
        d = degree[i];
        if(d)
        {
            k++;
            pointer[i]->lchild = pointer[k];
            for(j=2;j<=d;j++)
            	pointer[k]->lchild = pointer[k++];
        }
    }
    T = pointer[0];
    free(pointer);
}

複製代碼

2.以孩子兄弟鏈表爲存儲結構,請設計遞歸和非遞歸的算法求樹的深度。(1800,算法15)

由孩子兄弟鏈表表示的樹高度的遞歸模型是:若樹爲空,高度爲零;若第一個子女爲空,高度爲1和兄弟子樹高度的大着;不然,高度爲第一子女樹高度加1和兄弟子樹高度的大者。遞歸算法的核心算法以下:

if(bt==null) return(0);
else if(!bt->firstchild)
    return(max(1,height(bt->nextsibling)));
else{
    hc=height(bt->firstchild);   //第一子女樹高
    hs=height(bt->nextsibling);  //兄弟樹高
    if(hc+1>hs)
        return(hc+1);
    esle return(hs);
}

複製代碼

其非遞歸算法使用隊列、逐層遍歷樹,取得樹的高度。

Q[rear]=t;                      //Q是以樹中結點爲元素的隊列
while(ftont<=last){             //初始front=rear=1
    t=Q[front++];               //隊頭出列
    while(t!=null)              //層次遍歷
    {
        if(t->firstchild)
            Q[++rear]=t->firstchild; //第一個子女入隊
        t=t->nextsibling;            //同層兄弟指針後移
    }
    if(front>last){
        h++;
        last=rear;
    }
}

複製代碼

3.編寫遞歸程序求以孩子兄弟鏈表結構表示的樹的葉子節點個數。

int count(CSTree){
    if(t==null)
        return(0);
    else if(t->firstchild==null)
        return(1+count(t->nextsibling));
    else return(count(t->firstchild)+count(t->nextsibling));
}

複製代碼

4.編寫程序計算以孩子兄弟鏈表表示的樹T的度。(1800算法73)

3.3 哈夫曼樹

1.求出二叉樹的帶權路徑長度(WPL)

static int wpl = 0; //全局變量來保存wpl
int Pre_Order(BiTree root,int deep) {
    if(root->lchild==NULL && root->rchild==NULL)         //爲葉節點的時候則累加
    	wpl+=deep*root->weight;
    if(root->lchild!=NULL)                               //左子樹不空 則對左子樹遞歸
        Pre_Order(root->lchild,deep+1);
    if(root->rchild!=NULL)
        Pre_Order(root->rchild,deep+1);                  //右子樹不空 則對右子樹遞歸
    return wpl;
}

int wpl(BiTree root) {
    Pre_Order(root,0);
    return wpl;
}
複製代碼
相關文章
相關標籤/搜索