日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构——平衡二叉树(AVL树)之插入

發(fā)布時間:2024/1/1 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构——平衡二叉树(AVL树)之插入 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 前言
  • 一.定義
  • 二.基本操作
    • 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ì):

  • 將B結(jié)點的右子樹成為A的左子樹。(B的左子樹比B大,比A小)
  • 讓A成為B的右子樹(A比B大)
  • 將根節(jié)點設(shè)為B

    顯然只有只有從根節(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é)點。
    代碼如下:
  • 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才是正確的樹}

    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),并且仍然要是一顆二叉搜索樹。

  • 將C結(jié)點的左子樹成為A的右子樹。
  • 讓A成為C的左子樹
  • 將根節(jié)點設(shè)為C
  • 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;

    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型就很簡單了,先右旋,再左旋

    void LeftrightCir(Bactree **tree){//LR旋轉(zhuǎn)SigelrightCir(&((*tree)->left));SigelleftCir(tree); }

    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型就很簡單了,先左旋,再右旋

    void RightleftCir(Bactree **tree){//RL旋轉(zhuǎn)SigelleftCir(&((*tree)->right));SigelrightCir(tree); }

    代碼實現(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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。