红黑树
定义:红黑树是一颗二分搜索树,并保证自己是一颗平衡二叉树
红黑树性质:
2 - 3树
满足二分搜索树的基本性质
节点可以存放一个元素或两个元素
每个节点可以有两个孩子或三个孩子
二三树是一颗绝对平衡的树
二三树添加节点,永远不会添加到一个空的节点,当某一个节点为4节点时,就会分裂成一个三个节点的树。如果分裂后的树不是一颗绝对平衡二叉树,则将中间节点向上融合,形成:
红黑树和二三树是等价的
引入红色节点和父亲节点一起表示原来的三节点
所有的红色节点是向左倾斜的
红黑树的增删改查的时间复杂度是O(logN)
当我们向一颗树中添加元素,可能需要进行左旋转,让其对应三节点
比添加的三节点的右节点还要大的情况
右旋转
左旋转相当于如果添加的元素在二三树二节点的右边,则需要进行左旋转把他变成左边为红子树,右边为黑子树的情况ho时是三节点。
颜色翻转相当于添加在一个三节点的右边,此时需要把节点变成一个二叉树,中间的节点向上抽离,所以需要进行颜色翻转,变成左右子树为黑色,头结点为红色。
右旋转为当节点添加在一个三节点的最左边,则需要进行右旋转,并将node.color赋值给x.color
哈希表
可以对一个素数进行取模
java中hash的设置:
@Override
public int hashCode(){
int B = 31;
int hash = 0;
hash = hash * B + ((Integer)grade).hashCode();
hash = hash * B + ((Integer)cls).hashCode();
hash = hash * B + firstName.toLowerCase().hashCode();
hash = hash * B + lastName.toLowerCase().hashCode();
return hash;
}
链地址法
hash表的本质是数组
对于hash冲突的键,可以对于冲突的地方使用链表或红黑树treeMap数据结构的方法链接解决。
hash表的性能得到了极大的优化,但是牺牲了顺序性。
hash表:
public class HashTable<K, V> {
private final int[] capacity
= {53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741};
private static final int upperTol = 10;
private static final int lowerTol = 2;
private int capacityIndex = 0;
//红黑树底层,直接使用就行
private TreeMap<K, V>[] hashtable;
private int size;
private int M;
public HashTable(){
this.M = capacity[capacityIndex];
size = 0;
hashtable = new TreeMap[M];
//一个treeMap下有多个treeMap
for(int i = 0 ; i < M ; i ++)
hashtable[i] = new TreeMap<>();
}
private int hash(K key){
return (key.hashCode() & 0x7fffffff) % M;
}
public int getSize(){
return size;
}
public void add(K key, V value){
TreeMap<K, V> map = hashtable[hash(key)];
if(map.containsKey(key))
map.put(key, value);
else{
//新增
map.put(key, value);
size ++;
//可能需要进行扩容
if(size >= upperTol * M && capacityIndex + 1 < capacity.length){
capacityIndex ++;
resize(capacity[capacityIndex]);
}
}
}
public V remove(K key){
V ret = null;
TreeMap<K, V> map = hashtable[hash(key)];
if(map.containsKey(key)){
ret = map.remove(key);
size --;
//可能需要进行缩容
if(size < lowerTol * M && capacityIndex - 1 >= 0){
capacityIndex --;
resize(capacity[capacityIndex]);
}
}
return ret;
}
private void resize(int newM) {
TreeMap<K,V>[] newHashTable = new TreeMap[newM];
for (int i = 0;i < newM;i ++){
newHashTable[i] = new TreeMap<>();
}
int oldM = M;
this.M = newM;
for (int i = 0;i < oldM;i ++){
TreeMap<K,V> map =hashtable[i];
for (K key:map.keySet()){
newHashTable[(hash(key))].put(key,map.get(key));
}
}
this.hashtable = newHashTable;
}
public void set(K key, V value){
TreeMap<K, V> map = hashtable[hash(key)];
if(!map.containsKey(key))
throw new IllegalArgumentException(key + " doesn't exist!");
map.put(key, value);
}
public boolean contains(K key){
return hashtable[hash(key)].containsKey(key);
}
public V get(K key){
return hashtable[hash(key)].get(key);
}
}
其他处理hash冲突的方法
开放地址法
每一个地址对所有元素都是开放的
总结
线性表表示数据是按照线性排列的