排序算法係列之二叉查找樹
排序算法係列之二叉查找樹
基本概念
二叉查找樹(Binary Search Tree),或者是一棵空樹,或者是具有下列性質的二叉樹:
- 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
- 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
- 它的左、右子樹也分別為二叉排序樹。

序列:1 3 4 6 7 8 10 13 14 序列:2 3 4 6 7 9 13 15 17 18 20
通常采取二叉鏈表作為二叉排序樹的存儲結構。中序遍曆二叉排序樹可得到一個關鍵字的有序序列,一個無序序列可以通過構造一棵二叉排序樹變成一個有序序列,構造樹的過程即為對無序序列進行排序的過程。每次插入的新的結點都是二叉排序樹上新的葉子結點,在進行插入操作時,不必移動其它結點,隻需改動某個結點的指針,由空變為非空即可。
二叉查找樹查找算法思想
在二叉查找樹b中查找x的過程為:
- 若b是空樹,則搜索失敗,否則:
- 若x等於b的根節點的數據域之值,則查找成功;否則:
- 若x小於b的根節點的數據域之值,則搜索左子樹;否則:
- 查找右子樹。
舉例圖:

二叉查找樹插入算法思想
向一個二叉查找樹b中插入一個節點s的算法,過程為:
- 若b是空樹,則將s所指結點作為根節點插入,否則:
- 若s->data等於b的根節點的數據域之值,則返回,否則:
- 若s->data小於b的根節點的數據域之值,則把s所指節點插入到左子樹中,否則:
- 把s所指節點插入到右子樹中。
舉例圖:

二叉查找樹刪除算法思想
在二叉排序樹刪去一個結點,分三種情況討論:
- 若*p結點為葉子結點,即PL(左子樹)和PR(右子樹)均為空樹。由於刪去葉子結點不破壞整棵樹的結構,則隻需修改其雙親結點的指針即可。
- 若*p結點隻有左子樹PL或右子樹PR,此時隻要令PL或PR直接成為其雙親結點*f的左子樹(當*p是左子樹)或右子樹(當*p是右子樹)即可,作此修改也不破壞二叉排序樹的特性。
- 若*p結點的左子樹和右子樹均不空。在刪去*p之後,為保持其它元素之間的相對位置不變,可按中序遍曆保持有序進行調整,可以有兩種做法:其一是令*p的左子樹為*f的左/右(依*p是*f的左子樹還是右子樹而定)子樹,*s為*p左子樹的最右下的結點,而*p的右子樹為*s的右子樹;其二是令*p的直接前驅(或直接後繼)替代*p,然後再從二叉排序樹中刪去它的直接前驅(或直接後繼)。
舉例圖:刪除節點值 6。方便記憶:節點6的右孩子直接替換刪除節點,節點6的左孩子掛在節點6的右孩子上。

二叉查找樹性能分析
每個結點的
為該結點的層次數。最壞情況下,當先後插入的關鍵字有序時,構成的二叉排序樹蛻變為單支樹,樹的深度為
,其平均查找長度為
(和順序查找相同),最好的情況是二叉排序樹的形態和折半查找的判定樹相同,其平均查找長度和
成正比。




二叉查找樹C語言實現
c語言.h代碼
/*file:biTree.h*/ #ifndef CHIYX_BITREE #define CHIYX_BITREE #ifndef NULL #define NULL 0 #endif typedef int DataType; //二叉樹的節點結構 typedef struct BiTreeNode { DataType data; struct BiTreeNode *parent; struct BiTreeNode *left; struct BiTreeNode *right; }BiTreeNode, *BiTree; //查找:返回第一個等於data域等於key的節點,不存在返回NULL BiTreeNode *search(BiTree *biTree, DataType key); //返回二叉樹的最小節點,空樹返回NULL BiTreeNode *minImum(BiTree *biTree); //返回二叉樹的最大節點,空樹返回NULL BiTreeNode *maxImum(BiTree *biTree); //返回節點x的後繼節點,不存在後繼(節點x為最大節點)返回NULL BiTreeNode *successor(BiTreeNode *x); //返回節點x的前驅節點,不存在前驅(節點x為最小節點)返回NULL BiTreeNode *predecessor(BiTreeNode *x); //將值data插入到二叉樹中(生成一個值為data的節點) void insertNode(BiTree *biTree, DataType data); //刪除一個值為data的節點 void deleteNode(BiTree *biTree, DataType data); //中序遍曆二叉樹 void inorderTraversal(BiTree *biTree, void (*visitor)(BiTreeNode *node)); #endif
C語言實現代碼:
/*file:biTree.c*/ #include <stdlib.h> #include "biTree.h" //查找:返回第一個等於data域等於key的節點,不存在返回NULL BiTreeNode *search(BiTree *biTree, DataType key) { BiTreeNode *curNode = *biTree; while (curNode != NULL && curNode->data != key) { if (key < curNode->data) { curNode = curNode->left; } else { curNode = curNode->right; } } return curNode; } //返回二叉樹的最小節點,空樹返回NULL BiTreeNode *minImum(BiTree *biTree) { BiTreeNode *curNode = *biTree; while (curNode != NULL && curNode->left != NULL) { curNode = curNode->left; } return curNode; } //返回二叉樹的最大節點,空樹返回NULL BiTreeNode *maxImum(BiTree *biTree) { BiTreeNode *curNode = *biTree; while (curNode != NULL && curNode->right != NULL) { curNode = curNode->right; } return curNode; } //返回節點x的後繼節點,不存在後繼(節點x為最大節點)返回NULL BiTreeNode *successor(BiTreeNode *x) { if (x == NULL) return NULL; //存在右子樹,則後繼節點為其右子樹中最小的節點 if (x != NULL && x->right != NULL) { return minImum(&(x->right)); } while (x->parent != NULL && x->parent->right == x) { x = x->parent; } return x->parent; //錯誤版本為 x, 此處應該返回父結點 } //返回節點x的前驅節點,不存在前驅(節點x為最小節點)返回NULL BiTreeNode *predecessor(BiTreeNode *x) { if (x == NULL) return NULL; //存在左子樹,則後繼節點為其左子樹中最大的節點 if (x != NULL && x->left != NULL) { return maxImum(&(x->left)); } while (x->parent != NULL && x->parent->left == x) { x = x->parent; } return x->parent; //錯誤版本為 x, 此處應該返回父結點 } void insertNode(BiTree *biTree, DataType data) { //創建節點 BiTreeNode *targetNode; targetNode = (BiTreeNode *)malloc(sizeof(BiTreeNode)); //沒有足夠內存 if (targetNode == NULL) return; targetNode->data = data; targetNode->parent = NULL; targetNode->left = NULL; targetNode->right = NULL; BiTreeNode *p, *y; p = *biTree; y = NULL; while (p != NULL ) { y = p; if (targetNode->data < p->data) { p = p->left; } else { p = p->right; } } //空樹,將新節點置為樹根 if (y == NULL) { *biTree = targetNode; } else { if (targetNode->data < y->data) { y->left = targetNode; } else { y->right = targetNode; } } targetNode->parent = y; } //刪除一個值為data的節點 void deleteNode(BiTree *biTree, DataType data) { //查找待刪除的節點 BiTreeNode *targetNode, *x, *y; targetNode = search(biTree, data); if (targetNode == NULL) return; //找出真正的刪除節點,如果目標節點最多隻有一個子樹,則其為真正刪除的節點 //否則其後繼節點(最多隻有一個子樹,想想為什麼)為真正刪除的節點,然後將後繼節點的值賦給目標節點 if (targetNode->left == NULL || targetNode->right == NULL) { y = targetNode; } else { y = successor(targetNode); } if (y->left != NULL) { x = y->left; } else { x = y->right; } if (x != NULL) { x->parent = y->parent; } //如果y是根節點, 則根節點變為x if (y->parent == NULL) { *biTree = x; } else { if (y->parent->right == y) { y->parent->right = x; } else { y->parent->left = x; } } if (y != targetNode) { targetNode->data = y->data; } //釋放y占有的空間 free(y); } //中序遍曆二叉樹 void inorderTraversal(BiTree *biTree, void (*visitor)(BiTreeNode *node)) { BiTreeNode *curNode; curNode = *biTree; if (curNode != NULL) { //遍曆左子樹 inorderTraversal(&(curNode->left), visitor); //訪問節點 visitor(curNode); //遍曆右子樹 inorderTraversal(&(curNode->right), visitor); } }
#include <stdio.h>
#include <stdlib.h>
#include "biTree.h"
#define N 10
void printNode(BiTreeNode *node);
int main(int argc, char *argv[]) {
BiTreeNode *root;
int i;
root = NULL;
int data[N] = {10, 23, 11, 98, 111, 87, 34, 11, 33, 8};
for (i = 0; i < N; i++) {
insertNode(&root, data[i]);
}
printf("before delete:\n");
inorderTraversal(&root, printNode);
printf("\n");
deleteNode(&root, 11);
deleteNode(&root, 8);
printf("after delete:\n");
inorderTraversal(&root, printNode);
printf("\n");
exit(0);
}
void printNode(BiTreeNode *node) {
printf("%d\t", node->data);
}
二叉查找樹優化
雖然二叉排序樹的最壞效率是O(n),但它支持動態查詢,且有很多改進版的二叉排序樹可以使樹高為O(logn):
- Size Balanced Tree(SBT)
- AVL樹
- 紅黑樹(紅黑樹係列文章)
- Treap(Tree+Heap)
這些均可以使查找樹的高度為。
後續再更!
最後更新:2017-04-03 19:13:18