爲實習準備的數據結構(5)-- 圖解AVL樹(平衡二叉搜索樹)

在這裏插入圖片描述

前言

以前種過AVL樹,爲何要再寫呢?依舊是由於我忘了,重刷一遍唄。node

平衡二叉搜索樹(AVL樹)

二叉搜索樹必定程度上能夠提升搜索效率,可是當原序列有序,例如序列A = {1,2,3,4,5,6},構造二叉搜索樹如圖。依據此序列構造的二叉搜索樹爲右斜樹,同時二叉樹退化成單鏈表,搜索效率下降爲O(n)。ios

以下圖:
在這裏插入圖片描述web

在此二叉搜索樹中查找元素6須要查找6次。二叉搜索樹的查找效率取決於樹的高度,所以保持樹的高度最小,便可保證樹的查找效率。一樣的序列A,改成下圖方式存儲,查找元素6時只需比較3次,查找效率提高一倍。數據結構

在這裏插入圖片描述

能夠看出當節點數目必定,保持樹的左右兩端保持平衡,樹的查找效率最高。這種左右子樹的高度相差不超過1的樹爲平衡二叉樹。svg

AVL樹的節點數據結構

和上面使用的那個普通結構略有不一樣。函數

class TreeNode{ 
 
   
public:
	//這幾個數據放作公有的,方便操做
    int depth; //深度,這裏計算每一個結點的深度,經過深度的比較可得出是否平衡
    TreeNode* parent; //該結點的父節點,方便操做
    int val;
    TreeNode* left;
    TreeNode* right;

    TreeNode(int x) : val(x), depth(0), left(NULL), right(NULL) { 
 
   }
    TreeNode() : val(0), depth(0), left(NULL), right(NULL) { 
 
   }
};

在原始數據上建立AVL樹

個人代碼嘗試:
(先對原始數據進行排序,而後再填充二叉搜索樹,使用遞歸的方式。)url

#include<iostream>
#include<vector>
using namespace std;

void createTree(vector<int>& vec, TreeNode* root, int begin, int end) { 
 
   
    //若是隻剩一個鍵
    if (begin == end) { 
 
   
        root->val = vec[begin];
        return;
    }

    int mid_sz = (begin+end)/2;
    root->val = vec[mid_sz];

    if (mid_sz - 1 >= begin) { 
 
   
        root->left = new TreeNode(0);
        createTree(vec, root->left, begin, mid_sz - 1);
    }

    root->right = new TreeNode(0);
    createTree(vec, root->right,mid_sz + 1,end);
}

void PreOrderTraverse(TreeNode* root) { 
 
   
    if (NULL == root)
        return;
    cout << root->val;
    PreOrderTraverse(root->left);
    PreOrderTraverse(root->right);
}

int main() { 
 
   
    TreeNode* roott = new TreeNode(0);
    vector<int> vec = { 
 
    0,1,2,3,4,5,6,7};
    createTree(vec,roott,0,vec.size()-1);
    PreOrderTraverse(roott);
}

調整樹的節點使平衡的操做:旋轉

LL (右旋):在左葉的左側插入數據

圖解過程:
在這裏插入圖片描述spa

在這裏插入圖片描述

代碼實現:

//在左葉的左側插入數據
TreeNode* LL(TreeNode* root) { 
 
   
    TreeNode* x = root->left;	//即將返回的節點是y的左子節點(就是那個B)
    TreeNode* temp = x->right;	//先把y的右子節點取出來(就是那個E)
    x->right = root;			//把y放進x的右子節點(把A放到B的右節點)
    root->left = temp;			//把前面預存的放到y的左子節點(把E放到A的右節點)
    return x;					//(返回那個B)
}

int main() { 
 
   
    TreeNode* roott = new TreeNode(0);
    vector<int> vec = { 
 
    0,1,2,3,4,5,6,7};
    createTree(vec,roott,0,vec.size()-1);
    roott = LL(roott);
    PreOrderTraverse(roott);
}

RR(左旋):在右子葉的右側插入數據

圖解過程:
在這裏插入圖片描述
在這裏插入圖片描述.net

右旋其實就是上面左旋的鏡像過程,因此不難,直接仿寫上面左旋的過程便可:3d

代碼實現

TreeNode* RR(TreeNode* root) { 
 
   
    TreeNode* x = root->right;	//即將返回的節點是y的右子節點
    TreeNode* temp = x->left;	//先把x的左子節點取出來
    x->left = root;			//把y放進x的左子節點
    root->right = temp;			//把前面預存的放到y的右子節點 
    return x;
}

int main() { 
 
   
    TreeNode* roott = new TreeNode(0);
    vector<int> vec = { 
 
    0,1,2,3,4,5,6,7};
    createTree(vec,roott,0,vec.size()-1);
    roott = RR(roott);
    PreOrderTraverse(roott);
}

後面的部分,就比較抽象了。


LR(左右旋):在左葉節點的右側插入數據

在這裏插入圖片描述

咱們將這種狀況抽象出來,獲得下圖:
在這裏插入圖片描述

咱們須要對節點y進行平衡的維護。步驟以下圖所示(第三個圖裏面x和z的位置換一下。):
在這裏插入圖片描述

代碼實現

TreeNode* LR(TreeNode* root) { 
 
   
	root->left = RR(root->left);
	root = LL(root);
	return root;
}
//簡單明瞭啊

RL(右左旋):在右葉節點的左側插入數據

在這裏插入圖片描述

咱們將這種狀況抽象出來,獲得下圖:

在這裏插入圖片描述

咱們須要對節點y進行平衡的維護。步驟以下圖所示(第三個圖裏面x和z的位置換一下。):
在這裏插入圖片描述

第二個圖中y的左孩子爲T1
(被水印遮住的部分爲:T1,T2,T3,T4)

代碼實現

TreeNode* RL(TreeNode* root) { 
 
   
    root->right = LL(root->right);
    root = RR(root);
    return root;
}
//簡單明瞭啊

新節點的插入

在這裏須要先補兩個函數,雖然可能如今看不懂,可是配上調用函數的上下文就懂了。

計算平衡因子

int getBalanceFactor(TreeNode* node){ 
 
   
	if(node==NULL){ 
 
   
		return 0;
	}
	return get_depth(node->left)-getHeight(node->right);
}
int get_depth(TreeNode* node){ 
 
   
	if(node==NULL){ 
 
   
		return 0;
	}
	return node->depth;
}

getBalanceFactor函數返回值的分析:

  1. 若是剛插入的葉子節點的爺爺節點的返回值大於0

    1. 若是剛插入的葉子節點的父節點的返回值大於0:(LL)
    2. 若是剛插入的葉子節點的父節點的返回值小於0:(LR)
  2. 若是剛插入的葉子節點的爺爺節點的返回值小於0

    1. 若是剛插入的葉子節點的父節點的返回值大於0:(RL)
    2. 若是剛插入的葉子節點的父節點的返回值小於0:(RR)

正式插入新節點

TreeNode* Insert_Node(TreeNode* root, int val) { 
 
   
    //先將節點插入
    if (NULL == root)
        return new TreeNode(val);
    else { 
 
   
        if (val < root->val)
            root->left = Insert_Node(root->left, val);
        else
            root->right = Insert_Node(root->right, val);
    }
    //計算平衡因子
    int balanceFactor = getBalanceFactor(root);

    //判斷是否該旋轉,該如何旋轉
    if (balanceFactor > 1) { 
 
       //左子樹有事兒
        balanceFactor = getBalanceFactor(root->left);
        if (balanceFactor == 1)     //插左邊了
            return LL(root);
        else if (balanceFactor == -1)   //插右邊了
            return RR(root);
        else { 
 
   
            cout << "罕見故障" << endl;
        }
    }
    else if (balanceFactor < -1) { 
 
     //右子樹有事兒
        balanceFactor = getBalanceFactor(root->right);
        if (balanceFactor == 1)     //插左邊了
            return RL(root);
        else if(balanceFactor == -1)   //插右邊了
            return RR(root);
        else { 
 
   
            cout << "罕見故障" << endl;
        }
    }
    return root;
}

int main() { 
 
   
    TreeNode* roott = new TreeNode(0);
    vector<int> vec = { 
 
    0,1,2,3,4,5,6,7};
    createTree(vec,roott,0,vec.size()-1);
    roott = Insert_Node(roott,8);
    PreOrderTraverse(roott);
}

現有節點刪除

代碼裏的註釋把整個過程寫的已經很詳盡了。

//刪除節點
TreeNode* DelSerchNode(TreeNode* node, int e) { 
 
   
    if (node == NULL)
        return NULL;
    TreeNode* retNode;
    if (e < node->val) { 
 
   
        node->left = DelSerchNode(node->left, e);
        retNode = node;
    }
    else if (e > node->val) { 
 
   
        node->right = DelSerchNode(node->right, e);
        retNode = node;
    }
    else { 
 
    
        // 待刪除節點左子樹爲空的狀況
        if (node->left == NULL) { 
 
   
            TreeNode* rightNode = node->right;
            node->right = NULL;
            retNode = rightNode;
        }
        // 待刪除節點右子樹爲空的狀況
        else if (node->right == NULL) { 
 
   
            TreeNode* leftNode = node->left;
            node->left = NULL;
            retNode = leftNode;
        }
        else { 
 
   
            // 待刪除節點左右子樹均不爲空的狀況
            // 找到比待刪除節點大的最小節點, 即待刪除節點右子樹的最小節點
            // 用這個節點頂替待刪除節點的位置
            TreeNode* temp = node;
            while (NULL != temp->left) { 
 
   
                temp = temp->left;
            }
            node->val = temp->val;
            node->left = NULL;
            //temp = NULL; //這還刪不掉了。。。。這指針還真是頑強
            delete temp;

            retNode = node;
        }
    }
    if (retNode == NULL)
        return NULL;

    //計算平衡因子
    int balanceFactor = getBalanceFactor(retNode);
    //判斷是否該旋轉,該如何旋轉
    if (balanceFactor > 1) { 
 
       //左子樹有事兒
        balanceFactor = getBalanceFactor(retNode->left);
        if (balanceFactor == 1)     //插左邊了
            return LL(retNode);
        else if (balanceFactor == -1)   //插右邊了
            return RR(retNode);
        else { 
 
   
            cout << "罕見故障" << endl;
        }
    }
    else if (balanceFactor < -1) { 
 
     //右子樹有事兒
        balanceFactor = getBalanceFactor(retNode->right);
        if (balanceFactor == 1)     //插左邊了
            return RL(retNode);
        else if (balanceFactor == -1)   //插右邊了
            return RR(retNode);
        else { 
 
   
            cout << "罕見故障" << endl;
        }
    }
    return retNode;
}

int main() { 
 
   
    TreeNode* roott = new TreeNode(0);
    vector<int> vec = { 
 
    0,1,2,3,4,5,6,7};
    createTree(vec,roott,0,vec.size()-1);
    roott = DelSerchNode(roott,5);
    PreOrderTraverse(roott);

先到這裏吧。

本文同步分享在 博客「看,將來」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索