二叉排序树(Binary Sort Tree)BST
又称二叉查找树(Binary Search Tree),亦称二叉搜索树。是数据结构中的一类。
优点:在一般情况下,查询效率比链表结构要高
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。
缺点:
插入数据可能会不平衡 一直在左或右子树插入,导致深度很高,这样效率变低
节点在左右子树中分布比较均匀,此时查找的时间复杂度为O(logn);最坏的情况就是在建立二叉排序树时,输入的关键字序列正好是有序的,此时形成的二叉排序树是一棵单支二叉树,此时查找退化成了单链表的查找,时间的复杂度为O(n).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#if 0
//这种用起来 后期加内容 会导致别树的不需要这个指针
typedef int KEY_VALUE;
struct bstree_node {
KEY_VALUE data;
struct bstree_node *left;
struct bstree_node *right;
};
struct bstree {
struct bstree_node *root;
};
struct bstree_node *bstree_create_node(KEY_VALUE key) {
struct bstree_node *node = (struct bstree_node*)malloc(sizeof(struct bstree_node));
if (node == NULL) {
assert(0);
}
node->data = key;
node->left = node->right = NULL;
return node;
}
int bstree_insert(struct bstree *T, int key) {
assert(T != NULL);
if (T->root == NULL) {
T->root = bstree_create_node(key);
return 0;
}
struct bstree_node *node = T->root;
struct bstree_node *tmp = T->root;
while (node != NULL) {
tmp = node;
if (key < node->data) {
node = node->left;
} else {
node = node->right;
}
}
if (key < tmp->data) {
tmp->left = bstree_create_node(key);
} else {
tmp->right = bstree_create_node(key);
}
return 0;
}
int bstree_traversal(struct bstree_node *node) {
if (node == NULL) return 0;
bstree_traversal(node->left);
printf("%4d ", node->data);
bstree_traversal(node->right);
}
#define ARRAY_LENGTH 20
int main() {
int keyArray[ARRAY_LENGTH] = {24,25,13,35,23, 26,67,47,38,98, 20,13,17,49,12, 21,9,18,14,15};
struct bstree T = {0};
int i = 0;
for (i = 0;i < ARRAY_LENGTH;i ++) {
bstree_insert(&T, keyArray[i]);
}
bstree_traversal(T.root);
printf("\n");
}
#else
// 提供一种业务分离 多树共享结点方式
typedef int KEY_VALUE;
#define BSTREE_ENTRY(name, type) \
struct name { \
struct type *left; \
struct type *right; \
}
//用宏定义结构体 业务节点包含数据和指针
struct bstree_node {
KEY_VALUE data;
BSTREE_ENTRY(, bstree_node) bst;
//BSTREE_ENTRY(, bstree_node) bst2; 可以定义多个 某些需要到的
};
struct bstree {
struct bstree_node *root;
};
struct bstree_node *bstree_create_node(KEY_VALUE key) {
struct bstree_node *node = (struct bstree_node*)malloc(sizeof(struct bstree_node));
if (node == NULL) {
assert(0);
}
node->data = key;
node->bst.left = node->bst.right = NULL;
return node;
}
int bstree_insert(struct bstree *T, int key) {
assert(T != NULL);
if (T->root == NULL) {
T->root = bstree_create_node(key);
return 0;
}
struct bstree_node *node = T->root;
struct bstree_node *tmp = T->root;
while (node != NULL) {
tmp = node;
if (key < node->data) {
node = node->bst.left;
} else {
node = node->bst.right;
}
}
if (key < tmp->data) {
tmp->bst.left = bstree_create_node(key);
} else {
tmp->bst.right = bstree_create_node(key);
}
return 0;
}
int bstree_traversal(struct bstree_node *node) {
if (node == NULL) return 0;
bstree_traversal(node->bst.left);
printf("%4d ", node->data);
bstree_traversal(node->bst.right);
}
#define ARRAY_LENGTH 20
int main() {
int keyArray[ARRAY_LENGTH] = {24,25,13,35,23, 26,67,47,38,98, 20,13,17,49,12, 21,9,18,14,15};
struct bstree T = {0};
int i = 0;
for (i = 0;i < ARRAY_LENGTH;i ++) {
bstree_insert(&T, keyArray[i]);
}
bstree_traversal(T.root);
printf("\n");
}
#endif
从而提出了AVL树和红黑树其中AVL树
AVL树
AVL是严格平衡的BST(平衡因子不超过1)
(1) 查找代价: 那么查找过程与BST一样,只是AVL不会出现最差情况的BST(单支树)。因此查找效率最好,最坏情况都是O(logN)数量级的。
(2) 插入代价: AVL必须要保证严格平衡(-1<=平衡因子<=1),那么每一次插入数据使得AVL中某些结点的平衡因子超过1就必须进行旋转操作。
事实上,AVL的每一次插入结点操作最多只需要旋转1次(单旋转或双旋转)。
因此,总体上插入操作的代价仍然在O(logN)级别上(插入结点需要首先查找插入的位置)。
(3) 删除代价:AVL删除结点的算法可以参见BST的删除结点,但是删除之后必须检查从删除结点开始到根结点路径上的所有结点的平衡因子。
因此删除的代价稍微要大一些。每一次删除操作最多需要O(logN)次旋转。
因此,删除操作的时间复杂度为O(logN)+O(logN)=O(2logN)
AVL 效率总结 : 查找的时间复杂度维持在O(logN),不会出现最差情况
AVL树在执行每个插入操作时最多需要1次旋转,其时间复杂度在O(logN)左右。
AVL树在执行删除时代价稍大,执行每个删除操作的时间复杂度需要O(2logN)。
总体使用没有红黑树多这里就不放代码了
二叉平衡树的严格平衡策略以牺牲建立查找结构(插入,删除操作)的代价,换来了稳定的O(logN) 的查找时间复杂度。 红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。
红黑树(Red-Black Tree)
红黑树满足原则
1. 每个结点是红的或者黑的
2. 根结点是黑的
3. 每个叶子结点是黑的
4. 如果一个结点是红的,则它的两个儿子都是黑的
5. 对每个结点,从该结点到其子孙结点的所有路径上的包含相同数目的黑结点
以上违背原则需要调整
(1) 查找代价:由于红黑树的性质(最长路径长度不超过最短路径长度的2倍),可以说明红黑树虽然不像AVL一样是严格平衡的,但平衡性 能还是要二叉排序树要好。
其查找代价基本维持在O(logN)左右,但在最差情况下(最长路径是最短路径的2倍少1),比AVL要略逊色一点。
(2) 插入代价:RBT插入结点时,需要旋转操作和变色操作。但由于只需要保证上述5原则即可。
因此插入结点最多只需要2次旋转,这一点和AVL的插入操作一样。虽然变色操作需要O(logN),但是变色操作十分简单,代价很小。
(3) 删除代价:RBT的删除操作代价要比AVL要好的多,删除一个结点最多只需要3次旋转操作。
RBT 效率总结 : 查找 效率最好情况下时间复杂度为O(logN),但在最坏情况下比AVL要差一些,但也远远好于BST。
插入和删除操作改变树的平衡性的概率要远小于AVL。因此需要的旋转操作的可能性要小,而且一旦需要旋转,插入一个结点最多只需要旋转2次,删除最多只需要旋转3次(小于AVL的删除操作所需要的旋转次数)。
虽然变色操作需要往上检查到根结点,时间复杂度在O(logN),但是实际上,这种操作由于简单所需要的代价很小
插入和删除时需要使用的一个重要操作左旋和右旋
作用:
左旋
//插入和删除使用 结点x是红色 右子树y黑色 左旋可以使得到x的这层的 左边黑色+1 右边黑色不变
右旋
//插入和删除使用 结点y是红色 左子树x黑色 右旋可以使得到y的这层的 左边黑色不变 右边黑色+1
插入默认需要红色,因为红色默认不会违背原则5,但可能违背原则4 当父亲是红色结点时
左旋和右旋xy示意图
插入结点
违背原则
3每个叶子结点是黑的
4.如果一个结点是红的,则它的两个儿子都是黑的
这里只举出了当父结点是左子树的情况,右子树时相反就行了
1.不需要旋转的情况,只需要变色就行了
2.违背原则:4.如果一个结点是红的,则它的两个儿子都是黑的
先变色,父亲123(红)和叔189(黑)结点都变黑色(爷爷150结点黑色变红色)
左边黑色数量3位 345(黑)->150->123(黑色)->89或145(黑)违背原则5
再以爷爷345结点为y 左子树150x 进行右旋,189需要接到右边子节点的当左子树
3.需要先左旋调整情况成上述2 以结点123为x, z结点150为y 进行左旋
(xy参考上面左旋和右旋xy示意图)
处理和情况2一样,变色再右旋
删除结点
删除结点相对比较复杂,需要找到xyz三个结点
先明白三个结点概念 z代表要删除的结点 y代表实际被删除的结点 x旋转调整的轴心点
y需要看情况,以下两种情况y等于z本身的时候
其他的时候y是z的后继,用后继去覆盖z 这样中序顺序才能正确
删除总共有四种情况其中
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RED 1
#define BLACK 2
typedef int KEY_TYPE;
typedef struct _rbtree_node {
unsigned char color;
struct _rbtree_node *right;
struct _rbtree_node *left;
struct _rbtree_node *parent;
KEY_TYPE key;
void *value;
} rbtree_node;
typedef struct _rbtree {
rbtree_node *root;
rbtree_node *nil;
} rbtree;
rbtree_node *rbtree_mini(rbtree *T, rbtree_node *x) {
while (x->left != T->nil) {
x = x->left;
}
return x;
}
rbtree_node *rbtree_maxi(rbtree *T, rbtree_node *x) {
while (x->right != T->nil) {
x = x->right;
}
return x;
}
//寻找后继结点 中序
rbtree_node *rbtree_successor(rbtree *T, rbtree_node *x) {
rbtree_node *y = x->parent;
if (x->right != T->nil) {
return rbtree_mini(T, x->right);
}
while ((y != T->nil) && (x == y->right)) {
x = y;
y = y->parent;
}
return y;
}
void rbtree_left_rotate(rbtree *T, rbtree_node *x) {
rbtree_node *y = x->right; // x --> y , y --> x, right --> left, left --> right
x->right = y->left; //1 1
if (y->left != T->nil) { //1 2
y->left->parent = x;
}
y->parent = x->parent; //1 3
if (x->parent == T->nil) { //1 4
T->root = y;
} else if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
y->left = x; //1 5
x->parent = y; //1 6
}
void rbtree_right_rotate(rbtree *T, rbtree_node *y) {
rbtree_node *x = y->left;
y->left = x->right;
if (x->right != T->nil) {
x->right->parent = y;
}
x->parent = y->parent;
if (y->parent == T->nil) {
T->root = x;
} else if (y == y->parent->right) {
y->parent->right = x;
} else {
y->parent->left = x;
}
x->right = y;
y->parent = x;
}
//插入后调整
void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {
//往上检查原则四
while (z->parent->color == RED) {
if (z->parent == z->parent->parent->left) {
//父亲是左子树 找他右边兄弟
rbtree_node *y = z->parent->parent->right;
if (y->color == RED) {
//父亲 和 父亲兄弟 都是红色 当前层不需要调整
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent; //z --> RED
} else {
//父亲兄弟是黑色 且本身是右子树 需要调整成左子树
//因为下面要统一用 右旋
if (z == z->parent->right) {
z = z->parent;
rbtree_left_rotate(T, z);
}
//染色 父亲 全染黑色 爷爷染红色 ,导致右边黑色结点-1
z->parent->color = BLACK;
z->parent->parent->color = RED;
//结点是红色 左子树黑色 右旋可以使得到x的这层的 左边黑色不变 右边黑色+1
rbtree_right_rotate(T, z->parent->parent);
}
}else {
//左兄弟
rbtree_node *y = z->parent->parent->left;
if (y->color == RED) {
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent; //z --> RED
} else {
//父亲在右边 孩子需要调整成右边 统一
if (z == z->parent->left) {
z = z->parent;
rbtree_right_rotate(T, z);
}
z->parent->color = BLACK;
z->parent->parent->color = RED;
rbtree_left_rotate(T, z->parent->parent);
}
}
}
T->root->color = BLACK;
}
//插入默认红色 违背父亲是红色结点 插入不能是红色原则 再进行调整=
void rbtree_insert(rbtree *T, rbtree_node *z) {
rbtree_node *y = T->nil;
rbtree_node *x = T->root;
while (x != T->nil) {
y = x;
if (z->key < x->key) {
x = x->left;
} else if (z->key > x->key) {
x = x->right;
} else { //Exist
return ;
}
}
z->parent = y;
if (y == T->nil) {
T->root = z;
} else if (z->key < y->key) {
y->left = z;
} else {
y->right = z;
}
z->left = T->nil;
z->right = T->nil;
z->color = RED;
rbtree_insert_fixup(T, z);
}
//删除了黑色结点需要重新调整
void rbtree_delete_fixup(rbtree *T, rbtree_node *x) {
while ((x != T->root) && (x->color == BLACK)) {
if (x == x->parent->left) {
rbtree_node *w= x->parent->right;
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
rbtree_left_rotate(T, x->parent);
w = x->parent->right;
}
if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
w->color = RED;
x = x->parent;
} else {
if (w->right->color == BLACK) {
w->left->color = BLACK;
w->color = RED;
rbtree_right_rotate(T, w);
w = x->parent->right;
}
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
rbtree_left_rotate(T, x->parent);
x = T->root;
}
} else {
rbtree_node *w = x->parent->left;
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
rbtree_right_rotate(T, x->parent);
w = x->parent->left;
}
if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
w->color = RED;
x = x->parent;
} else {
if (w->left->color == BLACK) {
w->right->color = BLACK;
w->color = RED;
rbtree_left_rotate(T, w);
w = x->parent->left;
}
w->color = x->parent->color;
x->parent->color = BLACK;
w->left->color = BLACK;
rbtree_right_rotate(T, x->parent);
x = T->root;
}
}
}
x->color = BLACK;
}
//删除红色 不影响 当删除是黑色 需要重新调整 **注意:这里删除指的不是z是真正被删除的y**
rbtree_node *rbtree_delete(rbtree *T, rbtree_node *z) {
rbtree_node *y = T->nil;
rbtree_node *x = T->nil;
//如果当前就一个结点或者只有一个子树 删除就是删除自己
if ((z->left == T->nil) || (z->right == T->nil)) {
y = z;
} else {
//左右子树都不为空 需要找后继结点y
y = rbtree_successor(T, z);
}
if (y->left != T->nil) {
x = y->left;
} else if (y->right != T->nil) {
x = y->right;
}
//x指向y的父结点 y要被删除了
x->parent = y->parent;
if (y->parent == T->nil) {
T->root = x;
} else if (y == y->parent->left) {
y->parent->left = x;
} else {
y->parent->right = x;
}
if (y != z) {
z->key = y->key;
z->value = y->value;
}
//y被删除 是黑色 影响
if (y->color == BLACK) {
rbtree_delete_fixup(T, x);
}
return y;
}
//搜索key
rbtree_node *rbtree_search(rbtree *T, KEY_TYPE key) {
rbtree_node *node = T->root;
while (node != T->nil) {
if (key < node->key) {
node = node->left;
} else if (key > node->key) {
node = node->right;
} else {
return node;
}
}
return T->nil;
}
//中序遍历
void rbtree_traversal(rbtree *T, rbtree_node *node) {
if (node != T->nil) {
rbtree_traversal(T, node->left);
printf("key:%d, color:%d\n", node->key, node->color);
rbtree_traversal(T, node->right);
}
}
int main() {
int keyArray[20] = {24,25,13,35,23, 26,67,47,38,98, 20,19,17,49,12, 21,9,18,14,15};
rbtree *T = (rbtree *)malloc(sizeof(rbtree));
if (T == NULL) {
printf("malloc failed\n");
return -1;
}
T->nil = (rbtree_node*)malloc(sizeof(rbtree_node));
T->nil->color = BLACK;
T->root = T->nil;
rbtree_node *node = T->nil;
int i = 0;
for (i = 0;i < 20;i ++) {
node = (rbtree_node*)malloc(sizeof(rbtree_node));
node->key = keyArray[i];
node->value = NULL;
rbtree_insert(T, node);
}
rbtree_traversal(T, T->root);
printf("----------------------------------------\n");
for (i = 0;i < 20;i ++) {
rbtree_node *node = rbtree_search(T, keyArray[i]);
rbtree_node *cur = rbtree_delete(T, node);
free(cur);
rbtree_traversal(T, T->root);
printf("----------------------------------------\n");
}
}