二叉搜索树又称二叉排序树
【二叉搜索树的查找,一般情况下一次可以干掉很多数据】
为了保证每次可以干掉很多数据
思路:取遍历节点为cur,每次要查找的值val和cur的val进行比较,如果要查找的值比cur的val小,则去cur的左边继续查找,如果大则去cur的右边继续查找。
public boolean search(int val) {
TreeNode cur = root;
while (cur != null) {
if (cur.val == val) {
return true;
}else if (cur.val > val) {
cur = cur.left;
}else {
cur = cur.right;
}
}
return false;
}
思路:整体思路与查找的思路类似,但是二叉搜索树的每次插入的节点一定是叶子节点,因此需要记录待插入节点的双亲。
当cur为空时,parent记录了待插入结点的父亲位置,再用val和parent的val比较进行插入。
public void insert (int val) {
Node node = new Node(val);
if (root == null) {
root = node;
return;
}
Node cur = root;
Node parent = root;
while (cur != null) {
if (cur.val == val) {
return;
}else if (cur.val > val) {
parent = cur;
cur = cur.left;
}else {
parent = cur;
cur = cur.right;
}
}
if (parent.val > val) {
parent.left = node;
}else {
parent.right = node;
}
}
设待删除结点为cur,待删除结点的双亲结点为parent
整体看分以下3种大情况:
①cur的左为空
②cur的右为空
③cur的左和右都不为空
继续细分:cur是不是root;cur不是root的话,是parent的左还是parent的右
故有3 + 3 + 1 = 7种情况。
Map是一个独立的接口,而Set继承自Collection接口。
TreeMap 和 TreeSet 都继承了一个Sorted接口,说明 Tree某某 都是经过排序的,即 Tree某某 都是关于key有序的。
Map是一个接口类,该类没有继承collection,该类中存储的是
Map<Integer, Integer> map1 = new HashMap<>();
Map<Integer, Integer> map2 = new TreeMap<>();
Map底层结构 | TreeMap | HashMap |
---|---|---|
底层结构 | 红黑树 | 哈希桶 |
插入/删除/查找时间复杂度 | O(log2N) | O(1) |
是否有序 | 关于key有序 | 无序 |
线程安全 | 不安全 | 不安全 |
插入/删除/查找区别 | 需要进行元素比较 | 通过哈希函数计算哈希地址 |
比较与覆写 | key必须能够比较,否则会抛出类型转换异常 | 自定义类型需要覆写equals和hashcode方法 |
应用场景 | 需要key有序的场景下 | key是否有序不关心,需要更高的时间性能 |
Set
Map<Integer, Integer> map = new TreeMap<>();
map.put(1,2);
map.put(2,3);
map.put(3,6);
//key一定是可以进行比较的
for(Map.Entry<Integer,Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
分析:
打印所有的键值对
entrySet():将Map中的键值对放在Set中返回了。
Set与Map主要的不同有2点:
①Set是继承自Collection的接口类
②Set中只存储了Key
Map不能使用迭代器遍历,但是Set可以,因为Set实现了Iterable接口
Set是继承自Collection的一个接口类
Set中只存储了key,并且要求key一定要唯一
Set最大的功能就是对集合中的元素进行去重
TreeSet中不能插入null的key,但是HashSet可以
Set底层结构 | TreeSet | HashSet |
---|---|---|
底层结构 | 红黑树 | 哈希桶 |
插入/删除/查找时间复杂度 | O(log2N) | O(1) |
是否有序 | 关于key有序 | 不一定有序 |
线程安全 | 不安全 | 不安全 |
插入/删除/查找区别 | 按照红黑树的特性进行插入和删除 | 通过哈希函数计算哈希地址 |
比较与覆写 | key必须能够比较,否则会抛出类型转换异常 | 自定义类型需要覆写equals和hashcode方法 |
应用场景 | 需要key有序的场景下 | key是否有序不关心,需要更高的时间性能 |
Iterator< E> iterator() 返回迭代器,可以利用其进行遍历
Set<Integer> set = new TreeSet<>();
set.add(1);
set.add(3);
set.add(2);
set.add(8);
set.add(4);
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
面试问题:
- 问:如果2个对象hashcode一样,equals一定一样吗?
答:不一定,hashcode一样只能证明我们要找的位置一样,位置一样的下面有很多值,无法确定2个对象的euqals。- 问:如果2个对象equals一样,hashcode一定一样吗?
答:一定,equals一样则hashcode一定一样。
面试题:调用构造方法给了1000,请问最后哈希数组的长度是多少?
答:1024
HashMap的最大容量保证是2的n次方,但是如果没有传入一个2的次方怎么办?
答:HashMap会将传入的参数做校验,返回距离传参最近的一个2的n次方的值,例如传入15会初始化为16。
那么,为什么返回二次幂呢?
分析put源码。
①先把key给到hash函数中,hash(key),调用hash方法,将引用类型key转换成整数类型
②hash这个方法中调用了hashcode方法,如果key重写了hashcode方法则会调用自己的hashcode方法,如果没有重写,则会调用Object类的hashcode方法。
h是通过hashcode方法得到的32位的整数,h 异或上 h右移16位
为什么要右移16位?
答:为了更好的均匀分布,低16位和高16位异或,可以让结果更均匀。
③i = (n - 1) & hash 等价于 n % len,位运算一定是最快的,一定要保证n是2的次幂,这样2个公式才等价。【所以初始化的长度为2的整数次幂】