java-实现哈希表 跳表

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

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

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

 

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

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

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

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

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

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

代码:

import java.sql.PreparedStatement;
import java.util.Hashtable;
import java.util.Map;

/**
 * 拉链发,
 * 负载因子-0.75 自动扩容
 *
 */


public class HashTable {

    //节点类
    class Node{
        private int Key;
        private Object Value;
        private Node next=null;
        
        public Node(int key,Object value){
            this.Key=key;
            this.Value=value;
        }
    }
    //数组大小
    private int size;
    //负载因子
    private float loadFactory;
    //数组
    private Node[] nodes;
    //默认哈希表容量
    private final static int DEFAULT_CAPACITY=16;
    //扩容翻倍数
    private final static int REHASH_BASE=2;
    //默认负责因子
    private final static float DEFAULT_LOAD_FACTOR=0.75f;

    //构造方法
    public HashTable(){
        this.size=0;
        this.loadFactory=DEFAULT_LOAD_FACTOR;
        nodes=new Node[DEFAULT_CAPACITY];
    }
    public HashTable(int capacity){
        this.size=0;
        this.loadFactory=DEFAULT_LOAD_FACTOR;
        this.nodes=new Node[capacity];
    }
    public HashTable(int capacity,float loadFactor){
        this.size=0;
        this.loadFactory=loadFactor;
        this.nodes=new Node[capacity];
    }
    //通过hash值缺定对应下标
    private int getIndex(int key,Node[] n){
        int hash=key^(key>>>16);
        return (n.length-1)&hash;
    }
    //遍历链表
    //返回目标的前驱节点,没有则返回最后一个节点
    private Node getPreNodeInNodes(Node node,int key){
        Node nextNode=node.next;
        while(nextNode!=null){
            if(nextNode.Key==key){
                return node;
            }else{
                node=nextNode;
                nextNode=nextNode.next;
            }
        }
        return node;
    }
    //增删改查
    //增改
    public Object put(int key,Object value){
        if(needReHash()){
            rehash();
        }
        int index=getIndex(key,this.nodes);
        Node root=this.nodes[index];
        if(root==null){
            this.nodes[index]=new Node(key,value);
            this.size++;
            return null;
        }
        if(root.Key==key){
            Object oldvalue=root.Value;
            root.Value=value;
            return oldvalue;
        }else{
            Node preNode=getPreNodeInNodes(root,key);
            Node targetNode=preNode.next;
            if(targetNode!=null){
                Object oldvalue=root.Value;
                targetNode.Value=value;
                return oldvalue;
            }else{//不在链表中返回链表最后一个
                preNode.next=new Node(key,value);
                this.size++;
                return null;
            }
        }
    }
    //
    public Object remove(int key){
        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--;
            return root.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--;
                return targetNode.Value;
            }else{
                return null;
            }
        }
    }
    //
    public Object get(int key){
        int index=getIndex(key,this.nodes);
        Node root=this.nodes[index];
        if(root==null)
            return null;
        if(root.Key==key)
            return root.Value;
        else{
            Node preNode=getPreNodeInNodes(root,key);
            Node targetNode=preNode.next;
            if(targetNode!=null)
                return targetNode.Value;
            else
                return null;
        }
    }

    //是否需要扩容
    private boolean needReHash(){
        if(this.size>this.nodes.length*this.loadFactory)
            return true;
        return false;
    }
    //扩容
    private void rehash(){
        Node[] newNodes=new Node[this.nodes.length*REHASH_BASE];
        for (int i=0;i<this.nodes.length;i++){
            //System.out.println("rehash-"+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]=new Node(curNode.Key,curNode.Value);
                }
                else{
                    while(root.next!=null){
                        root=root.next;
                    }
                    root.next=new Node(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]=new Node(pnext.Key,pnext.Value);
                    }
                    else{
                        while(root1.next!=null){
                            root1=root1.next;
                        }
                        root1.next=new Node(pnext.Key,pnext.Value);
                    }
                    pnext=pnext.next;
                }
            }
        }
        this.nodes=newNodes;
    }
 

    //打印
    private void printTable(){
        System.out.println("哈希表:");
        for(int i=0;i<this.nodes.length;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");
            }else
                System.out.print("null");
            System.out.println();
        }
    }


    public static void main(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节点,这样维护起来更简单。

即:

代码:

import java.util.Random;

/**参考:
 * https://www.cnblogs.com/acfox/p/3688607.html
 * https://blog.csdn.net/pcwl1206/article/details/83512600
 *
 * 查找
 * 插入
 * 删除
 */
public class SkipList {

    private static final int MAX_LEVEL=16;
    private int levelCount=1;
    private Node head=new Node();
    //内部类
    public class Node{
        private int data=-1;
        private Node next[]=new Node[MAX_LEVEL];
        private int maxLevel=0;
    }

    //查找
    public Node find(int data){
        Node p=head;
        for(int i=levelCount-1;i>=0;--i){
            while(p.next[i]!=null&&p.next[i].data<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(int value) {
        int level = randomLevel();
        if (levelCount < level){
            levelCount = levelCount+1;
            level=levelCount;
        }
        Node newNode = new Node();
        newNode.data = value;
        newNode.maxLevel = level;
        Node update[]=new Node[level];
        for(int i=0;i<level;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<value){
                p=p.next[i];
            }
            update[i]=p;
        }
        //再所有所在层插入节点
        for(int i=0;i<level;i++){
            newNode.next[i]=update[i].next[i];
            update[i].next[i]=newNode;
        }
    }

    //删除操作
    public void delete(int data){
        Node[] update=new Node[levelCount];
        Node p=head;
        for(int i=levelCount-1;i>=0;i--){
            while(p.next[i]!=null&&p.next[i].data<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 int randomLevel(){
        Random rand=new Random();
        int level=1;
        for(int i=0;i<MAX_LEVEL;i++){
            int a=rand.nextInt(2);//产生0或者1
            //System.out.println("a="+a);
            if(a==0){
                level++;
            }else
                break;

        }
        System.out.println("l="+level);
        return level;
    }

    //打印跳表
    private void printlist(){
        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 void main(String args[]) {
        SkipList l=new SkipList();
        //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
原文地址:https://www.cnblogs.com/wangpan8721/p/13738829.html