平衡二叉查找树的构建
平衡二叉查找樹的構建
筆者最近在數據結構的學習過程中,覺得有必要將重要的內容記錄下來,供自己以后復習使用。以下內容若有錯誤之處還請指出。
1. 概念
二叉樹中結點的平衡因子Balance Factor定義為該結點的左子樹深度減去右子樹深度,表示為
平衡二叉樹可以是一棵空樹,或者是滿足以下性質的二叉樹:
abs(BF(A)) <= 1;
左子樹和右子樹都是一顆平衡二叉樹,滿足遞歸的定義。
平衡二叉查找樹則是二叉查找樹與平衡二叉樹相結合,也簡稱為平衡二叉樹。
2. 具體操作
平衡二叉樹的基本操作類似于二叉查找樹的基本操作,但平衡二叉樹在進行插入或刪除操作后需要保持平衡二叉樹的平衡性,即需要進行平衡化旋轉。
一般情況下,假設由于在平衡二叉樹上插入結點而失去平衡的最小子樹的根結點的指針為A,即A是離插入結點位置最近,且平衡因子絕對值超過1的祖先結點,則此時在A上發生的失衡有四種情況:
1. LL型(左子樹左高)
2.??RR型(右子樹右高)
3.??LR型(左子樹右高)
4.??RL型(右子樹左高)
其中LR型旋轉和RL型旋轉是對稱的
因此,在平衡二叉樹插入刪除操作中,與二叉查找樹不同之處在于,我們需要在每次插入或刪除元素后,若失去平衡,則對樹進行相應的平衡化旋轉即可
3. 代碼部分
以下部分代碼未進行優化,如果有錯誤之處還請指出。
樹結點的定義
struct BSTNode {int data;struct BSTNode *pLchild;struct BSTNode *pRchild;struct BSTNode *pParent; };三種遍歷的遞歸實現
void displayData(struct BSTNode *T) {printf("%d\n",T->data); }void PreOrderTraverse(struct BSTNode *T) {if(T){displayData(T);PreOrderTraverse(T->pLchild);PreOrderTraverse(T->pRchild);} }void MidOrderTraverse(struct BSTNode *T) {if(T){MidOrderTraverse(T->pLchild);displayData(T);MidOrderTraverse(T->pRchild);} }void PostOrderTraverse(struct BSTNode *T) {if(T){PostOrderTraverse(T->pLchild);PostOrderTraverse(T->pRchild);displayData(T);} }搜索操作
struct BSTNode* SearchBSTree(struct BSTNode* pBST, int keynum) //查找關鍵值,若查找成功則返回該節點的地址 {if(pBST == NULL)return NULL;else if(keynum < pBST->data)return SearchBSTree(pBST->pLchild,keynum);else if(keynum > pBST->data)return SearchBSTree(pBST->pRchild,keynum);elsereturn pBST; }刪除操作 為二叉查找樹的刪除 平衡二叉樹的刪除還需進行平衡化旋轉 因為作業中沒有用到所以暫時沒有修改這部分函數內容
void DeleteBSTree(struct BSTNode *pBST,int DeleteVal) //根據關鍵值刪除二叉樹中的結點 {struct BSTNode *pTem = SearchBSTree(pBST,DeleteVal);if(pTem == NULL){printf("沒有找到要刪除的元素!\n");exit(-1);}if(NULL == pTem ->pLchild && NULL == pTem ->pRchild) //刪除節點無左右孩子{if(pTem == pTem ->pParent ->pLchild) //刪除節點是其父節點的左孩子{pTem ->pParent ->pLchild = NULL;}else //刪除節點是其父節點的右孩子{pTem ->pParent ->pRchild =NULL;}free(pTem);pTem = NULL;}else if(NULL == pTem ->pLchild) //刪除節點無左孩子{if(pTem == pTem ->pParent ->pLchild) //刪除節點為其父節點的左孩子{pTem ->pParent ->pLchild = pTem ->pRchild;pTem ->pRchild ->pParent =pTem ->pParent;}else //刪除節點為其父節點的右孩子{pTem ->pParent ->pRchild = pTem ->pRchild;pTem ->pRchild ->pParent =pTem ->pParent;}free(pTem);pTem = NULL;}else if(NULL == pTem ->pRchild) //刪除節點無右孩子{if(pTem == pTem ->pParent->pLchild) //刪除節點為其父節點的左孩子{pTem ->pParent ->pLchild = pTem ->pLchild;pTem ->pLchild ->pParent = pTem ->pParent;}else //刪除節點為其父節點的右孩子{pTem ->pParent ->pRchild = pTem ->pLchild;pTem ->pLchild ->pParent = pTem ->pParent;}free(pTem);pTem = NULL;}else //刪除節點有左右孩子,用刪除節點左字樹中的最大值或者右子樹中最小值來代替刪除節點//本例中用右子樹中最小值來代替{struct BSTNode *pTial = pTem ->pRchild;while(NULL != pTial ->pLchild) //查找右子樹中的最小值{pTial = pTial ->pLchild;}printf("\n\t右子樹中值最小的節點為%d\t\n",pTial->data);if(pTem == pTial ->pParent) //右子樹中的最小值的父節點是刪除節點{if(pTem == pTem ->pParent ->pLchild) //刪除節點為其父節點的左孩子{pTem ->pParent ->pLchild = pTial;pTial ->pLchild = pTem ->pLchild;pTem ->pLchild->pParent = pTial;}else{pTem ->pParent ->pRchild = pTial;pTial ->pLchild = pTem ->pLchild;pTem ->pLchild->pParent = pTial;}free(pTem);pTem = NULL;}else //右子樹中的最小結點的父結點不是刪除節點{if(pTial->pRchild){pTial ->pRchild ->pParent = pTial ->pParent;pTial ->pParent ->pLchild = pTial ->pRchild;}pTem->data = pTial->data;pTial->pParent->pLchild = NULL;free(pTial);pTial = NULL;}} }獲取結點深度 獲取結點平衡因子
int getNodeDepth(struct BSTNode *T) {if(T==NULL){return 0;}int lenleft=getNodeDepth(T->pLchild)+1;int lenright=getNodeDepth(T->pRchild)+1;if(lenleft>lenright){return lenleft;}else{return lenright;} }int getBalanceFactor(struct BSTNode *T) //獲取結點平衡因子(左子樹深度 - 右子樹深度) {return getNodeDepth(T->pLchild) - getNodeDepth(T->pRchild); }四種旋轉的實現 此處代碼寫得比較爛 還需要進一步優化 經測試功能正常
struct BSTNode * left_left_rotation(struct BSTNode *T) //LL型旋轉 兩種情況分別為旋轉的結點 為根節點/不為根節點 {struct BSTNode *pTeml = T->pLchild;if(T->pParent){T->pLchild->pParent = T->pParent;if(T == T->pParent->pLchild)T->pParent->pLchild = T->pLchild;elseT->pParent->pRchild = T->pLchild;if(pTeml->pRchild==NULL)T->pLchild = NULL;else{T->pLchild = pTeml->pRchild;pTeml->pRchild->pParent = T;}pTeml->pRchild = T;T->pParent = pTeml;}else{T->pParent = pTeml;T->pLchild = pTeml->pRchild;pTeml->pRchild = T;pTeml->pParent = NULL;}return pTeml; }struct BSTNode * right_right_rotation(struct BSTNode *T) //RR型旋轉 兩種情況分別為旋轉的結點 為根節點/不為根節點 {struct BSTNode *pTemr = T->pRchild;if(T->pParent){T->pRchild->pParent = T->pParent;if(T == T->pParent->pLchild)T->pParent->pLchild = T->pRchild;elseT->pParent->pRchild = T->pRchild;if(pTemr->pLchild==NULL)T->pRchild = NULL;else{T->pRchild = pTemr->pLchild;pTemr->pLchild->pParent = T;}pTemr->pLchild = T;T->pParent = pTemr;}else{T->pParent = pTemr;T->pRchild = pTemr->pLchild;pTemr->pLchild = T;pTemr->pParent = NULL;}return pTemr; }struct BSTNode * left_right_rotation(struct BSTNode *T) //LR型旋轉 兩種情況分別為 T為根結點/T不為根結點 {struct BSTNode *pTeml = T->pLchild;struct BSTNode *pTemlr = T->pLchild->pRchild;if(T->pParent){//先進行一次逆時針旋轉pTemlr->pParent = T;T->pLchild = pTemlr;pTeml->pParent = pTemlr;pTeml->pRchild = pTemlr->pLchild;pTemlr->pLchild = pTeml;//再進行一次順時針旋轉pTemlr->pParent = T->pParent;if(T->pParent->pLchild == T)T->pParent->pLchild = pTemlr;elseT->pParent->pRchild = pTemlr;T->pLchild = pTemlr->pRchild;T->pParent = pTemlr;pTemlr->pRchild = T;}else{//先進行一次逆時針旋轉pTemlr->pParent = T;T->pLchild = pTemlr;pTeml->pParent = pTemlr;pTeml->pRchild = pTemlr->pLchild;pTemlr->pLchild = pTeml;//再進行一次順時針旋轉pTemlr->pParent = NULL;T->pLchild = pTemlr->pRchild;T->pParent = pTemlr;pTemlr->pRchild = T;}return pTemlr; }struct BSTNode * right_left_rotation(struct BSTNode *T) //RL型旋轉 {struct BSTNode *pTemr = T->pRchild;struct BSTNode *pTemrl = T->pRchild->pLchild;if(T->pParent){//先進行一次順時針旋轉pTemrl->pParent = T;T->pRchild = pTemrl;pTemr->pParent = pTemrl;pTemr->pLchild = pTemrl->pRchild;pTemrl->pRchild = pTemr;//再進行一次逆時針旋轉pTemrl->pParent = T->pParent;if(T->pParent->pLchild == T)T->pParent->pLchild = pTemrl;elseT->pParent->pRchild = pTemrl;T->pRchild = pTemrl->pLchild;T->pParent = pTemrl;pTemrl->pLchild = T;}else{//先進行一次逆時針旋轉pTemrl->pParent = T;T->pRchild = pTemrl;pTemr->pParent = pTemrl;pTemr->pLchild = pTemrl->pRchild;pTemrl->pRchild = pTemr;//再進行一次順時針旋轉pTemrl->pParent = NULL;T->pRchild = pTemrl->pLchild;T->pParent = pTemrl;pTemrl->pLchild = T;}return pTemrl; }判斷結點的平衡狀態 若失衡則進行相應的平衡化處理
struct BSTNode * enBalance(struct BSTNode *T) {if(abs(getBalanceFactor(T)) < 2) //平衡{return T;}else{if(getBalanceFactor(T) > 0){if(getNodeDepth(T->pLchild->pLchild) > getNodeDepth(T->pLchild->pRchild)) {return left_left_rotation(T);}else{return left_right_rotation(T);}}else{if(getNodeDepth(T->pRchild->pRchild) > getNodeDepth(T->pRchild->pLchild)){return right_right_rotation(T);}else{return right_left_rotation(T);}}} }二叉查找樹的插入操作
struct BSTNode * InsertBSTree(struct BSTNode *pBST,int NUM) //插入元素 返回新插入的結點 {struct BSTNode *pRoot = pBST;//記錄根節點地址struct BSTNode *pNew = (struct BSTNode *)malloc(sizeof(struct BSTNode));if(NULL == pNew)exit(-1);pNew ->data = NUM;pNew ->pLchild = pNew ->pRchild = NULL;struct BSTNode *pTmp = NULL;while(NULL != pBST ){pTmp = pBST;if(pNew ->data < pBST ->data)pBST = pBST ->pLchild;elsepBST = pBST ->pRchild;}pNew->pParent = pTmp;if(NULL == pTmp) //當樹為空時,將插入節點的地址賦給根節點*pRoot = *pNew;else if(pNew ->data <= pTmp ->data)pTmp ->pLchild = pNew;elsepTmp ->pRchild = pNew;return pNew; }構建一個平衡二叉樹
int main() {int n = 5;int i;int a[5] = {3,10,24,65,32};struct BSTNode *pRoot;struct BSTNode *Temp;struct BSTNode *Record,*Record2;Record = NULL;pRoot = (struct BSTNode *)malloc(sizeof(struct BSTNode));pRoot->pParent = NULL;pRoot->pLchild = pRoot->pRchild = NULL;pRoot->data = a[0];for(i = 1; i < n; i++){if(!pRoot->pParent)Temp = InsertBSTree(pRoot,a[i]);elseTemp = InsertBSTree(Record,a[i]);if(i >= 2){if(abs(getBalanceFactor(Temp->pParent->pParent)) >= 2){Record2 = Record;Record = enBalance(Temp->pParent->pParent);if(Record->pParent != NULL)Record = Record2;}}}printf("\n\t先序遍歷\t\n");PreOrderTraverse(Record);printf("\n\t中序遍歷\t\n");MidOrderTraverse(Record);return 0; }輸出結果為
構建成功.
新增一個根據數組創建樹的函數,將創建數寫到一個函數內,增強代碼美觀性,返回值為指向創建的樹的根結點的指針
struct BSTNode *createTree(struct BSTNode *pRoot, int* nums, int numSize){int i;struct BSTNode *Temp;struct BSTNode *Record = NULL,*Record2 = NULL;pRoot->data = nums[0];for(i = 1; i < numSize; i++){if(!pRoot->pParent)Temp = InsertBSTree(pRoot,nums[i]);elseTemp = InsertBSTree(Record,nums[i]);if(i >= 2){if(abs(getBalanceFactor(Temp->pParent->pParent)) >= 2){Record2 = Record;Record = enBalance(Temp->pParent->pParent);if(Record->pParent != NULL)Record = Record2;}}}return Record; }一個將gets到的字符串轉化成整型數組的函數
int transition(char *x, int *a){char b[2];int i = 0;int j = 0;int k = 0;int len = 0;while(i <= (int)strlen(x)){while(x[i] != ' ' && i < strlen(x)){b[j] = x[i];i++;j++;}a[k] = atoi(b);len++;i++;k++;j = 0;b[0] = ' ';b[1] = ' ';}return len; }總結
以上是生活随笔為你收集整理的平衡二叉查找树的构建的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 气象学需要计算机知识吗,没毕业就被签走的
- 下一篇: 如何使用BBP公式直接计算π的第n位