数据结构——平衡二叉树(AVL树)之插入
文章目錄
- 前言
- 一.定義
- 二.基本操作
- 1.查找,
- 2.插入(如何調(diào)整)
- 如何調(diào)整
- 代碼實現(xiàn)插入
前言
首先我們來思考一下一個普通二叉樹保存數(shù)據(jù),如果想查找一個數(shù)據(jù),由于普通二叉樹保存數(shù)據(jù)是隨機的,要找到數(shù)據(jù)的時間復(fù)雜度為O(n)。后面為了方便
,我們又學(xué)習(xí)二叉搜索樹,它的定義是將比根節(jié)點小的數(shù)放左邊,比根節(jié)點大的數(shù)放右邊,并且每一課子樹都是二叉搜索樹這樣使得數(shù)據(jù)在樹上存儲有一定
的規(guī)律,在一定情況下查找起來很方便。但是,二叉搜索數(shù)當給出數(shù)據(jù)的順序不同時,二叉搜索樹也會不同。比如如果給出的序列為{1,2,3,4,5}或
{3,2,1,4,5}兩顆二叉搜索樹會截然不同,查找效率也會不同。
我們知道一顆樹的查找效率與樹的高度有關(guān),最好的樹結(jié)構(gòu)當然是滿二叉樹或者是完全二叉樹,我們稱這種樹是平衡的。由我們知道二叉搜索樹在一定情況下可以達到很好的查找效率。但是,通常情況二叉搜索樹的結(jié)點插入順序并不能事先確定,動態(tài)查找(查找時同時進行刪除與插入操作)的時候總還是會改變數(shù)的結(jié)構(gòu),不能做到平衡。所以,我們就需要考慮如何保證既能完成動態(tài)查找,又能保持一個較完美樹結(jié)構(gòu)的二叉搜索樹。這樣我們就有兩個問題需要考慮:一個是標準,什么樣的樹才是一顆樹結(jié)構(gòu)好的樹,第二是平衡化處理,怎樣使其達到平衡。
一.定義
平衡二叉樹又稱AVL樹(由發(fā)明它的兩位數(shù)學(xué)家名字命名),它仍然是一顆二叉搜索樹,所以具備二叉搜索樹的所有性質(zhì),只是加上了"平衡"的限制。 平衡二叉樹要不是一顆空樹,要不是具備以下條件的非空二叉搜索樹: 1. 左右子樹高度差的絕對值不能超過1(平衡因子,相當于一種標準)。 2. 左右子樹仍然是一顆平衡二叉樹。 有了平衡因子的定義,我們就可以找出不平衡的樹,然后再對其進行平衡化處理,處理的具體內(nèi)容分如下。處理完后使樹的高度能保持在O(logn)級別,使得 查找操作的時間復(fù)雜度為O(logn).
圖1圖2圓上為平衡因子的值,明顯圖1不是平衡二叉樹,有提個點的平衡因子超過了1。圖2是平衡二叉樹。
二.基本操作
1.查找,
由于平衡二叉樹仍是一顆二叉搜索樹,其查找操作并不會改變平衡因子,所以與二叉搜索樹的操作相同。可見博客:二叉搜索樹的查找。
2.插入(如何調(diào)整)
如何調(diào)整
假設(shè)這里有一顆平衡二叉樹:(上方代表平衡因子)
LL型
當要往D的左子樹或者右子樹插入結(jié)點時,此時破壞了原來平衡二叉樹的平衡結(jié)構(gòu),如圖:此時A結(jié)點的平衡因子超過了1,我們稱A結(jié)點為發(fā)現(xiàn)問題結(jié)點,六角星為產(chǎn)生問題結(jié)點。
此時,我們需要看發(fā)現(xiàn)問題結(jié)點與產(chǎn)生問題結(jié)點路徑上,從發(fā)現(xiàn)問題結(jié)點開始的連續(xù)三個結(jié)點。入上圖的A->B->D。我們發(fā)現(xiàn)這三個結(jié)點都在發(fā)現(xiàn)問題結(jié)點A左子樹的左子樹上,我們稱這種不平衡狀態(tài)為LL型(向左傾斜)不平衡。
如何調(diào)整,LL型不平衡的調(diào)整策略主要將這三個結(jié)點(A,B,D)順時針旋轉(zhuǎn),并且仍然要是一顆二叉搜索樹。
由于二叉搜索樹的性質(zhì):
顯然只有只有從根節(jié)點到插入結(jié)點路徑上的結(jié)點才會發(fā)生平衡因子的變化,因此只需要對這條路徑上的失衡的結(jié)點進行調(diào)整,并且只需要把最靠近插入節(jié)點的失衡節(jié)點調(diào)整到正常,路徑上的所有結(jié)點都會平衡。所以發(fā)現(xiàn)問題的結(jié)點不一定是根節(jié)點
注意:看的是發(fā)現(xiàn)問題結(jié)點到產(chǎn)生問題結(jié)點路徑上,從發(fā)現(xiàn)問題結(jié)點開始的連續(xù)三個結(jié)點的方向,確定是什么型。并且主要也是調(diào)整這三個結(jié)點。
代碼如下:
RR型
RR型的處理方法與LL型類似,如圖:
產(chǎn)生問題結(jié)點為六角星結(jié)點(插入E的左子樹或者右子樹),發(fā)現(xiàn)問題結(jié)點為A結(jié)點。路徑上從發(fā)現(xiàn)問題結(jié)點開始的連續(xù)三個結(jié)點為(A,C,E)結(jié)點,分別在發(fā)現(xiàn)問題結(jié)點右子樹的右子樹上,所以為RR型(向右傾斜)不平衡。
調(diào)整方式:主要將這三個結(jié)點(A,C,E)逆時針旋轉(zhuǎn),并且仍然要是一顆二叉搜索樹。
LR型
產(chǎn)生問題結(jié)點為六角星(插入E的左子樹或者右子樹),發(fā)現(xiàn)問題結(jié)點為A結(jié)點。路徑上從發(fā)現(xiàn)問題結(jié)點開始的連續(xù)三個結(jié)點為(A,B,E)結(jié)點,分別在發(fā)現(xiàn)問題結(jié)點左子樹的右子樹上,所以為LR型不平衡。
調(diào)整方式:“左-右雙旋”,主要先將B結(jié)點作為根節(jié)點逆時針旋轉(zhuǎn)(左旋),再以A結(jié)點作為根節(jié)點順時針旋轉(zhuǎn)(右旋)。(畫圖以白色六角星作為插入)
代碼實現(xiàn)調(diào)整LR型就很簡單了,先右旋,再左旋
RL型
產(chǎn)生問題結(jié)點為六角星(插入D的左子樹或者右子樹),發(fā)現(xiàn)問題結(jié)點為A結(jié)點。路徑上從發(fā)現(xiàn)問題結(jié)點開始的連續(xù)三個結(jié)點為(A,C,D)結(jié)點,分別在發(fā)現(xiàn)問題結(jié)點右子樹的左子樹上,所以為RL型不平衡。
調(diào)整方式:"右-左雙旋"主要先將C結(jié)點作為根節(jié)點順時針旋轉(zhuǎn)(右旋),再以A結(jié)點作為根節(jié)點逆時針旋轉(zhuǎn)(左旋)。(畫圖以白色六角星作為插入)
代碼實現(xiàn)調(diào)整RL型就很簡單了,先左旋,再右旋
代碼實現(xiàn)插入
#include<stdio.h> #include<stdlib.h> #include<Windows.h> #pragma warning(disable:4996)typedef int Elementtype; typedef struct Balacetree{//二叉樹信息Elementtype val;int hight;struct Balacetree *left;struct Balacetree *right;}Bactree;Bactree *TreeNodeCreate(Elementtype x){//創(chuàng)建樹節(jié)點Bactree *tree = (Bactree *)malloc(sizeof(Bactree));tree->val = x;tree->hight = 1;tree->left = NULL;tree->right = NULL;return tree; }int GetHight(Bactree *tree){//求樹高度if (!tree){return 0;}int H;int LH;int RH;LH = GetHight(tree->left);RH = GetHight(tree->right);H = LH > RH ? LH : RH;return H + 1; } void SigelleftCir(Bactree **tree){//LL旋轉(zhuǎn)Bactree *temp = NULL;temp = (*tree)->left;(*tree)->left = temp->right;temp->right = (*tree);(*tree)->hight = (GetHight((*tree)->left) > GetHight((*tree)->right) ? GetHight((*tree)->left) : GetHight((*tree)->right)) + 1;//調(diào)整后要保持每個結(jié)點高度正確。temp->hight = (GetHight(temp->left) > GetHight(temp->right) ? GetHight(temp->left) : GetHight(temp->right)) + 1;(*tree) = temp; //要賦值給(*tree),不然temp才是正確的數(shù)} void SigelrightCir(Bactree **tree){//RR旋轉(zhuǎn),與LL一樣Bactree *temp = NULL;temp = (*tree)->right;(*tree)->right = temp->left;temp->left = (*tree);(*tree)->hight = (GetHight((*tree)->left) > GetHight((*tree)->right) ? GetHight((*tree)->left) : GetHight((*tree)->right)) + 1;temp->hight = (GetHight(temp->left) > GetHight(temp->right) ? GetHight(temp->left) : GetHight(temp->right)) + 1;(*tree) = temp;}void LeftrightCir(Bactree **tree){//LR旋轉(zhuǎn)SigelrightCir(&((*tree)->left));SigelleftCir(tree); } void RightleftCir(Bactree **tree){//RL旋轉(zhuǎn)SigelleftCir(&((*tree)->right));SigelrightCir(tree); }Bactree *BalaceTreePush(Bactree **tree, Elementtype x){if (!(*tree)){ //插入結(jié)點*tree=TreeNodeCreate(x);return *tree;}if ((*tree)->val > x){ //x小的插入左邊(*tree)->left = BalaceTreePush(&((*tree)->left), x);//往右邊找,將結(jié)點返回給左指針//為什么放循環(huán)里,放循環(huán)里確認了一個方向,是左邊if (GetHight((*tree)->left) - GetHight((*tree)->right) >= 2){//平衡因子if ((*tree)->left->val > x){//確認了第二個方向 左邊SigelleftCir(tree);//LL}else{//右邊LeftrightCir(tree);//LR}}}else{(*tree)->right = BalaceTreePush(&((*tree)->right), x);if (GetHight((*tree)->left) - GetHight((*tree)->right) <= -2){//注意是-2if ((*tree)->right->val < x){SigelrightCir(tree);}else{RightleftCir(tree);}}}(*tree)->hight = (GetHight((*tree)->left) > GetHight((*tree)->right) ? GetHight((*tree)->left) : GetHight((*tree)->right)) + 1;//更新高度return *tree;}void Print(Bactree *tree){if (tree){Print(tree->left);printf("%d ", tree->val);Print(tree->right);} }int main(){Bactree *T = NULL;BalaceTreePush(&T, -10);BalaceTreePush(&T, -3);BalaceTreePush(&T, 0);BalaceTreePush(&T, 5);BalaceTreePush(&T, 9);BalaceTreePush(&T, 10);Print(T);system("pause");return 0; }總結(jié)
以上是生活随笔為你收集整理的数据结构——平衡二叉树(AVL树)之插入的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab混响时间仿真,用PC机测量声
- 下一篇: vs setup project ,