一,哈希表,将关键字与储存位置通过某种函数关联起来,拿到要查找的对象后,通过关键字得出对象的储存位置,
使得查询时间复杂度为O(1),而不用遍历集合去比较,哈希函数是函数,
存在输入不同key得到相同解的情况,即发生哈希碰撞。理论上只要储存空间有限,必定会发生哈希碰撞。
哈希表的关键:设计哈希函数,和哈希碰撞处理。
实例:用除留余数法哈希函数,和拉链法解决哈希碰撞
参考:https://www.cnblogs.com/xiaoxiongcanguan/p/10190861.html
除留余数:表数组的长度L有限,关键字对L求余,即储存地址都落在0-(L-1)上,
拉链法:当发生哈希碰撞,落在相同储存位置的对象储存再同一个链表上,表头在数组里,这样查找时
先求出,储存地址,在按该地址上的对象链表上找即可。
代码:
importjava.sql.PreparedStatement;importjava.util.Hashtable;importjava.util.Map;/*** 拉链发,
* 负载因子-0.75 自动扩容
**/
public classHashTable {//节点类
classNode{private intKey;privateObject Value;private Node next=null;public Node(intkey,Object value){this.Key=key;this.Value=value;
}
}//数组大小
private intsize;//负载因子
private floatloadFactory;//数组
privateNode[] nodes;//默认哈希表容量
private final static int DEFAULT_CAPACITY=16;//扩容翻倍数
private final static int REHASH_BASE=2;//默认负责因子
private final static float DEFAULT_LOAD_FACTOR=0.75f;//构造方法
publicHashTable(){this.size=0;this.loadFactory=DEFAULT_LOAD_FACTOR;
nodes=newNode[DEFAULT_CAPACITY];
}public HashTable(intcapacity){this.size=0;this.loadFactory=DEFAULT_LOAD_FACTOR;this.nodes=newNode[capacity];
}public HashTable(int capacity,floatloadFactor){this.size=0;this.loadFactory=loadFactor;this.nodes=newNode[capacity];
}//通过hash值缺定对应下标
private int getIndex(intkey,Node[] n){int hash=key^(key>>>16);return (n.length-1)&hash;
}//遍历链表//返回目标的前驱节点,没有则返回最后一个节点
private Node getPreNodeInNodes(Node node,intkey){
Node nextNode=node.next;while(nextNode!=null){if(nextNode.Key==key){returnnode;
}else{
node=nextNode;
nextNode=nextNode.next;
}
}returnnode;
}//增删改查//增改
public Object put(intkey,Object value){if(needReHash()){
rehash();
}int index=getIndex(key,this.nodes);
Node root=this.nodes[index];if(root==null){this.nodes[index]=newNode(key,value);this.size++;return null;
}if(root.Key==key){
Object oldvalue=root.Value;
root.Value=value;returnoldvalue;
}else{
Node preNode=getPreNodeInNodes(root,key);
Node targetNode=preNode.next;if(targetNode!=null){
Object oldvalue=root.Value;
targetNode.Value=value;returnoldvalue;
}else{//不在链表中返回链表最后一个
preNode.next=newNode(key,value);this.size++;return null;
}
}
}//删
public Object remove(intkey){int index=getIndex(key,this.nodes);
Node root=this.nodes[index];if(root==null)return null;if(root.Key==key){this.nodes[index]=root.next;this.size--;returnroot.Value;
}else{
Node preNode=getPreNodeInNodes(root,key);//System.out.println("preNode: "+preNode.Key);
Node targetNode=preNode.next;if(targetNode!=null){
preNode.next=targetNode.next;this.size--;returntargetNode.Value;
}else{return null;
}
}
}//查
public Object get(intkey){int index=getIndex(key,this.nodes);
Node root=this.nodes[index];if(root==null)return null;if(root.Key==key)returnroot.Value;else{
Node preNode=getPreNodeInNodes(root,key);
Node targetNode=preNode.next;if(targetNode!=null)returntargetNode.Value;else
return null;
}
}//是否需要扩容
private booleanneedReHash(){if(this.size>this.nodes.length*this.loadFactory)return true;return false;
}//扩容
private voidrehash(){
Node[] newNodes=new Node[this.nodes.length*REHASH_BASE];for (int i=0;i
Node curNode=this.nodes[i];if(curNode!=null){
Node pnext=curNode.next;int index=getIndex(curNode.Key,newNodes);
Node root=newNodes[index];if(root==null){
newNodes[index]=newNode(curNode.Key,curNode.Value);
}else{while(root.next!=null){
root=root.next;
}
root.next=newNode(curNode.Key,curNode.Value);
}while(pnext!=null){int index1=getIndex(pnext.Key,newNodes);
Node root1=newNodes[index1];//System.out.println(index1);
if(root1==null){
newNodes[index1]=newNode(pnext.Key,pnext.Value);
}else{while(root1.next!=null){
root1=root1.next;
}
root1.next=newNode(pnext.Key,pnext.Value);
}
pnext=pnext.next;
}
}
}this.nodes=newNodes;
}//打印
private voidprintTable(){
System.out.println("哈希表:");for(int i=0;i
System.out.print(i+": ");
Node root=this.nodes[i];if(root!=null){
Node nextNode=root.next;
System.out.print(root.Key+"["+root.Value+"]"+"->");while(nextNode!=null){
System.out.print(nextNode.Key+"["+nextNode.Value+"]"+"->");
nextNode=nextNode.next;
}
System.out.print("null");
}elseSystem.out.print("null");
System.out.println();
}
}public static voidmain(String args[]) {
HashTable table=new HashTable(8);
table.put(45,"qwe");
table.put(23,"qwe");
table.put(18,"qwe");
table.put(6,"qwe");
table.put(27,"qwe");
table.put(34,"qwe");//table.put(3,"qwe");//table.put(7,"qwe");//table.put(22,"qwe");//table.put(49,"qwe");//table.put(57,"qwe");//table.put(38,"qwe");//table.put(33,"qwe");//table.put(29,"qwe");//table.put(28,"qwe");
System.out.println(table.get(88));
table.printTable();//Hashtable h=new Hashtable();
}
}
View Code
二,跳表
参考:https://blog.csdn.net/pcwl1206/article/details/83512600 这边文章分析的很透彻,关于跳表的,性质,原理。
https://www.cnblogs.com/acfox/p/3688607.html
跳表:在单链表的基础上,再维持几条索引,类似二叉树或红黑树那样,从上层往下层找,层层缩小查找范围
但想再插入删除后维持完美的上层索引,同AVL树,RB树一样麻烦。要实现基于单链表实现简单的维护操作(类似维护平衡的操作)
跳表的维护原理是:通过随机函数来维护上层索引。
插入节点时通过随机数给出一个层级N,这个层级即表示把向上N层也插入索引,N的概率:2n分之一。
删除节点时,如果节点出现在索引中,从索引中删除。
代码:
https://blog.csdn.net/pcwl1206/article/details/83512600
搬运的代码,解释一下我理解的
Node 类,有一个数组,该数组的长度为,随机函数产生高度如图:
忘记了,level3包括level2,2333下面图中level2的节点画的太多了,233自行脑补把
红色框表示一个节点,节点高多少既有一个容量为即的数组,直接用数组的好处是省掉了只用单个节点时的child节点,这样维护起来更简单。
即:
代码:
importjava.util.Random;/**参考:
*https://www.cnblogs.com/acfox/p/3688607.html*https://blog.csdn.net/pcwl1206/article/details/83512600*
* 查找
* 插入
* 删除*/
public classSkipList {private static final int MAX_LEVEL=16;private int levelCount=1;private Node head=newNode();//内部类
public classNode{private int data=-1;private Node next[]=newNode[MAX_LEVEL];private int maxLevel=0;
}//查找
public Node find(intdata){
Node p=head;for(int i=levelCount-1;i>=0;--i){while(p.next[i]!=null&&p.next[i].data
p=p.next[i];
}
}if(p.next[0]!=null&&p.next[0].data==data){return p.next[0];
}else
return null;
}// public void insert(intvalue) {int level =randomLevel();if (levelCount
levelCount= levelCount+1;
level=levelCount;
}
Node newNode= newNode();
newNode.data=value;
newNode.maxLevel=level;
Node update[]=newNode[level];for(int i=0;i
update[i]=head;
Node p=head;//找到新节点再所有所在层(level)的前驱节点,
for(int i=level-1;i>=0;i--){while(p.next[i]!=null&&p.next[i].data
p=p.next[i];
}
update[i]=p;
}//再所有所在层插入节点
for(int i=0;i
newNode.next[i]=update[i].next[i];
update[i].next[i]=newNode;
}
}//删除操作
public void delete(intdata){
Node[] update=newNode[levelCount];
Node p=head;for(int i=levelCount-1;i>=0;i--){while(p.next[i]!=null&&p.next[i].data
p=p.next[i];
}
update[i]=p;
}if(p.next[0]!=null&&p.next[0].data==data){for(int i=levelCount-1;i>=0;i--){if(update[i].next!=null&&update[i].next[i].data==data){
update[i].next[i]=update[i].next[i].next[i];
}
}
}
}//随机函数-保证第n层的节点数是总结的数的2的n-1次方分子一//抛硬币,抛最大层数次,连续出现几次正面,就几层
private intrandomLevel(){
Random rand=newRandom();int level=1;for(int i=0;i
if(a==0){
level++;
}else
break;
}
System.out.println("l="+level);returnlevel;
}//打印跳表
private voidprintlist(){
Node p=head;
System.out.println("跳表:");for(int i=levelCount-1;i>=0;i--){
p=head;while (p.next[i]!=null){
System.out.print(p.next[i].data+"->");
p=p.next[i];
}if(p.next[i]==null)
System.out.print("null");
System.out.println();
}
}public static voidmain(String args[]) {
SkipList l=newSkipList();//l.randomLevel();
l.insert(3);
l.insert(5);
l.insert(7);
l.insert(2);
l.insert(9);
l.insert(4);
l.insert(17);//System.out.println("跳表层数:"+l.levelCount);
l.printlist();
System.out.println(l.find(3).maxLevel);
l.delete(5);
l.printlist();
}
}
View Code