实现哈希表 java_java-实现哈希表 跳表

一,哈希表,将关键字与储存位置通过某种函数关联起来,拿到要查找的对象后,通过关键字得出对象的储存位置,

使得查询时间复杂度为O(1),而不用遍历集合去比较,哈希函数是函数,

存在输入不同key得到相同解的情况,即发生哈希碰撞。理论上只要储存空间有限,必定会发生哈希碰撞。

d1d40312884690e51e6a09703a7d3ac0.png

哈希表的关键:设计哈希函数,和哈希碰撞处理。

实例:用除留余数法哈希函数,和拉链法解决哈希碰撞

参考:https://www.cnblogs.com/xiaoxiongcanguan/p/10190861.html

除留余数:表数组的长度L有限,关键字对L求余,即储存地址都落在0-(L-1)上,

拉链法:当发生哈希碰撞,落在相同储存位置的对象储存再同一个链表上,表头在数组里,这样查找时

先求出,储存地址,在按该地址上的对象链表上找即可。

代码:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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

跳表:在单链表的基础上,再维持几条索引,类似二叉树或红黑树那样,从上层往下层找,层层缩小查找范围

7f2f08cc476a9aa21388b9114915d265.png

但想再插入删除后维持完美的上层索引,同AVL树,RB树一样麻烦。要实现基于单链表实现简单的维护操作(类似维护平衡的操作)

跳表的维护原理是:通过随机函数来维护上层索引。

cbeeeb84e296a464c35bad6c8f56baa0.png

插入节点时通过随机数给出一个层级N,这个层级即表示把向上N层也插入索引,N的概率:2n分之一。

删除节点时,如果节点出现在索引中,从索引中删除。

代码:

https://blog.csdn.net/pcwl1206/article/details/83512600

搬运的代码,解释一下我理解的

Node 类,有一个数组,该数组的长度为,随机函数产生高度如图:

忘记了,level3包括level2,2333下面图中level2的节点画的太多了,233自行脑补把

8e0dba86f80e8758a17e0568dc4a1053.png

红色框表示一个节点,节点高多少既有一个容量为即的数组,直接用数组的好处是省掉了只用单个节点时的child节点,这样维护起来更简单。

即:

73a637653445484e3fe29697c7035768.png

代码:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值