java 算法 avl_红黑树_bst_查找算法 之 二叉查找树

[前言                       前面一篇文章,笔者就二叉查找树进行了一些解释与实现,这篇文章笔者将会就平衡二叉树                   做一些总结与实现。读者若不了解二

给定一个关键字 key 和 一个集合 Collection, 确定这个Collection是否包含给定的key,如果包含,找出该key对应的位置,这个过程为 查找 (Search)。

如果在 查找 过程中, Collection的结构始终不发生变化,那么称之为静态查找; 反之,需要频繁的向这个集合中添加、删除元素,从这样的集合中进行查找则称之为动态查找。

动态查找技术一般用树来存储查找集合,如二叉查找树。而二叉查找树的查找效率又与自身形态密切相关,需要对二叉查找树平衡化,保持其查找高效。本文就二叉查找树和平衡二叉树作简单介绍和分析以及代码实现,仅供学习参考。

1、二叉查找树

1.1  二叉查找树的定义

15dea76f7f6166b84fe0f060bb0383f8.png

二叉查找树(Binary Search Tree, BST),也有称作二叉排序树,二叉检索树,二叉搜索树,检索树 等等。其数据结构表示和二叉树相同,在此基础之上还有其特殊的性质,递归定义为:

一颗非空的二叉查找树(Binary Search Tree, BST),满足如下特性:

若根节点的左子树非空,则左子树所有节点数值小于根节点;

若根节点的右子树非空,则右子树所有节点数值大于根节点;

左右子树分别为一颗二叉查找树.

// define data type for the key

typedef int keyType;

// define binary search tree data structure

struct BinaryTreeNode {

keyType key;

BinaryTreeNode* left; // left child

BinaryTreeNode* right; // right child

};

// alias for the tree

typedef BinaryTreeNode bstree;

// alias for the tree node

typedef BinaryTreeNode bstnode;

1.2 基本操作(查找、插入和删除节点)

(1) 查找key:从定义一颗查找二叉树来看,可以利用其特性根据关键字进行快速查找,这和顺序表的二分查找一样。

// search BST by the given key

bstnode* search_by_key(bstree* tree, keyType key) {

bstnode* node = tree;

int found = 0;

while (NULL != node) {

if (key == node->key) {

found = 1;

break;

}

node = (key > node->key) ? node->right : node->left;

}

return found ? node : NULL;

}

(2) 插入key:向BST中插入一个key,不能破坏BST的规则

// insert a key to BST

int insert_key(bstree* &tree, keyType key) {

if (NULL == tree) {

tree = (bstnode*) malloc(sizeof(bstnode));

tree->key = key;

return 1;

}

int found = 0;

bstnode* curr = tree;

bstnode* prev = NULL;

while (NULL != curr) {

// if already exists

if (key == curr->key) {

found = 1;

break;

}

prev = curr;

curr = (key > curr->key) ? curr->right : curr->left;

}

if (!found && NULL == curr) {

curr = (bstnode*) malloc(sizeof(bstnode));

curr->key = key;

((key > prev->key) ? prev->right : prev->left) = curr;

return 1;

}

return 0;

}

(3) 删除key:在BST中删除一个key,不能破坏BST的规则

删除操作需要注意:1. 若删除的元素是树根,则需要确保删除之后还能构建成新的树; 2. 按照查找二叉树的特性,按照中序遍历则必定有序,所以在删除某个节点之后可以寻找其中序前去或者后继来替代该节点。

// delete a key from the BST

int delete_key(bstree* &tree, keyType key) {

// 设定临时树根,其key假设为无穷大,防止出现删除root的情况

// 主要作用是让原来树的root可以像其他节点一样参与运算

bstnode* head = (bstnode*) malloc(sizeof(bstnode));

head->left = tree;

bstnode *curr = tree, *prev = head;

bstnode *t1 = NULL, *t2 = NULL;

int found = 0;

while (NULL != curr) {

if (key == curr->key) {

found = 1;

break;

}

prev = curr;

curr = ((key > curr->key) ? curr->right : curr->left);

}

if (found) {

// delete the node with the given key

if (NULL == curr->left) {     // when the left child of the node is NULL

((curr == prev->left) ? prev->left : prev->right) = curr->right;

free(curr);

} else if (NULL == curr->right) { // when the right child of the node is NULL

((curr == prev->left) ? prev->left : prev->right) = curr->left;

free(curr);

} else {     // when the node has two children

// 按照二叉查找树的特性,保持中序遍历顺序即可

// 即用该节点的中序前序后者后继替代该节点即可

t1 = curr->left;

while (NULL != t1->right) {

t2 = t1;

t1 = t1->right;

}

curr->key = t1->key;

((NULL == t2) ? curr->left : t2->right) = t1->left;

free(t1);

}

}

// 还原树根

tree = head->left;

free(head);

return found;

}

测试 操作

[1.      查找树的创建(createTree)假设有如下数组4,1,45,78,345,23,12,3,6,21首先选定4为root,然后遍历剩下的数字,如果大于等于4则放到4的右侧,小于4放到4的左侧,最

int main() {

cout << "插入构建二叉查找树:4 2 5 3 1 6" << endl;

bstree * bst = NULL;

insert_key(bst, 4);

insert_key(bst, 2);

insert_key(bst, 5);

insert_key(bst, 3);

insert_key(bst, 1);

insert_key(bst, 6);

cout << endl;

cout << "遍历二叉查找树 :" << endl << "前序\t : ";

rpre_order(bst);

cout << endl << "中序\t : ";

rin_order(bst);

cout << endl;

cout << endl;

cout << "删除元素 4 : ";

int r = delete_key(bst, 4);

if (r) {

cout << "Success";

} else {

cout << "Failed";

}

cout << endl;

cout << "删除元素 32 : ";

r = delete_key(bst, 32);

if (r) {

cout << "Success";

} else {

cout << "Failed";

}

cout << endl;

cout << endl;

cout << "刪除之后 :" << endl << "前序\t : ";

rpre_order(bst);

cout << endl << "中序\t : ";

rin_order(bst);

cout << endl;

//

destory_btree(bst);

return 0;

}

测试结果:

插入构建二叉查找树:4 2 5 3 1 6

遍历二叉查找树 :

前序 : 4 2 1 3 5 6

中序 : 1 2 3 4 5 6

删除元素 4 : Success

删除元素 32 : Failed

刪除之后 :

前序 : 3 2 1 5 6

中序 : 1 2 3 5 6

1.3 小结

5761747ad67694111efef1530c188fc2.png

对于一颗二叉查找树来说,如果分布较为对称,每次比较都会将查找分为缩减一半,其时间复杂度为 Log2(N), 最坏的情况下,即这个查找树二叉树退化成只有左子树或者右子树的情况下,其复杂度为N .

这样看来,二叉查找树的查找效率和它自身的形态有密切的关系,一颗形态分布匀称的查找二叉树才能发挥出它的真正的效率。

在经过多次插入、删除操作之后,如果不加控制,一颗二叉查找树很有可能退化,这样就大大降低了其查找效率。

2、平衡二叉树

上文说到退化的二叉查找树会降低查找效率,那么有什么办法可以防止二叉查找树退化呢?当然是有的,平衡二叉树(Balenced Binary Tree)就可以很好的解决这个问题。

2.1 定义

从形态上来说,平衡二叉树是一颗形态均匀的二叉树,但这不能当作定义。

对于一颗含有 n 个节点的二叉树,其高度 h 控制在 节点数的对数范围内的都属于平衡     树, 即 h <= O ( log2(n) ) .

平衡二叉树有很多种类,如常见的  AVL树、红黑树 ,下面就对这两种平衡二叉树作简单介绍并实现其算法。

2.2 AVL树

关于AVL树的介绍以及AVL树插入和删除节点代码实现在另一篇博客平衡二叉树 之 AVL树中做了详细叙述。

2.3 Red-Black Tree 红黑树 关于红黑树的介绍以及红黑树 插入和删除节点 代码实现在另一篇博客

3、总结

利用二叉查找树可以实现快速查找的目的,平均情况下其查找时间复杂度为 O(log2(N))。而在动态地对树进行插入删除节点时,一些极端的情况下,二叉查找树会退化成线性,造成查找效率降低。

采用平衡二叉树对 二叉查找树进行 改造,可以保证二叉查找树形态分布均匀 ,从而保证查找效率。

[二叉树中查找算法: /** * @author hbliu * @param data * @return 找到的结点 */ public Node searchNode(int data){ return searchNode(this.root,d

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值