目录
Map和Set详解
Map:一种键值对结构,hashMap中键和值均可以为空,hashTable中则不可以存放null值
Set:一种集合,不能存放重复元素,可以理解为与map中的键的集合。
Map 和 set 是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关 。在Java中Map和Set最常见到下面四个实现类,HashMap/TreeMap/HashSet/TreeSet,他们分别与两种数据结构相关,二叉搜索树和哈希表,下面的文章中我会详解这两种数据结构,以及Java是怎样对这两种数据结构进行实现的。
1.二叉搜索树
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
如下图所示:
二叉搜索树的模拟实现:
public class BinarySearchTree {
public static class Node {
int key;
Node left;
Node right;
public Node(int key) {
this.key = key;
}
}
private Node root = null;
/**
* 在搜索树中查找 key,如果找到,返回 key 所在的结点,否则返回 null
* @param key
* @return
*/
public Node search(int key) {
Node cur = root;
while (cur != null) {
if (key == cur.key) {
return cur;
} else if (key < cur.key) {
cur = cur.left;
} else {
cur = cur.right;
}
}
return null;
}
/**
* 插入
* @param key
* @return true 表示插入成功, false 表示插入失败
*/
public boolean insert(int key) {
if (root == null) {
root = new Node(key);
return true;
}
Node cur = root;
Node parent = null;
while (cur != null) {
if (key == cur.key) {
return false;
} else if (key < cur.key) {
parent = cur;
cur = cur.left;
} else {
parent = cur;
cur = cur.right;
}
}
Node node = new Node(key);
if (key < parent.key) {
parent.left = node;
} else {
parent.right = node;
}
return true;
}
/**
* 删除成功返回 true,失败返回 false
* @param key
* @return
*/
public boolean remove(int key) {
return delete(root,key);
}
private boolean delete(Node root,int key){
/*
根据cur的孩子是否存在分四种情况
1. cur左右孩子均不存在
2. cur只有左孩子
3. cur只有右孩子
4. cur左右孩子均存在
看起来有四种情况,实际情况1可以与情况2或者3进行合并,只需要处理是那种情况即可
除了情况4之外,其他情况可以直接删除
情况4不能直接删除,需要在其子树中找一个替代节点进行删除
*/
Node cur = root;
Node parent = null;
while (cur != null) {
if (key == cur.key) {
break;
} else if (key < cur.key) {
parent = cur;
cur = cur.left;
} else {
parent = cur;
cur = cur.right;
}
}
// 该元素不在二叉搜索树中
if(null == cur){
return false;
}
//由于二叉查找树的性质,如果将当前节点替换为左子树中最大的或者右子树中最小的一定不会破坏二叉查找树的结构。
if((cur.left!=null&&cur.right==null)||(cur.left==null&&cur.right==null)){
cur=cur.left;
}else if(cur.left==null&&cur.right!=null){
cur=cur.right;
}else{
int val=SearchleftMax(cur.left);
cur.key=val;
delete(cur.left,val);
}
return true;
}
private int SearchleftMax(Node left) {
if(left==null){
return 0;
}
if(left.right==null){
return left.key;
}
return SearchleftMax(left.right);
}
}
性能分析:
二叉搜索树的插入和删除操作都离不开查找,所以主要看二叉搜索树的查找效率。
二叉搜索树的查找效率与树的高度有关,结点越深,所需要的比较次数越多。对于同一个数的集合来说,关键码的插入次序不同也会得到不同结构的二叉搜索树。
- 最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log2N
- 最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2
可以看到如果退化为单支树,性能就会下降非常多。为了保证二叉搜索树的效率,大佬们还发明AVL树和红黑树,来限制树的高度尽量趋近于完全二叉树。