C語言實現二叉樹的插入和刪除
阿新 • • 發佈:2018-12-31
二叉樹的插入刪除:
釋放刪除節點的空間;
注意:
1>第二步中找到的替代節點,可能會有左子樹,但一定沒有右子樹。
2>第五步要判斷替代節點的父節點不是刪除節點後,才將替代節點的左子節點指標指向刪除節點的左子樹,否則
會出現替代節點左子節點指標指向自己的情況,從而丟失替代節點的左子樹。
第三種方式的圖畫顯示:兩種方式;
//首先介紹二叉樹的插入:
//首先需要明白插入的規則:每個建好的結點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; }
//輸出結果截圖: