二叉查找树
来源:互联网 发布:中国旅游服务贸易数据 编辑:程序博客网 时间:2024/06/02 03:01
不知道为什么,之前写的二叉查找树被CSDN删掉了,往上搜了一下好像好挺多这种情况,不管了,就当是为了学习吧,再写一遍好了。
二叉查找树
什么是二叉查找树
二叉查找树是一种特殊的二叉树,之所以特殊是因为它的查找效率很高,而查找效率高的原因是对节点的组织方式遵从下面的两条规则:
1. 所有的父节点的左子树中的元素的key小于父节点的key
2. 所有的父节点的右子树中的结点的key大于父节点的key
查找
既然称之为查找树,先来说说它是如何查找到一个key所在的节点的。
简单的说就是从树根开始,如果要查找key大于根的key值,则在它的又子树中找,否则就在左子树中找,这样下来,数有多高,查找的最坏情况就是多少,所以查找的时间复杂度为:O(h).
Node *tree_search(Node* tree, int key){ Node *x = tree; while(x && x->key != key) { if(key > x->key) x = x->right; else x = x->left; } return x;}
二叉树的最大元素和最小元素
最大元素和最小元素分别是树的最右侧元素和最左侧元素:
最小元素:
Node* tree_minimum(Node* node){ while(node->left) node = node->left; return node;}
最大元素:
Node* tree_maximum(Node* node){ while(node->right) node=node->right; return node;}
树的遍历
树的遍历有三种方式:
- 先输出左儿子,再输出父节点,最后输出右儿子,叫中序遍历。
- 先输出父节点,再输出左儿子,最后输出右儿子,叫前序遍历。
- 先输出左儿子,再输出右儿子,最后输出父节点,叫后续遍历。
分别为:
中序遍历:
void tree_inorder_walk(Node * tree){ if(!tree) return; tree_inorder_walk(tree->left); PRINT tree tree_inorder_walk(tree->right);}
前序遍历:
void tree_inorder_walk(Node * tree){ if(!tree) return; PRINT tree tree_inorder_walk(tree->left); tree_inorder_walk(tree->right);}
后序遍历:
void tree_inorder_walk(Node * tree){ if(!tree) return; tree_inorder_walk(tree->left); tree_inorder_walk(tree->right); PRINT tree}
前驱和后继
前驱和后继的概念非常简单,在进行中序遍历时,一个节点的紧邻前一个节点就是前驱节点,后一个节点就是后继节点。中序遍历总是先输出小数再输出大数,所以是以从小到大的顺序输出的。也就是说一个节点的前驱就是比它小的第一个数,后驱就是比它大的第一个数。
在一个二叉树中,对于一个节点来说,谁在它后面输出谁就是后继。
1. 如果该节点有右子树,右子树种最小者(使用tree_minimum操作)是后继。
2. 如果没有右子树, 下一个要输出就是它的祖先节点中的一个,就是向上找,直到找到一个向右拐的叉时停下来,此时第一个向右拐的节点就是它的后继。对于一个树的右子数的最右边节点,一直向上找都不会找到一个向右拐的结点,所以就不存在后继。其实这个节点是最后一个输出的,没有人是它的后继。同理,最小的那个节点同样没有前驱。
对于一个节点,谁在它前面输出,谁就是它的前驱,所以第一个输出的节点没有前驱。
1. 如果节点有左子树,则左子树中最大的那个就是这个节点的前驱。
2. 如果没有左子树,下一个要输出的就是它的祖先节点中的一个,向上找,找到一个向左拐的结点,就是它的前驱。
算法代码如下:
找到一个节点的前驱:
Node *tree_precessor(Node *node){ if(node->left) return tree_maximum(node->left); Node *p = node->p; while(p && p->left == node) { node = p; p = node->p; } return p;}
找到一个节点的后继:
Node *tree_successor(Node *node){ if(node->right) return tree_minimum(node); Node *p = node->p; while(p && p->right == node) { node = p; p = node->p; } return p;}
树的插入
要把一个节点插入到树中,先要找到它所在的位置,这个位置满足儿二叉查找树的性质。
1. 找到它的父节点位置
2. 根据key与父节点key的关系,选择插入
代码如下:
Node *tree_insert(Node* tree, Node *node){ Node *x = node; Node *p = NULL; while(x) { p = x; if(node->key > p->key) x = p->right; else x = p->left; } node->right = node->left = NULL; node->p = p; if(!p) { return node; } else { if(p->key > node->key) p->left = node; else p->right = node; } return tree;}
删除
节点的删除稍微要分情况一下:
1. 当要删除的结点没有子树时,直接给它父节点的响应指向赋值NULL即可。
2. 当要删除的结点只有一个子树时, 把这个子树链接到父节点上即可。
3. 当要删除的结点同时有左右子树时,把它的后继节点替换到要删除节点,然后删除它的后继节点。
所以应该按照下面的思路执行删除:
1. 找到要删除的节点:要删除节点 或 它的后继节点。
2. 删除该节点。
3. 如果删除的结点不是原来要删除节点,则把删除的结点的key替换到要删除节点那里。
代码如下:
Node* tree_delete(Node* tree, Node* node){ Node *d = NULL; if(node->left == NULL || node->right == NULL) d = node; else d = tree_successor(node); Node *n == NULL; if(d->left == NULL) n = d->right; else n = d->left; if( n != NULL) n->p = d->p; if(d->p == NULL) return n; else if(d->p->left == d) d->p->left = n; else d->p->right = n; if(d != node) node->key = d->key; return tree;}
到这里二叉查找树的基本操作就介绍完了。
二叉查找树的缺点
二叉查找树在查找效率上比较高,但是如果插入的元素按递增或递减的顺序插入,那么整棵树就只有一条腿,插入多少就有多高,这样下来查找的效率还不如链表来的快,也就是说这棵树没有自平衡的能力,下一节的 红黑树 很好的解决了这个问题。
最后,麻烦csdn别再删除我写的博客了,至少删的话给个原因再删啊,弄的莫名其妙的。
- 查找--二叉查找树
- 二叉树、二叉查找树
- 二叉树 & 二叉查找树
- 【查找结构】二叉查找树
- 查找之二叉树查找
- 查找之二叉树查找
- 查找:二叉查找树总结
- 二叉树查找树...
- 二叉树查找树
- 查找--遍历二叉树
- 二叉查找树
- 二叉查找树实现
- 二叉查找树
- 动态二叉查找树
- 最优二叉查找树
- 二叉查找树
- 二叉查找树
- 平衡二叉查找树
- 第五章 继承
- 字符串匹配, Zenefits的面试题
- JVM-4-JVM类加载器总结及自定义类加载器
- C#实现的事务
- Hibernate查询,返回new对象(注意这个新定义的类要有构造函数),使用sql带条件分页查询并且把结果显示到一个对象的集里面的解决方案
- 二叉查找树
- jsp连接mysql数据库
- 用Maven创建Mahout项目
- 鸟哥Linux—第五章、首次登入与在线求助 man page
- 冒泡排序
- ZOJ 2833-Friendship
- Java String之String和CharSequence、StringBuilder和StringBuffer的区别
- LeetCode 099 Recover Binary Search Tree
- Linux C/C++ 模板:主模板、完全特化