上一篇寫了二叉排序樹,構建一個二叉排序樹,若是構建序列是徹底有序的,則會出現這樣的狀況:html
顯然這種狀況會使得二叉搜索樹退化成鏈表。當出現這樣的狀況,二叉排序樹的查找也就退化成了線性查找,因此咱們須要合理調整二叉排序樹的形態,使得樹上的每一個結點都儘可能有兩個子結點,這樣整個二叉樹的高度就會大約在\(log(n)\) 左右,其中 \(n\) 爲結點個數。算法
AVL樹也稱爲平衡二叉樹,是一種自平衡的二叉排序樹,本質上仍然是一顆二叉排序樹,只是增長了「平衡」的要求,平衡是指,對AVL樹中任何節點的兩個子樹的高度之差(稱爲平衡因子)的絕對值不超過 \(1\) 。能保證上面這一點,AVL樹的高度就能始終保持在 \(O(logn)\) 級別。數據結構
因爲須要對每一個結點都獲得平衡因子,所以在AVL樹的結構中加入一個變量height
,用來記錄當前結點爲根結點的子樹的高度:dom
typedef struct Node { char data; int height; struct Node* Left; struct Node* Right; }*AVLTree;
獲取 root
結點高度:測試
int getHeight(Node *root){ if(!root) return 0;//空節點高度爲0 return root->height; }
AVL樹是一顆二叉查找樹,所以查找操做與二叉查找樹相同。由於AVL樹的高度爲 \(O(logn)\) 級別,因此查找操做的時間複雜度爲 \(O(logn)\)。spa
能夠獲得和二叉查找樹徹底相同的代碼:3d
//找不到返回NULL,找到返回該節點。 //非遞歸 Node* Find(AVLTree t, int x) { if (!t)return NULL; if (t->data == x) return t; if (x < t->data) return BSTreeFind(t->Left, x); if (x > t->data) return BSTreeFind(t->Right, x); } //非遞歸 Node* Find(AVLTree T,int x) { BSTree p = T; while (p) { if (x == p->data) return p; p = x > p->data ? p->Right : p->Left; } return NULL; }
先拋開AVL樹的插入問題,看下面左邊的二叉排序樹。你們原本和平共處,忽然有一天 B 以爲本身的權值比 A 大,要造反,可是B要作根結點,必須也要保證調整後的樹仍然是一顆二叉排序樹。code
☆上全部權值都比A小, ∆ 上全部權值都比B大,無需在調整中進行位置變更;由於調整後B的左孩子變成了A,那麼▲必須移動到其餘地方去,由於A、B、▲的權值關係知足 A<▲<B ,因此讓▲成爲A的右子樹便可。htm
這個調整過程稱爲左旋,分解調整過程以下:blog
代碼以下:
void L(AVLTree *root){ Node* temp = (*root)->Right; //root指向結點A,temp指向結點B (*root)->Right = temp->Left; //圖示步驟2 temp->Left = *root; //圖示步驟3 root->height = max(getHeight(root->Left), getHeight(root->Rihgt)) + 1;//更新結點A高度 temp = max(getHeight(temp->Left), getHeight(temp->Rihgt)) + 1;//更新結點B高度 *root = temp;//圖示步驟4 }
右旋是左旋的逆過程,以下:
分解調整過程以下:
代碼以下:
void R(AVLTree *root) { Node* temp = (*root)->Left;//root指向結點B,temp指向結點A (*root)->Left = temp->Right; temp->Right = *root; root->height = max(getHeight(root->Left), getHeight(root->Rihgt)) + 1; temp = max(getHeight(temp->Left), getHeight(temp->Rihgt)) + 1; *root = temp; }
接下來討論AVL樹的插入操做,假設如今已經有一顆平衡二叉樹,那麼在向其中插入一個結點時,必定會有結點的平衡因子發生改變,此時就可能會有結點的平衡因子大於1 ,這樣以該結點爲根結點的子樹就是失去平衡的,會使平衡二叉樹發生失衡的狀況能夠分爲下面四種:
左左(LL)、右右(RR),LL,RR只表示樹型(致使樹失去平衡的插入位置),不是左旋、右旋的意思。
對於LL型,須要以A結點爲根進行右旋;
對於RR型,須要以A爲根結點進行左旋。
因此代碼以下:
void RR_Rotate(AVLTree *root){ L(root); } void LL_Rotate(AVLTree *root) { R(root); }
左右(LR)、右左(RL)。
對於LR型,須要先以B結點爲根結點進行一次左旋,再以A結點爲根結點進行一次右旋。
對於RL型,須要先以B結點爲根結點進行一次右旋,再以A結點爲根結點進行一次左旋。
void LR_Rotate(AVLTree *root) { L(&(*root)->Left); R(root); } void RL_Rotate(AVLTree *root) { R(&(*root)->Right); L(root); }
插入算法就是出現不平衡狀態時,判斷須要使用哪一種旋轉方式來使得二叉樹保持平衡
AVLTree InsertAVLTree(AVLTree root, int x) { if (root == NULL) { root = new Node; root->Left = NULL; root->Right = NULL; root->data = x; return root; } if (x > root->data) { //遞歸返回插入位置的父節點或者祖父……。 root->Right = InsertAVLTree(root->Right, x); //若是插入以後失去了平衡 if (height(root->Left) - height(root->Right) == -2) { //若是插入的值大於,當前節點的左孩子節點,說明該節點是插在root的右子樹上的 if (x > root->Left->data) RR_Rotate(&root); else RL_Rotate(&root); } } else if (x < root->data) { root->Left = InsertAVLTree(root->Left, x); if (height(root->Left) - height(root->Right) == 2) { if (x < root->Left->data) LL_Rotate(&root); else LR_Rotate(&root); } } else { cout << "the number is already included." << endl; return NULL; } return root; }
和二叉排序樹的節點的刪除差很少,就是多出來一個判斷從哪一個子樹刪除節點的問題。
void AVLTreeDel(AVLTree *root, int data) { if (!*root) { cout << "delete failed" << endl; return; } Node *p = *root; if (data == p->data) { //左右子樹都非空 if (p->Left && p->Right) { //在高度更大的那個子樹上進行刪除操做 //進左子樹,右轉到底,進右子樹,左轉到底,轉彎碰壁,殺孩子。 if (height(p->Left) > height(p->Right)) { Node *pre=NULL,*q = p->Left; if (!q->Right) q->Right = p->Right; else { while (q->Right) { pre = q; q = q->Right; } pre->Right = q->Left; q->Left = p->Left; q->Right = p->Right; } *root = q; } else { Node *pre = NULL, *q = p->Right; if (!q->Left) q->Left = p->Left; else { while (q->Left) { pre = q; q = q->Left; } pre->Left = q->Right; q->Left = p->Left; q->Right = p->Right; } *root=q; } } else (*root) = (*root)->Left ? (*root)->Left : (*root)->Right; delete p; } else if (data < p->data){//要刪除的節點在左子樹中 //在左子樹中進行遞歸刪除 AVLTreeDel(&(*root)->Left, data); //判斷是否仍然知足平衡條件 if (height(p->Right) - height(p->Left) == 2){ //若是當前節點右孩子的左子樹更高 if (height(p->Right->Left) > height(p->Right->Right)) RL_Rotate(root); else RR_Rotate(root); } } else{ AVLTreeDel(&(*root)->Right, data); if (height(p->Left) - height(p->Right) == 2) { if (height((*root)->Left->Left) > height((*root)->Left->Right)) LL_Rotate(root); else LR_Rotate(root); } } }
完整測試代碼:
#pragma once #include "top.h" typedef BTreeNode Node, *AVLTree; void RR_Rotate(AVLTree *root){ Node* Right = (*root)->Right; (*root)->Right = Right->Left; Right->Left = *root; *root = Right; } void LL_Rotate(AVLTree *root) { Node* Left = (*root)->Left; (*root)->Left = Left->Right; Left->Right = *root; *root = Left; } void LR_Rotate(AVLTree *root) { RR_Rotate(&(*root)->Left); return LL_Rotate(root); } void RL_Rotate(AVLTree *root) { LL_Rotate(&(*root)->Right); RR_Rotate(root); } AVLTree AVLTreeInsert(AVLTree root, int x) { if (root == NULL) { root = new Node; root->Left = NULL; root->Right = NULL; root->data = x; return root; } if (x > root->data) { root->Right = AVLTreeInsert(root->Right, x); //遞歸返回插入位置的父節點或者祖父……,若是失去了平衡 if (height(root->Left) - height(root->Right) == -2) { //若是插入的值大於,當前節點的右孩子節點,說明該節點是插在root的右子樹上的 //if (x > root->Left->data) RR_Rotate(&root);不能保證該節點必定有左子樹 if (x > root->Right->data)RR_Rotate(&root); else RL_Rotate(&root); } } else if (x < root->data) { root->Left = AVLTreeInsert(root->Left, x); if (height(root->Left) - height(root->Right) == 2) { if (x < root->Left->data) LL_Rotate(&root); else LR_Rotate(&root); } } else { cout << "the number is already included." << endl; return NULL; } return root; } AVLTree AVLTreeCreat(int *a, int length) { AVLTree T = NULL; for (int i = 0; i < length; i++) { T = AVLTreeInsert(T, a[i]); } return T; } Node* AVLFind(AVLTree T, int x) { Node *p = T; while (p) { if (x == p->data) break; p = x > p->data ? p->Right : p->Left; } return p; } AVLTree AVLMax(AVLTree p) { if (!p) return NULL; if (p->Right == NULL) return p; return AVLMax(p->Right); } AVLTree AVLMin(AVLTree p) { if (!p) return NULL; if (p->Left == NULL) return p; return AVLMin(p->Left); } void AVLTreeDel(AVLTree *root, int data) { if (!*root) { cout << "delete failed" << endl; return; } Node *p = *root; if (data == p->data) { //左右子樹都非空 if (p->Left && p->Right) { //在高度更大的那個子樹上進行刪除操做 //進左子樹,右轉到底,進右子樹,左轉到底,轉彎碰壁,殺孩子。 if (height(p->Left) > height(p->Right)) { Node *pre=NULL,*q = p->Left; if (!q->Right) q->Right = p->Right; else { while (q->Right) { pre = q; q = q->Right; } pre->Right = q->Left; q->Left = p->Left; q->Right = p->Right; } *root = q; } else { Node *pre = NULL, *q = p->Right; if (!q->Left) q->Left = p->Left; else { while (q->Left) { pre = q; q = q->Left; } pre->Left = q->Right; q->Left = p->Left; q->Right = p->Right; } *root=q; } } else (*root) = (*root)->Left ? (*root)->Left : (*root)->Right; delete p; } else if (data < p->data){//要刪除的節點在左子樹中 //在左子樹中進行遞歸刪除 AVLTreeDel(&(*root)->Left, data); //判斷是否仍然知足平衡條件 if (height(p->Right) - height(p->Left) == 2){ //若是當前節點右孩子的左子樹更高 if (height(p->Right->Left) > height(p->Right->Right)) RL_Rotate(root); else RR_Rotate(root); } } else{ AVLTreeDel(&(*root)->Right, data); if (height(p->Left) - height(p->Right) == 2) { if (height((*root)->Left->Left) > height((*root)->Left->Right)) LL_Rotate(root); else LR_Rotate(root); } } } int height(BTree L) { if (L == NULL) return 0; int left = height(L->Left); int right = height(L->Right); return left >= right ? left + 1 : right + 1; } void checkCreat() { int length = 10; int *a = getNoRepateRandomArray(length, 10); for (int i = 0; i < length; i++) { cout << a[i] << ","; } cout << endl; AVLTree T = AVLTreeCreat(a, length); int t = rand() % length; AVLTreeDel(&T, a[t]); for (int i = t; i < length - 1; i++) { a[i] = a[i + 1]; } Preorder(T); cout << endl; Inorder(T); cout << endl; Postorder(T); cout << endl; free(a); }