自己动手实现java数据结构(六)二叉搜索树
副标题[/!--empirenews.page--]
1.二叉搜索树介绍前面我们已经介绍过了向量和链表。有序向量可以以二分查找的方式高效的查找特定元素,而缺点是插入删除的效率较低(需要整体移动内部元素);链表的优点在于插入,删除元素时效率较高,但由于不支持随机访问,特定元素的查找效率为线性复杂度O(n),效率较低。 向量和链表的优缺点是互补的,那么有没有办法兼具两者的优点呢?这便引出了接下来需要介绍的数据结构——二叉搜索树(Binary Search Tree)。 二叉搜索树和链表类似,同样是以节点为单位存储数据的链式数据结构。二叉搜索树作为一种树形数据结构,内部维护着一个根节点,在插入新数据时,会不断的和当前子树的根节点进行key值的大小比较,较小的key值落在左子树,较大的key值落在右子树,使得二叉搜索树从左到右维持一个有序的状态。 形式化的定义: 二叉搜索树的左子树上结点的值均小于根结点的值;右子树上结点的值均大于根结点的值;二叉搜索树的左、右子树也分别为二叉搜索树。 由于二叉搜索树中的数据是有序存储的,可以使用高效的二分查找查询特定元素;同时由于内部存储结构为链式节点,在插入、删除元素时的效率和链表类似,也十分高效。 可以说,二叉搜索树兼具了向量和链表的优点。
2.二叉搜索树ADT接口二叉搜索树同样是一个存储key/value类型数据结构,因此和哈希表实现共用同一个接口(Map)。K/V数据结构需要暴露出内部节点的Key,value给用户灵活的访问,但哈希表和二叉搜索树的内部节点实现有一定的差异,所以在Map接口中暴露了Map.EntryNode接口,由哈希表和二叉搜索树的内部节点分别实现Map.EntryNode接口。 public interface Map <K,V>{
/**
* 存入键值对
* @param key key值
* value value
* @return 被覆盖的的value值
*/
V put(K key,V value);
* 移除键值对
* 被删除的value的值
V remove(K key);
* 获取key对应的value值
* 对应的value值
V get(K key);
* 是否包含当前key值
* true:包含 false:不包含
*/
boolean containsKey(K key);
* 是否包含当前value值
* value value值
* true:包含 false:不包含
containsValue(V value);
* 获得当前map存储的键值对数量
* 键值对数量
* int size();
* 当前map是否为空
* true:为空 false:不为空
isEmpty();
* 清空当前map
void clear();
* 获得迭代器
* 迭代器对象
Iterator<EntryNode<K,V>> iterator();
* entry 键值对节点接口
* interface EntryNode<K,1)">{
* 获得key值
*
K getKey();
* 获得value值
*
V getValue();
* 设置value值
* */
setValue(V value);
}
}
3.二叉搜索树实现细节3.1?二叉搜索树基本属性值得一提的是,二叉搜索树通过给存储的元素进行排序来加快查询的速度(遍历查询 ---> 二分查询)。 java是面向对象的语言,二叉搜索树中的元素不仅仅是整数、小数。如果说对于整数、小数甚至字符串的排序,我们确定了一个公认的排序逻辑。但是用户自定义的对象,例如小猫、小狗对象的排序可就仁者见仁智者见智了。 由于java并不支持比较符号">","<"的运算符重载,因此我们提供了一个比较排序的接口,用户可以在二叉搜索树初始化时指定排序时元素间比较的逻辑,使得二叉搜索树能以满足用户需求的方式执行排序的逻辑。 比较器接口(Comparator)定义: @FunctionalInterface
interface Comparator<T> {
* 比较方法逻辑
* o1 参数1
* o2 参数2
* 返回值大于0 ---> (o1 > o2)
* 返回值等于0 ---> (o1 = o2)
* 返回值小于0 ---> (o1 < o2)
compare(T o1,T o2);
}
基本属性: class TreeMap<K,V> implements Map<K,1)">{
* 根节点
* private EntryNode<K,1)"> root;
* 比较器(初始化之后,不能改)
* private final Comparator<? super K> comparator;
* 当前二叉树的大小
* size;
* 默认构造函数
* public TreeMap() {
this.comparator = null;
}
* 指定了比较器的构造函数
* public TreeMap(Comparator<? comparator) {
this.comparator = comparator;
}
}
3.2?二叉搜索树内部节点二叉搜索树的内部节点除了必须的key,value字段,同时还维护了左、右孩子节点和双亲节点的引用。 通过实现暴露出去的Map.EntryNode接口,允许用户访问内部节点的key、value值,但二叉搜索树节点内部的孩子、双亲节点的引用是被封装起来的,外部用户是无法感知,也无需了解的。
* 二叉搜索树 内部节点
* static class EntryNode<K,1)">implements Map.EntryNode<K,1)">
* key值
*
K key;
* value值
*
V value;
* 左孩子节点
*
EntryNode<K,1)"> left;
* 右孩子节点
* right;
* 双亲节点
* parent;
EntryNode(K key,V value) {
this.key = key;
this.value = value;
}
EntryNode(K key,V value,EntryNode<K,1)"> parent) {
value;
this.parent = parent;
}
@Override
K getKey() {
return key;
}
@Override
V getValue() {
value;
}
@Override
setValue(V value) {
String toString() {
return key + "=" + value;
}
}
3.3?二叉搜索树 内部辅助函数为了简化代码逻辑以及去除重复代码,在实现过程中提取出了诸如:获取第一个节点(getFirst)、获取节点直接后继(getSuccessor)、获得key值对应目标节点(getTargetEntryNode)等等辅助方法。 getTargetEntryNode用于获取key值对应的目标节点,运用了哨兵的思想。从根节点开始,使用二分查找的方式逐步逼近key值对应目标节点的位置。 如果目标节点确实存在,自然直接返回目标节点的引用(相对位置:RelativePosition.CURRENT); (编辑:我爱故事小小网_铜陵站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |



浙公网安备 33038102330570号