二叉樹數組
什麼是二叉樹?數據結構
父節點至多隻有兩個子樹的樹形結構成爲二叉樹。以下圖所示,圖1不是二叉樹,圖2是一棵二叉樹。ide
圖1 普通的樹 圖2 二叉樹spa
若是一棵樹全部的非葉子節點都有兩個子節點,則稱該樹爲徹底二叉樹,圖2就是一棵徹底二叉樹。3d
二叉查找樹(ADT)指針
二叉樹一個重要的應用是二差查找樹,顧名思義,二叉查找樹是二叉樹在查找方面的應用。根據以往的知識,若是給你一個數組或者是鏈表,可能須要遍歷一整個數組或者是量表才能找到須要查找的目標。使用二叉樹做爲查找的數據結構,可以大大縮小查找的深度(若鏈表和數組的長度爲N,一棵二叉樹的深度爲logN),提升查找的效率。code
如何定義一棵查找樹?blog
設每個節點對應一個鍵值,而該節點的左子節點對用的數值小於該節點的值,該節點的右子節點的數值大於該節點的值。以下圖所示:it
圖3 二差查找樹 圖4 非二差查找樹event
圖3是二叉查找樹,由於他總數知足上述的條件,而圖4不是二叉查找樹,圖中紅色框住的部分不知足二叉查找樹的條件(父節點6<左子節點7)。
代碼:
struct Tree{
double value;
Tree *left;
Tree *right;
};
TreeNode表示二叉樹節點。其中value表示樹對應的值,left表示該節點的左節點指針,right表示該節點的右節點指針。
二叉查找樹的重要操做
1.查找
1.1 查找固定的數值
給定一個數值,查找二叉樹中是否有對應的數值,若是樹中包含該數值,則返回數值對應的節點,不然,返回NULL。查找的過程:
首先定位到跟節點,若是查找的數值跟節點的數值相等,則根節點爲所求;不然,若是數值小於根節點的數值,則查找根節點的左節點,相反,則查找跟節點的右節點。這樣一直遍歷下去,知道找到對應的或者或者已經不能繼續往下查找爲止(即已經到達子節點)。
代碼:
1 Tree* Find(double value,Tree *t){ 2 if(t==NULL) 3 return NULL; 4 if(value==t->value) 5 return t; 6 else if(value<t->value) 7 return Find(value,t->left); 8 else 9 return Find(value,t->right); 10 }
1.2查找最大值或最小值
二叉查找樹一個很大的特色是左子節點的數值<父節點的數值,而右子節點的數值>父節點的數值,所以查找最大值或者最小值就至關的方便,只須要從跟節點開始,不斷遍歷左子結點,直到到達葉子節點,就能夠獲得最小值;而查找最大值則從根節點開始遍歷右子結點,直到到達葉子節點,對用的數值即爲最大值。
代碼:
超找最小元素的節點
1 Tree* FindMin(Tree* t){ 2 if(t==NULL) 3 return NULL; 4 if(t->left==NULL) 5 return t; 6 else{ 7 return FindMin(t->left); 8 } 9 }
查找最大元素的節點
1 Tree* FindMax(Tree *t){ 2 if(t==NULL) 3 return NULL; 4 if(t->right==NULL) 5 return t->right; 6 else 7 return FindMax(t->right); 8 }
2. 插入
插入操做是將一個數值插入到二叉查找樹中的過程,插入後的樹依然知足二叉查找樹的條件。一棵二叉查找樹的構建過程,就是一個不斷將元素插入到二叉樹的過程。
插入的操做最關鍵的是一個查找的過程,若是在二叉樹中找到要插入的數值,則什麼都不用作,若是找不到,只須要在最後咱們查找的葉子節點上新增長一個子節點便可。舉一個例子:
上圖是一棵二叉樹,若是咱們要插入11,則按如下步驟進行:
再好比要找17,按如下步驟查找:
代碼:
1 void Insert(double value,Tree* t){ 2 if(t==NULL){ 3 t = new Tree(); 4 if(t==NULL) 5 return; 6 else{ 7 t->value=value; 8 t->left=NULL; 9 t->right=NULL; 10 } 11 } 12 if(t->value>value) 13 Insert(value,t->left); 14 else 15 Insert(value,t->right); 16 }
3. 刪除
刪除操做就是刪除鍵值與數值相同的節點。刪除操做相比查找和插入來講是一個較爲複雜的過程。若是要刪除的節點就是葉子節點,直接將該節點刪除便可,可是,若是要刪除非葉子節點,就須要經過調整其餘節點的位置來構建新的二叉樹。通常來講,直接用該節點的右子樹的最小值替換該節點的值,而後再刪除右子樹的最小節點便可。
例子:
AVL樹
雖然說二叉查找樹是一種優秀的數據結構,可以大大下降數據查詢的複雜度。可是,並非說有狀況下二叉樹都可以達到快速查找的目的。
咱們發現,若是按照[7,10,11,12,14,15,18]這樣的順序一個個元素進行插入的話就會出現右圖所示的二叉樹,這樣的二叉樹跟一個鏈表幾乎是沒有區別的,查找的效率同樣,沒有體現出二叉樹的優點。出現這種緣由是構建二叉樹的過程當中沒有平衡節點的左右子樹的高度。根節點7的右子樹有很高的深度,可是左子樹是空的。咱們須要的是一棵左右節點平衡的二叉樹,而其中一種傳統的平衡二叉樹是AVL樹。
定義:AVL樹是二叉樹,其各個節點的左右子樹的高度相差不超過1。
定義:數的高度能夠看作是節點與最低的葉子節點的距離。跟節點的高度最大,而葉子節點的高度爲0,一個不存在的節點的高度定義爲-1。
例如:左圖中節點12的高度爲2,節點18的高度爲0,而右圖中節點12的高度爲3,節點18的高度爲1。
左圖是一棵AVL樹,右圖不是一棵AVL樹,由於右圖節點15的左右子樹的高度相差2(左子樹的高度爲-1,右子樹的蓋度爲1)。
AVL樹的構建
AVL樹的構建一樣是不斷將元素插入的過程,可是與二叉查找樹不一樣,AVL樹在插入的過程當中須要知足AVL樹的條件,若是發現插入新的元素後不能知足AVL條件,須要經過調整元素的位置直到知足條件。
元素的插入無非就只有如下四種狀況:1.左子樹插入一個左節點;2.左子樹插入一個右節點;3.右子樹插入一個左節點;4.右子樹插入一個右節點。
其中,1和四、2和3是對稱的操做,所以下面只討論1和2兩種狀況。
1. 左子樹插入一個左節點
如上圖所述,左邊是原始的二叉樹,該二叉樹知足AVL樹的條件,在插入元素5後變成了右邊的二叉樹,而此時不知足AVL樹的條件,由於節點10的左右兩棵子樹的高度相差2(左子樹的高度爲1,右子樹的高度爲-1)。
此時須要經過對子樹進行調整才能讓二叉樹再次知足AVL的條件。
如上圖所示,調整後的二叉樹從新變成一棵AVL樹,則種調整的方法稱爲「左旋轉」,經過旋轉調整節點的位置,使二叉樹知足AVL樹的條件。同理,若是新增的元素爲右子樹的右子樹,並且新增後子樹的左右子樹高度相差爲2,此時進行「右旋轉」便可調整爲AVL樹。
2. 左子樹插入一個右節點
如上圖所述,左邊是原始的二叉樹,該二叉樹知足AVL樹的條件,在插入元素8後變成了右邊的二叉樹,而此時不知足AVL樹的條件,由於節點10的左右兩棵子樹的高度相差2(左子樹的高度爲1,右子樹的高度爲-1)。
與狀況1(左子樹新增左節點)不同,此時不能經過一個簡單的「坐旋轉」來調整左右子樹的高度。怎麼辦呢?須要經過兩次「旋轉」,顯示經過對元素7進行右旋轉,而後再對10進行左旋轉。以下圖所示:
同理,對於右子樹插入一個左節點的狀況,若是此時不符合AVL樹的條件,須要先進性「左旋轉」,再進行「右旋轉」便可。
代碼:
1 AVLTree* AVLInsert(double value,AVLTree * tree){ 2 if(tree==NULL){ 3 tree = new AVLTree(); 4 tree->value=value; 5 tree->left=NULL; 6 tree->right=NULL; 7 tree->height=0; 8 return; 9 } 10 if(value>tree->value){ 11 tree->right=AVLInsert(value,tree->right); 12 if(Height(tree->right)-Height(tree->left)==2){ 13 if(value>tree->right->value) 14 tree=SingleRotateWithRight(tree); 15 else 16 tree=DoubleRouteWithRight(tree); 17 } 18 } 19 else{ 20 tree->left=AVLInsert(value,tree->left); 21 if(Height(tree->left)-Height(tree->right)==2){ 22 if(value<tree->left->value) 23 tree=SingleRotateWithLeft(tree); 24 else 25 tree=DoubleRouteWithLeft(tree); 26 } 27 } 28 tree->height = Height(tree->left)>Height(tree->right)?Height(tree->left)+1:Height(tree->right)+1; 29 return tree; 30 } 31 //左旋轉 32 AVLTree* SingleRotateWithLeft(AVLTree * tree){ 33 AVLTree * left = tree->left; 34 tree->left=left->right; 35 left->right=tree; 36 tree->height=Height(tree->left)>Height(tree->right)?Height(tree->left)+1:Height(tree->right)+1; 37 left->height=Height(left->left)>Height(left->right)?Height(left->left)+1:Height(left->right)+1; 38 return left; 39 } 40 //右旋轉 41 AVLTree* SingleRotateWithRight(AVLTree * tree){ 42 AVLTree * right = tree->right; 43 tree->right=right->left; 44 right->left=tree; 45 tree->height=Height(tree->left)>Height(tree->right)?Height(tree->left)+1:Height(tree->right)+1; 46 right->height=Height(right->left)>Height(right->right)?Height(right->left)+1:Height(right->right)+1; 47 return right; 48 } 49 //右—左雙旋轉 50 AVLTree* DoubleRouteWithLeft(AVLTree* tree){ 51 tree->left = SingleRotateWithRight(tree->left); 52 return SingleRotateWithLeft(tree); 53 } 54 //右—左雙旋轉 55 AVLTree* DoubleRouteWithRight(AVLTree* tree){ 56 tree->right = SingleRotateWithLeft(tree->right); 57 return SingleRotateWithRight(tree); 58 }