• 算法_散列表


      使用散列的查找算法分为两步,第一步用散列函数将被查找的键转化为数组的一个索引,理想情况下不同的键都被转化为不同的索引值.而当多个键散列到相同的索引值的情况下,就需要处理碰撞冲突,为此有两种方法,拉链法和线性探测法.

      散列函数用于通过键来获取其对应的索引值.好的散列函数应该具有计算简便,等价的键必然产生相等的散列值,均匀的散列所有的键的条件.

      一.基于拉链法的散列表.

      拉链法对于碰撞处理的解决方法是将大小为M的数组中的每个元素都指向一个链表,链表中的每一个节点都存储了散列值为该元素的索引的键值对.查找分两步:首先根据散列值找到对应的链表,然后沿着链表顺序查找相应的键.用M条链表保存N个键,链表的平均长度为N/M.代码实现如下:

    public class SeparateChainingHashST<Key,Value> {
        private int N;            //键值对总数
        private int M;            //散列表大小
        private SequentialSearchST<Key,Value>[] st;
        public SeparateChainingHashST() {
            this(997);
        }
        public SeparateChainingHashST(int M) {
            //创建M条链表.
            this.M=M;
            st=(SequentialSearchST<Key,Value>[])new SequentialSearchST[M];
            for (int i = 0; i < M; i++) {
                st[i]=new SequentialSearchST();//创建链表
            }
        }
        private int hash(Key key) {
            return (key.hashCode()&0x7fffffff)%M;
        }
        public Value get(Key key) {
            return (Value)st[hash(key)].get(key);
        }
        public void put(Key key,Value val) {
            st[hash(key)].put(key, val);
        }
    }
    //链表
    class SequentialSearchST<Key,Value> {
            private Node first;     //链表首节点
            private class Node {
                Key key;
                Value val;
                Node next;
                public Node(Key key,Value val,Node next) {
                    this.key=key;
                    this.val=val;
                    this.next=next;
                }
            }
            public Value get(Key key) {
                for(Node x=first;x!=null;x=x.next) {
                    if(key.equals(x.key)) {
                        return x.val;
                    }
    
                }
                return null;
            }
            public void put(Key key,Value val) {
                for (Node x=first;x!=null;x=x.next) {
                    if(key.equals(x.key)) {
                        x.val=val;
                        return;
                    }
                    first=new Node(key,val,first);
                }
            }
        }

      二.基于线性探测法的散列表

      实现散列表的第二种形式就是用大小为M的数组保存N个键值对,其中M>N.依靠数组的空位来解决碰撞冲突.当碰撞发生的时候,直接检查散列表的下一个位置,并将索引加1.我们用散列函数找到键在数组的索引,判断键是否和被查找的键相同,如果不同则继续查找,直到找到该键或者遇到一个空元素.代码实现如下(其中需要动态的调整数组的大小以提高查找效率)

    public class LinearProbingHashST<Key,Value> {
        private int N;        //符号表中的键值对的总数
        private int M=16;    //线性探测表的大小
        private Key[] keys;    //
        private Value[] vals;//
        public LinearProbingHashST() {
            keys=(Key[]) new Object[M];
            vals=(Value[]) new Object[M];
        }
        public LinearProbingHashST(int x) {
            M=x;
            keys=(Key[]) new Object[M];
            vals=(Value[]) new Object[M];
        }
        private int hash(Key key) {
            return (key.hashCode()&0x7fffffff)%M;
        }
        private void resize(int cap) {
            LinearProbingHashST<Key, Value> t;
            t=new LinearProbingHashST<>(cap);
            for(int i=0;i<M;i++) {
                if(keys[i]!=null)
                    t.put(keys[i], vals[i]);
            }
            keys=t.keys;
            vals=t.vals;
            M=t.M;
        }
        //实现动态的调整数组大小
        public void put(Key key,Value val) {
            if(N>=M/2) resize(2*M);
            int i;
            for(i=hash(key);keys[i]!=null;i=(i+1)%M) {
                if(keys[i].equals(key)) {
                    vals[i]=val;
                    return ;
                }
            }
            keys[i]=key;
            vals[i]=val;
            N++;
        }
        public Value get(Key key) {
            for(int i=hash(key);keys[i]!=null;i=(i+1)%M) {
                if(keys[i].equals(key))
                    return vals[i];
            }
            return null;
        }
        /*
         * 删除元素不能直接设为null
         * 因为会影响到后续元素的查找,方法是将后面的元素重新插入数组.
         * */
        public void delete(Key key) {
            if(!contains(key)) return;
            int i=hash(key);
            while(!key.equals(keys[i]))
                i=(i+1)%M;
            keys[i]=null;
            vals[i]=null;
            i=(i+1)%M;
            while(keys[i]!=null) {
                Key keyToRedo=keys[i];
                Value valToRedo=vals[i];
                keys[i]=null;
                vals[i]=null;
                N--;
                put(keyToRedo,valToRedo);
                i=(i+1)%M;
            }
            N--;
            if(N>0&&N==M/8) resize(M/2);
        }
        public boolean contains(Key key) {
            for(Key okey:keys) {
                if(key.equals(okey))
                    return true;
            }
            return false;
        }
    }
  • 相关阅读:
    子类构造函数 supper关键字
    匿名内部类
    IK 分词器 源码分析
    java重写equals方法
    java编程思想
    设置centos7默认运行级别
    centos7配置wordpress
    python安装tkinter
    centos 7编译安装apache
    关于python中带下划线的变量和函数 的意义
  • 原文地址:https://www.cnblogs.com/hlhdidi/p/5677432.html
Copyright © 2020-2023  润新知