泛型
-
自定义类型的TreeMap和TreeSet的要求:要么类型是Comparable,要么构造时传入Comparator
-
泛型类/泛型方法
-
把握E…是一个类型变量,有其作用域,类名称后面的是定义类型变量,其余后面都是使用
-
谁是谁的父类问题
List不是List的父类
List<?>是List的父类 -
泛型是编译期间的工作
-不能使用静态类或者静态方法
哈希表
-
构建步骤:
1.内部数据结构是数组
2.关键字经过变换(hash函数)得到int类型的值
3.int类型的值变成一个合法的数组下标
4.把关键字放入数组的该下标位置 -
哈希冲突/哈希碰撞:对于不同的key,经过哈希后,找到了相同的下标
-
负载因子=填入表中的元素个数 / 数组长度
-
尽量避免哈希冲突,减少冲突的方法:
1.哈希函数的设计,(尽可能的让出来的下标均匀分布)
2.负载因子的调节:
负载因子越少,冲突率越低
数据个数不能动,所以吧数组长度扩大
事先规定好一个值,来控制resize -
解决冲突的方法:闭散列 开散列
【1】闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以 把key存放到冲突位置中的“下一个” 空位置中去。
1.线性探测 2.二次探测法
【2】开散列:又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子 集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。哈希桶 -
试题:求查找成功的平均查找长度和查找失败的平均查找长度
-
时间复杂度为O(1).
//哈希表key-value模型
public class HashBucket {
private static class Node{
private int key;
private int val;
Node next;
public Node(int ket,int val){
this.key=key;
this.val=val;
}
}
private Node[]array;
private int size;
private static final double LOAD_FACTOR=0.75;
public HashBucket() {
array=new Node[8];
size=0;
}
//get
public int get(int key){
int index=key%array.length;
for(Node cur=array[index];cur!=null;cur=cur.next){
if(key==cur.key)
return cur.val;
}
return -1;
}
//put
public int put(int key,int val){
// key => int
// int 合法的下标
int index=key%array.length;
// 2. 在链表中查找 key 所在的结点
// 如果找到了,更新
// 所有结点都不是 key,插入一个新的结点
for(Node cur=array[index];cur!=null;cur=cur.next){
if(key==cur.key){
int oldval=cur.val;
cur.val=val;
return oldval;
}
}
//没找到就作头插
Node node=new Node(key,val);
node.next=array[index];
array[index]=node;
size++;
if(loadfactor()>LOAD_FACTOR){
resize();
}
return -1;
}
/*扩容 需要考虑到 重新哈希 因为数组长度已经发生改变
两层循环 外层遍历数组 内层遍历链表
*/
private void resize() {
Node []newarray=new Node[array.length*2];
for(int i=0;i<array.length;i++){
Node next;
for(Node cur=array[i];cur!=null;cur=next){
next=cur.next;
int index=cur.key%newarray.length;
cur.next=newarray[index];
newarray[index]=cur;
}
}
array=newarray;
}
//负载因子
private double loadfactor(){
return size*1.0/array.length;
}
public static void main(String[] args) {
}
}