二叉搜尋樹(Binary Search Tree )的定義及分析
定義:
二叉搜尋樹或者是一棵空樹,或者是具有下列性質的二叉樹:
-
每個結點都有一個作為搜尋依據的關鍵碼(key),所有結點的關鍵碼互不相同。
-
左子樹(如果非空)上所有結點的關鍵碼都小於根結點的關鍵碼。
-
右子樹(如果非空)上所有結點的關鍵碼都大於根結點的關鍵碼。
-
左子樹和右子樹也是二叉搜尋樹。
-
結點左子樹上所有關鍵碼小於結點關鍵碼;
-
右子樹上所有關鍵碼大於結點關鍵碼;
-
若從根結點到某個葉結點有一條路徑,路徑左邊的結點的關鍵碼不一定小於路徑上的結點的關鍵碼。
-
如果對一棵二叉搜尋樹進行中序遍歷,可以按從小到大的順序,將各結點關鍵碼排列起來,所以也稱二叉搜尋樹為二叉排序樹。 下圖為二叉搜尋樹的一個圖
二叉搜尋樹時一個用於查詢操作的搞笑資料結構,因為在最壞的情況下只要查詢其中一個分支上的資料就可以了。複雜度為O(nlg n),n為二叉樹元素的個數。所以在實際使用中,儘可能的保證二叉樹的平衡,這樣對搜尋來說是最高效的。
二叉樹的實現和分析
前面提到,只有當二叉樹搜尋樹保持平衡時對搜尋來說才是最高效的,如何保持平衡,實際上很難得。在實際運用中使用最多的就是AVL樹,專門在百度百科上專門搜尋了下。下面是概述:
********************************************************************************
概述
在電腦科學中,AVL樹是最先發明的自平衡二叉查詢樹。AVL樹得名於它的發明者 G.M. Adelson-Velsky 和 E.M. Landis,他們在 1962 年的論文 "An algorithm for the organization of information" 中發表了它。
AVL 節點數計算
高度為 h 的 AVL 樹,節點數 N 最多2^h − 1; 最少 (其中)。
最少節點數 n 如以費伯納西數列可以用數學歸納法證明:
Nh= F【h+ 2】 - 1 (F【h+ 2】是 Fibonacci polynomial 的第h+2個數)。
即:
N0 = 0 (表示 AVL Tree 高度為0的節點總數)
N1 = 1 (表示 AVL Tree 高度為1的節點總數)
N2 = 2 (表示 AVL Tree 高度為2的節點總數)
Nh= N【h− 1】 + N【h− 2】 + 1 (表示 AVL Tree 高度為h的節點總數)
換句話說,當節點數為 N 時,高度 h 最多為。
節點的平衡因子是它的右子樹的高度減去它的左子樹的高度。帶有平衡因子 1、0 或 -1 的節點被認為是平衡的。帶有平衡因子 -2 或 2 的節點被認為是不平衡的,並需要重新平衡這個樹。平衡因子可以直接儲存在每個節點中,或從可能儲存在節點中的子樹高度計算出來。
操作
AVL樹的基本操作一般涉及運做同在不平衡的二叉查詢樹所運做的同樣的演算法。但是要進行預先或隨後做一次或多次所謂的"AVL 旋轉"。
-
單向右旋平衡處理LL:(在百度百科上,這好像寫錯了)
由於在*a的左子樹根結點的左子樹上插入結點,*a的平衡因子由1增至2,致使以*a為根的子樹失去平衡,則需進行一次右旋轉操作;
-
單向左旋平衡處理RR:
由於在*a的右子樹根結點的右子樹上插入結點,*a的平衡因子由-1變為-2,致使以*a為根的子樹失去平衡,則需進行一次左旋轉操作;
-
雙向旋轉(先左後右)平衡處理LR:
由於在*a的左子樹根結點的右子樹上插入結點,*a的平衡因子由1增至2,致使以*a為根的子樹失去平衡,則需進行兩次旋轉(先左旋後右旋)操作。
-
雙向旋轉(先右後左)平衡處理RL:
由於在*a的右子樹根結點的左子樹上插入結點,*a的平衡因子由-1變為-2,致使以*a為根的子樹失去平衡,則需進行兩次旋轉(先右旋後左旋)操作。
**********************************************************************************
標頭檔案定義:
/*bistree.h*/
#ifndef BISTREE_H
#define BISTREE_H
/*定義平衡因子為AVL樹*/
#define AVL_LFT_HEAVY 1
#define AVL_BALANCED 0
#define AVL_RGT_HEAVY -1
/*定義AVL樹的節點*/
typedef struct AvlNode_ {
void *data;
int hidden;
int factor;/*平衡因子*/
}AvlNode;
/*定義二叉樹結構體*/
typedef struct BisTree_ {
int size;/*存放資料個數*/
int (*compare) (const void *key1,const void *key2);/*預留的一個介面*/
void (*destroy)(void *data);/*封裝的解構函式,作用是釋放data空間*/
BiTreeNode *root;/*指向根節點的一個指標*/
}BisTree;
/*函式介面*/
void bistree_init (BisTree *tree, int (*compare)(const void *key1,const void
*key2),void (*destroy)(void *data));
void bistree_destory(BisTree *tree);
int bistree_insert(BisTree *tree,const void *data);
int bistree_remove (BisTree *tree,const void *data);
int bistree_lookup(BisTree *tree,void **data);
#define bistree_size(tree) ((tree)->size)
#endif
左旋:(在演算法精解上面說的是右旋,和百度百科上描述的一樣,但我就按照我理解的寫吧)這個單憑看程式碼真的比較亂的,最好在圖紙上畫個圖,把實現過程畫一遍,其他也不是很難理解的。RR可以參照LL右旋的操作。RL也一樣。
/*bistree.c*/
#include <stdlib.h>
#include <string.h>
#include "../include/bistree.h"
#include "../include/bitree.h"
/*左旋*/
static void rotate_left(BiTreeNode **node)
{
BiTreeNode *right ,grandchild;
right = bitree_right(*node);
if (((AvlNode *)bitree_data(right) ->factor == AVL_RGT_HEAVY){
/* RR */
bitree_right(*node) = bitree_left(right);
bitree_left(right) = *node;
((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;
((AvlNode *)bitree_data(right)) ->factor = AVL_BALANCED;
*node = right;
} else { /*RL*/
grandchild = bitree_left(right);
bitree_left(right) = bitree_right(grandchild);
bitree_right(grandchild) = right;
bitree_right(*node) = bitree_left(grandchild);
bitree_left(grandchild) = *node;
switch (((AvlNode *)bitree_data(grandchild)) ->factor){
case AVL_LFT_HEAVY:
((AvlNode *)bitree_data(*node)) ->factor = AVL_BALANCED;
((AvlNode *)bitree_data(right)) ->factor = AVL_RGT_HEAVY;
break;
case AVL_BALANCED:
((AvlNode *)bitree_data(*node)) ->factor = AVL_BALANCED;
((AvlNode *)bitree_data(right)) ->factor = AVL_BALANCED;
break;
case AVL_RGT_HEAVY:
((AvlNode *)bitree_data(*node)) ->factor = AVL_LFT_HEAVY;
((AvlNode *)bitree_data(right)) ->factor = AVL_BALANCED;
break;
default:
break;
}
((AvlNode *)bitree_data(grandchild)) ->factor = AVL_BALANCED;
*node = grandchild;
}
return;
}
右旋
左旋的逆操作
/*右旋*/
static void rotata_right(BiTreeNode **node)
{
BiTreeNode *left ,grandchild;
left = bitree_left(*node);
if (((AvlNode *)bitree_data(left) ->factor == AVL_LFT_HEAVY){
/* LL */
bitree_left(*node) = bitree_right(left);
bitree_right(left) = *node;
((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;
((AvlNode *)bitree_data(left)) ->factor = AVL_BALANCED;
*node = left;
} else { /*LR*/
grandchild = bitree_right(left);
bitree_right(left) = bitree_left(grandchild);
bitree_left(grandchild) = left;
bitree_left(*node) = bitree_right(grandchild);
bitree_right(grandchild) = *node;
switch (((AvlNode *)bitree_data(grandchild)) ->factor){
case AVL_LFT_HEAVY:
((AvlNode *)bitree_data(*node)) ->factor = AVL_RGT_HEAVY;
((AvlNode *)bitree_data(left)) ->factor = AVL_BALANCED;
break;
case AVL_BALANCED:
((AvlNode *)bitree_data(*node)) ->factor = AVL_BALANCED;
((AvlNode *)bitree_data(left)) ->factor = AVL_BALANCED;
break;
case AVL_RGT_HEAVY:
((AvlNode *)bitree_data(*node)) ->factor = AVL_BALANCED;
((AvlNode *)bitree_data(left)) ->factor = AVL_LFT_HEAVY;
break;
default:
break;
}
((AvlNode *)bitree_data(grandchild)) ->factor = AVL_BALANCED;
*node = grandchild;
}
return;
}
AVL樹初始化及刪除節點
/*刪除當前節點的左子樹
* 如過引數2為NULL,表示刪除所有節點
*/
static void destroy_left (BisTree * tree, BiTreeNode * node)
{
BiTreeNode ** position;
/*當樹為空時,直接返回*/
if (bistree_size(tree) == 0){
return;
}
/*當刪除節點為NULL時,當前位置指向樹根
否則,指向當前節點的左子樹*/
if (node == NULL){
position = &tree->root;
} else {
position = &node->left;
}
/*刪除節點*/
if (*position != NULL){
destroy_left(tree,*position);
destroy_right(tree, *position);
if (tree ->destroy != NULL){
tree->destroy(((AvlNode *)(*position)->data)->data);
}
free ((*position)->data);
free(*position);
*position = NULL;
tree->size --;
}
return ;
}
/*刪除節點的右子樹
*如果節點為NULL,刪除整個樹
*/
static void destroy_right(BisTree * tree, BiTreeNode * node)
{
BiTreeNode ** position;
/*當樹為空時,直接返回*/
if (bistree_size(tree) == 0){
return;
}
/*當刪除節點為NULL時,當前位置指向樹根
否則,指向當前節點的左子樹*/
if (node == NULL){
position = &tree->root;
} else {
position = &node->right;
}
/*刪除節點*/
if (*position != NULL){
destroy_left(tree,*position);
destroy_right(tree, *position);
if (tree ->destroy != NULL){
tree->destroy(((AvlNode *)(*position)->data)->data);
}
free ((*position)->data);
free(*position);
*position = NULL;
tree->size --;
}
return ;
}
/*初始化AVL*/
void bistree_init(BisTree * tree, int(* compare)(const void * key1, const void * key2), void(* destroy)(void * data))
{
bitree_init(tree, destroy);
tree->compare = compare;
return;
}
/*銷燬*/
void bistree_destory(BisTree * tree)
{
/*刪除所有節點*/
destroy_left( tree, NULL);
memset(tree,0,sizeof(BisTree));
return;
}
插入和刪除
static int insert(BisTree *tree,BiTreeNode **node,const void *data,int *balanced)
{
AvlNode *avl_data;
int cmpval,retval;
/**/
if (bitree_is_eob(*node)){/*插入到根節點*/
/*申請空間,並賦值*/
if ((avl_data = (AvlNode *) malloc(sizeof(AvlNode))) == NULL)
return -1;
avl_data ->factor = AVL_BALANCED;
avl_data ->hidden = 0;
avl_data -> data = (void *) data;
return bitree_ins_left(tree, * node, avl_data);
} else {/*插入不為空的情況*/
/*比較插入數值與節點數值大小*/
cmpval = tree->compare(data,((AvlNode*)bitree_data(*node))->data);
if (cmpval < 0){
/*插入數值小於節點數值時,插入左子樹*/
if (bitree_is_eob(bitree_left(*node))){
/*當插入節點左子樹為空時,直接插入並且樹平衡*/
if ((avl_data = (AvlNode *) malloc(sizeof(AvlNode))) == NULL)
return -1;
avl_data ->factor = AVL_BALANCED;
avl_data ->hidden = 0;
avl_data -> data = (void *) data;
*balanced = 0;
return bitree_ins_left(tree, * node, avl_data);
} else { /*當不為空時,繼續判斷*/
if (retval = insert( tree, &bitree_left(*node), data, balanced) != 0)
return retval;
}
/*確保樹的平衡*/
if (!(*balanced)){
switch (((AvlNode *)bitree_data(*node)->factor){
case AVL_LFT_HEAVY:
rotata_right(node);
*balanced = 1;
break;
case AVL_BALANCED:
((AvlNode*)bitree_data(*node) -> factor = AVL_LFT_HEAVY;
break;
case AVL_RGT_HEAVY:
((AvlNode*)bitree_data(*node) -> factor = AVL_BALANCED;
*balanced = 1;
break;
}
}
}else if (cmpval > 0){
/*插入右子樹*/
if (bitree_is_eob(bitree_right(*node))){
if ((avl_data = (AvlNode *) malloc(sizeof(AvlNode))) == NULL)
return -1;
avl_data ->factor = AVL_BALANCED;
avl_data ->hidden = 0;
avl_data -> data = (void *) data;
*balanced = 0;
return bitree_ins_right(tree, * node, avl_data);
} else {/*節點不為空時,繼續插入*/
if (retval = insert( tree, &bitree_right(*node), data, balanced) != 0)
return retval;
}
/*確保樹的平衡*/
if (!(*balanced)){
switch (((AvlNode *)bitree_data(*node)->factor){
case AVL_RGT_HEAVY:
rotata_right(node);
*balanced = 1;
break;
case AVL_BALANCED:
((AvlNode*)bitree_data(*node) -> factor = AVL_RGT_HEAVY;
break;
case AVL_LFT_HEAVY:
((AvlNode*)bitree_data(*node) -> factor = AVL_BALANCED;
*balanced = 1;
break;
}
}
} else { /*cmpval = 0*/
/*插入資料相同*/
if (!((AvlNode *)bitree_data(*node))->hadden){
return 1;
} else {/*將替換掉原來的數值*/
if (tree ->destroy != NULL){
tree ->destroy(((AvlNode*)bitree_data(*node)) ->data);
}
((AvlNode*)bitree_data(*node) -> data = (void *)data;
((AvlNode*)bitree_data(*node) ->hadden = 0;
*balanced = 1;
}
}
}
return 0;
}
static int hide(BisTree *tree, BiTreeNode *node, const void *data)
{
int cmpval,retval;
if (bitree_is_eob(node)
return -1;
cmpval = tree->compare(data,((AvlNode*)bitree_data(node))->data);
if (cmpval < 0 ){/*去左子樹查詢*/
retval = hide(tree, bitree_left(node),data);
} else if (cmpval > 0){
retval = hide( tree, bitree_right(node), data);
} else {
/*設定為隱藏*/
((AvlNode *)bitree_data(node))->hidden = 1;
retval = 0
}
return retval;
}
/*插入資料
* 返回值:
* 0: 成功
* 1: 插入資料已經存在樹中
* -1: 失敗
*/
int bistree_insert(BisTree * tree, const void * data)
{
/*balance 作用是設定一個標誌位用來對二叉樹的
*左右旋操作及設定factor的數值
* 0:表示不平衡,需要重新設定
* 1:則正好相反。
*/
int balance = 0;
return insert(tree,&bitree_root(tree),data,&balance);
}
/*刪除資料
* 原則上並未刪除這個節點,只是將節點標誌設定
* 為隱藏。
*/
int bistree_remove(BisTree * tree, const void * data)
{
return hide (tree,bitree_root(tree),data);
}
查詢資料
/*查詢資料*/
int look_up(BisTree *tree, BiTreeNode *node, void **data)
{
int cmpval,retval;
if (bitree_is_eob(node))
return -1;
cmpval = tree->compare(data,((AvlNode*)bitree_data(node))->data);
if (cmpval < 0 ){/*去左子樹查詢*/
retval = look_up(tree, bitree_left(node),data);
} else if (cmpval > 0){
retval = look_up( tree, bitree_right(node), data);
} else {
if (!((AvlNode *)bitree_data(node))->hidden ){
*data = ((AvlNode *)bitree_data(node))->data;
retval =0;
} else {
return -1;
}
}
return retval;
}
相關文章
- 二叉搜尋樹(Binary Search Tree)(Java實現)Java
- 如何在Java中實現二叉搜尋樹( binary search tree)?Java
- 【LeetCode 235_二叉搜尋樹】Lowest Common Ancestor of a Binary Search TreeLeetCode
- 96-Unique Binary Search Trees 二叉搜尋樹的數量
- PAT 1043 Is It a Binary Search Tree (25分) 由前序遍歷得到二叉搜尋樹的後序遍歷
- Maximum Depth of Binary Tree 二叉樹的深度二叉樹
- [CareerCup] 4.7 Lowest Common Ancestor of a Binary Search Tree 二叉樹的最小共同父節點二叉樹
- 二叉搜尋樹
- [CareerCup] 4.1 Balanced Binary Tree 平衡二叉樹二叉樹
- leetcode 700. 二叉搜尋樹中的搜尋 思考分析LeetCode
- 173. Binary Search Tree Iterator
- LintCode-Search Range in Binary Search Tree
- 二叉搜尋樹的操作集
- 二叉搜尋樹的結構
- 看動畫學演算法之:平衡二叉搜尋樹AVL Tree動畫演算法
- Day20 | 654.最大二叉樹 、 617.合併二叉樹 、 700.二叉搜尋樹中的搜尋 98.驗證二叉搜尋樹二叉樹
- 二叉搜尋樹和二叉樹的最近公共祖先二叉樹
- 96. 不同的二叉搜尋樹
- 資料結構中的樹(二叉樹、二叉搜尋樹、AVL樹)資料結構二叉樹
- javascript實現二叉搜尋樹JavaScript
- 有序表和搜尋二叉樹二叉樹
- 501-Find Mode in Binary Search Tree
- Leetcode Validate Binary Search TreeLeetCode
- leetcode 之 Recover Binary Search TreeLeetCode
- Recover Binary Search Tree leetcode javaLeetCodeJava
- 257. Binary Tree Paths(列印二叉樹所有路徑)二叉樹
- js實現完全排序二叉樹、二叉搜尋樹JS排序二叉樹
- 二叉搜尋樹的python實現Python
- Leetcode 700. 二叉搜尋樹中的搜尋(DAY 2)LeetCode
- SAP RETAIL 特性樹(Characteristic Tree)的定義AI
- JavaScript 二叉搜尋樹以及實現翻轉二叉樹JavaScript二叉樹
- 樹(1)--樹和二叉樹的基本定義二叉樹
- LeetCode 545. Boundary of Binary Tree 二叉樹邊界LeetCode二叉樹
- 改造layui-樹(tree)元件支援樹的關鍵字搜尋操作UI元件
- 演算法篇 - 二叉搜尋樹演算法
- 資料結構-二叉搜尋樹資料結構
- 【資料結構】二叉搜尋樹!!!資料結構
- 二叉搜尋樹程式碼例項