二叉查找树
来源:互联网 发布:办公室新装修 知乎 编辑:程序博客网 时间:2024/06/05 04:14
1:定义
查找树的定义非常简单,一句话就是左孩子比父节点小,右孩子比父节点大,还有一个特性就是”中序遍历“可以让结点有序。
2:树节点
为了具有通用性,我们定义成泛型模板,在每个结点中增加一个”数据附加域”。
1 /// <summary> 2 /// 二叉树节点 3 /// </summary> 4 /// <typeparam name="K"></typeparam> 5 /// <typeparam name="V"></typeparam> 6 public class BinaryNode<K, V> 7 { 8 /// <summary> 9 /// 节点元素10 /// </summary>11 public K key;12 13 /// <summary>14 /// 节点中的附加值15 /// </summary>16 public HashSet<V> attach = new HashSet<V>();17 18 /// <summary>19 /// 左节点20 /// </summary>21 public BinaryNode<K, V> left;22 23 /// <summary>24 /// 右节点25 /// </summary>26 public BinaryNode<K, V> right;27 28 public BinaryNode() { }29 30 public BinaryNode(K key, V value, BinaryNode<K, V> left, BinaryNode<K, V> right)31 {32 //KV键值对33 this.key = key;34 this.attach.Add(value);35 36 this.left = left;37 this.right = right;38 }39 }
3:添加
根据查找树的性质我们可以很简单的写出Add的代码,一个一个的比呗,最终形成的效果图如下
这里存在一个“重复节点”的问题,比如说我在最后的树中再插入一个元素为15的结点,那么此时该怎么办,一般情况下,我们最好
不要在树中再追加一个重复结点,而是在“重复节点"的附加域中进行”+1“操作。
1 #region 添加操作 2 /// <summary> 3 /// 添加操作 4 /// </summary> 5 /// <param name="key"></param> 6 /// <param name="value"></param> 7 public void Add(K key, V value) 8 { 9 node = Add(key, value, node);10 }11 #endregion12 13 #region 添加操作14 /// <summary>15 /// 添加操作16 /// </summary>17 /// <param name="key"></param>18 /// <param name="value"></param>19 /// <param name="tree"></param>20 /// <returns></returns>21 public BinaryNode<K, V> Add(K key, V value, BinaryNode<K, V> tree)22 {23 if (tree == null)24 tree = new BinaryNode<K, V>(key, value, null, null);25 26 //左子树27 if (key.CompareTo(tree.key) < 0)28 tree.left = Add(key, value, tree.left);29 30 //右子树31 if (key.CompareTo(tree.key) > 0)32 tree.right = Add(key, value, tree.right);33 34 //将value追加到附加值中(也可对应重复元素)35 if (key.CompareTo(tree.key) == 0)36 tree.attach.Add(value);37 38 return tree;39 }
4:范围查找
这个才是我们使用二叉树的最终目的,既然是范围查找,我们就知道了一个”min“和”max“,其实实现起来也很简单,
第一步:我们要在树中找到min元素,当然min元素可能不存在,但是我们可以找到min的上界,耗费时间为O(logn)。
第二步:从min开始我们中序遍历寻找max的下界。耗费时间为m。m也就是匹配到的个数。
最后时间复杂度为M+logN,要知道普通的查找需要O(N)的时间,比如在21亿的数据规模下,匹配的元素可能有30个,那么最后
的结果也就是秒杀和几个小时甚至几天的巨大差异,后面我会做实验说明。
1 #region 树的指定范围查找 2 /// <summary> 3 /// 树的指定范围查找 4 /// </summary> 5 /// <param name="min"></param> 6 /// <param name="max"></param> 7 /// <returns></returns> 8 public HashSet<V> SearchRange(K min, K max) 9 {10 HashSet<V> hashSet = new HashSet<V>();11 12 hashSet = SearchRange(min, max, hashSet, node);13 14 return hashSet;15 }16 #endregion17 18 #region 树的指定范围查找19 /// <summary>20 /// 树的指定范围查找21 /// </summary>22 /// <param name="range1"></param>23 /// <param name="range2"></param>24 /// <param name="tree"></param>25 /// <returns></returns>26 public HashSet<V> SearchRange(K min, K max, HashSet<V> hashSet, BinaryNode<K, V> tree)27 {28 if (tree == null)29 return hashSet;30 31 //遍历左子树(寻找下界)32 if (min.CompareTo(tree.key) < 0)33 SearchRange(min, max, hashSet, tree.left);34 35 //当前节点是否在选定范围内36 if (min.CompareTo(tree.key) <= 0 && max.CompareTo(tree.key) >= 0)37 {38 //等于这种情况39 foreach (var item in tree.attach)40 hashSet.Add(item);41 }42 43 //遍历右子树(两种情况:①:找min的下限 ②:必须在Max范围之内)44 if (min.CompareTo(tree.key) > 0 || max.CompareTo(tree.key) > 0)45 SearchRange(min, max, hashSet, tree.right);46 47 return hashSet;48 }
5:删除
对于树来说,删除是最复杂的,主要考虑两种情况。
<1>单孩子的情况
这个比较简单,如果删除的节点有左孩子那就把左孩子顶上去,如果有右孩子就把右孩子顶上去,然后打完收工。
<2>左右都有孩子的情况。
首先可以这么想象,如果我们要删除一个数组的元素,那么我们在删除后会将其后面的一个元素顶到被删除的位置,如图
那么二叉树操作同样也是一样,我们根据”中序遍历“找到要删除结点的后一个结点,然后顶上去就行了,原理跟"数组”一样一样的。
同样这里也有一个注意的地方,在Add操作时,我们将重复元素的值追加到了“附加域”,那么在删除的时候,就可以先判断是
不是要“-1”操作而不是真正的删除节点,其实这里也就是“懒删除”,很有意思。
1 #region 删除当前树中的节点 2 /// <summary> 3 /// 删除当前树中的节点 4 /// </summary> 5 /// <param name="key"></param> 6 /// <returns></returns> 7 public void Remove(K key, V value) 8 { 9 node = Remove(key, value, node);10 }11 #endregion12 13 #region 删除当前树中的节点14 /// <summary>15 /// 删除当前树中的节点16 /// </summary>17 /// <param name="key"></param>18 /// <param name="tree"></param>19 /// <returns></returns>20 public BinaryNode<K, V> Remove(K key, V value, BinaryNode<K, V> tree)21 {22 if (tree == null)23 return null;24 25 //左子树26 if (key.CompareTo(tree.key) < 0)27 tree.left = Remove(key, value, tree.left);28 29 //右子树30 if (key.CompareTo(tree.key) > 0)31 tree.right = Remove(key, value, tree.right);32 33 /*相等的情况*/34 if (key.CompareTo(tree.key) == 0)35 {36 //判断里面的HashSet是否有多值37 if (tree.attach.Count > 1)38 {39 //实现惰性删除40 tree.attach.Remove(value);41 }42 else43 {44 //有两个孩子的情况45 if (tree.left != null && tree.right != null)46 {47 //根据二叉树的中顺遍历,需要找到”有子树“的最小节点48 tree.key = FindMin(tree.right).key;49 50 //删除右子树的指定元素51 tree.right = Remove(key, value, tree.right);52 }53 else54 {55 //单个孩子的情况56 tree = tree.left == null ? tree.right : tree.left;57 }58 }59 }60 61 return tree;62 }
- 查找--二叉查找树
- 二叉树、二叉查找树
- 二叉树 & 二叉查找树
- 【查找结构】二叉查找树
- 查找之二叉树查找
- 查找之二叉树查找
- 查找:二叉查找树总结
- 二叉树查找树...
- 二叉树查找树
- 查找--遍历二叉树
- 二叉查找树
- 二叉查找树实现
- 二叉查找树
- 动态二叉查找树
- 最优二叉查找树
- 二叉查找树
- 二叉查找树
- 平衡二叉查找树
- hdu 1213 How Many Tables
- 利用SE16n 修改数据
- Pixhawk之前期准备
- spark , NoSuchMethodError: ConcurrentHashMap.keySet 和failed to connect to master的错
- 蓝桥杯 字符串转整数
- 二叉查找树
- Notepad++开发JavaScript运行时中文出现乱码的解决方法
- 文本类控件(EditView 的介绍)
- 远程桌面环境Xfce4中Tab键失效的解决方法
- 嵌入式Linux开发学习笔记:编译与调试
- JavaScript判断浏览器类型及版本
- Centos6.5 64+oracle11.2.0.4+rman 搭建物理DG
- Unity之计算环境反射WorldRefl
- 51Nod 1268 和为K的组合(搜索/+回溯)