1. 程式人生 > >C語言實現二叉樹的插入和刪除

C語言實現二叉樹的插入和刪除

二叉樹的插入刪除:
//首先介紹二叉樹的插入:
    //首先需要明白插入的規則:每個建好的結點p都需要從跟結點開始與根結點相比較資料域,如果根結點的資料域小於結點p,則接著將結點p與根結點的右子樹相比較,否則p將與根結點的左子樹相比較;
    //繼續往下類推,一直到最後一次比較完後,指標head的左子樹或者右子樹為空,退出迴圈(也就是,當到達葉子結點時),因為每次進入迴圈都要把head結點賦給parent雙親結點,所以這個結點也表示雙親結點;
    //之後將雙親結點parent與p->data(也就是key的值)比較,如果雙親結點大,那麼,p為雙親結點的左子樹,否則為雙親結點的右子樹;


//介紹二叉樹的刪除:
    //二叉樹的刪除有三種情況:
        //1>刪除的結點為葉子結點;
            //刪除節點是葉節點,即沒有子節點,或者說左右子節點都是NULL。這種情況下,只需要把刪除節點的父節點中對應的指標指向NULL即可。然後釋放掉刪除節點的空間;
        //2>刪除的結點有左子樹或者右子樹,只能有一個;
            //刪除節點有一個子節點(左子節點或右子節點),這種情況下,把刪除節點的父節點中對應的指標指向刪除節點的子節點即可。然後釋放掉刪除節點的空間;    
        //3>刪除的結點左右子樹都有,兩個都有;
            //刪除節點有兩個子節點,這種情況下,必須要找到一個替代刪除節點的替代節點,並且保證二叉樹的排序性。根據二叉樹的排序性,可知替代節點的鍵值必須最接近刪除節點鍵值。比刪除節點鍵值小的所有鍵值中最大那個,或者是比刪除節點鍵值大的所有鍵值中最小的那個,是符合要求的。這兩個鍵值所在的節點分別在刪除節點的左子樹中最右邊的節點,刪除節點右子樹中最左邊的節點;

    //以圖例的形式表示第三種方式的刪除;

        第三種方式的刪除:最重要的是找到刪除結點以及它的父結點;

                1>找到刪除節點以及它的父節點在刪除節點的左子樹中,向下向右遍歷,找到替代節點以及它的父節點;
                2>刪除節點的父節點中對應的指標指向替代節點;
                3>替代節點中的右子節點指標指向刪除節點的右子樹;
                4>如果替代節點的父節點不是刪除節點,則將替代節點的左子節點指標指向刪除節點的左子樹,並且替代節點的父節
                    點中對應的指標指向替代節點的左子節點;
                5>

釋放刪除節點的空間;
        注意:
                1>第二步中找到的替代節點,可能會有左子樹,但一定沒有右子樹。
                2>第五步要判斷替代節點的父節點不是刪除節點後,才將替代節點的左子節點指標指向刪除節點的左子樹,否則
                    會出現替代節點左子節點指標指向自己的情況,從而丟失替代節點的左子樹。
        第三種方式的圖畫顯示:兩種方式;


#include<stdio.h>
#include<stdlib.h>
#define N 9
int a[]={3,2,5,8,4,7,6,9,10};

//二叉樹的結點型別;
typedef struct tree
{
	int data;
	struct tree *lchild;
	struct tree *rchild;
}BitTree;

//在二叉排序樹中插入查詢關鍵字可以;
void Inserter(BitTree *bt,int key)  
{
	BitTree *parent;   //表示雙親結點;
	BitTree *head = bt;
	BitTree *p=(BitTree *)malloc(sizeof(BitTree));
	p->data=key;   //儲存結點資料;
	p->lchild=p->rchild=NULL;  //左右子樹置空;
	
	//查詢需要新增的父結點,這個父結點是度為0的結點;
	while(head) 
	{
		parent=head;
		if(key<head->data)   //若關鍵字小於結點的資料;
			head=head->lchild; //在左子樹上查詢; 
		else   //若關鍵字大於結點的資料;
			head=head->rchild;  //在右子樹上查詢;
	}
	//判斷新增到左子樹還是右子樹;
	if(key<parent->data)   //小於父結點;
		parent->lchild=p;    //新增到左子樹;
	else    //大於父結點;
		parent->rchild=p;   //新增到右子樹;
}

//n個數據在陣列data[]中;
BitTree *Createer(BitTree *bt,int data[],int n)  
{
	bt=(BitTree *)malloc(sizeof(BitTree));
	bt->data=data[0];
	bt->lchild=bt->rchild=NULL;
	for(int i=1;i<n;i++)
		Inserter(bt,data[i]);
	return bt;
}

//中序遍歷;
void PreOrder(BitTree *bt)
{
	if(bt)
	{
		PreOrder(bt->lchild);
		printf("%d ",bt->data);
		PreOrder(bt->rchild);
	}
}

//刪除結點;
void Deleteer(BitTree *bt,int key)
{
	BitTree *L,*LL;    //在刪除左右子樹都有的結點時使用;
	BitTree *p=bt;
	BitTree *parent=bt;
	int child=0;  //0表示左子樹,1表示右子樹;
	if(!bt)    //如果排序樹為空,則退出;
		return ;
	while(p)  //二叉排序樹有效;
	{
		if(p->data==key)
		{
			if(!p->lchild&&!p->rchild)  //葉結點(左右子樹都為空);
			{
				if(p==bt)  //被刪除的結點只有根結點;
					free(p);
				else if(child==0)
				{
					parent->lchild=NULL;  //設定父結點左子樹為空;
					free(p);   //釋放結點空間;
				}
				else   //父結點為右子樹;
				{
					parent->rchild=NULL;  //設定父結點右子樹為空;
					free(p);  //釋放結點空間;
				}
			}

			else if(!p->lchild)  //左子樹為空,右子樹不為空;
			{
				if(child==0)    //是父結點的左子樹;
					parent->lchild=p->rchild;
				else      //是父結點的右子樹;
					parent->rchild=p->rchild;
				free(p);  //釋放被刪除的結點;
			}

			else if(!p->rchild)  //右子樹為空,左子樹不為空;
			{
				if(child==0)  //是父結點的左子樹;
					parent->lchild=p->lchild;
				else      //是父結點的右子樹;
					parent->rchild=p->lchild;
				free(p);  //釋放被刪除的結點;
			}

			else
			{
				LL=p;  //儲存左子樹的結點;
				L=p->rchild;  //從當前結點的右子樹進行查詢;
				if(L->lchild)  //左子樹不為空;
				{
					LL=L;
					L=L->lchild;   //查詢左子樹;
					p->data=L->data;  //將左子樹的資料儲存到被刪除結點;
					LL->lchild=L->lchild;  //設定父結點的左子樹指標為空;
					for(;L->lchild;L=L->lchild);
					L->lchild=p->lchild;
					p->lchild=NULL;
				}
				else
				{
					p->data=L->data;
					LL->rchild=L->rchild;
				}
			}
			p=NULL;
		}

		else if(key<p->data)  //需刪除記錄的關鍵字小於結點的資料;
		{
			//要刪除的結點p是parent的左子樹;
			child=0;  //標記在當前結點左子樹;
			parent=p;//儲存當前結點作為父結點;
			p=p->lchild;  //查詢左子樹;
		}

		else  //需刪除記錄的關鍵字大於結點的資料;
		{
			//要刪除的結點p是parent的右子樹;
			child=1;  //標記在當前結點右子樹查詢;
			parent=p;  //儲存當前結點作為父結點;
			p=p->rchild;  //查詢右子樹;
		}
	}
}

int main(void)
{
	BitTree *bt;  //儲存二叉排序樹根結點;
	printf("陣列資料為:\n");
	for(int i=0;i<N;i++)
		printf("%d ",a[i]);
	printf("\n\n");

	bt=Createer(bt,a,N);
	printf("遍歷後的二叉排序樹為(中序遍歷輸出):\n");
	PreOrder(bt);
	printf("\n\n\n");

	printf("     **將資料8插入到二叉樹中**\n\n");
	printf("插入後的二叉樹為(中序遍歷輸出):\n");
	Inserter(bt,8);
	PreOrder(bt);
	printf("\n\n\n");
	
	printf("     **將資料5從二叉樹中刪除**\n\n");
	printf("刪除後的二叉樹為(中序遍歷輸出):\n");
	Deleteer(bt,5);   //刪除擁有左右子樹的結點有問題;
	PreOrder(bt);
	printf("\n");
	return 0;
}

//輸出結果截圖: