• 算法_符号表


      符号表是一种存储键值对的数据结构,支持两种操作:插入(put),即将一组新的键值对存入表中;查找(get):即根据指定的键得到相应的值.

      实现的原则:

      每个键只对应一个值,表中不允许存在重复的键

      当用例代码向表中存入的键值对和表中已有的键(及关联的值)冲突时新的值会替代旧的值.

      而对于符号表来说,如果保持键的有序性,可以大大的扩展它的API,根据键的相对位置作出更多有用的操作.这种抽象的数据结构又被称为有序符号表.

      可以通过链表来实现无序符号表,代码如下:

      

    public 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);
            }
        }
    }

      在这种实现中,未命中(不存在指定的键)的查找和插入操作都需要N次比较.命中的查找在最坏的情况下需要N次比较.特别的,向一个空表中,插入N个不同的键需要N2/2次比较.

      接下来通过一对平行的数组实现有序符号表.实现的核心是rank方法.它返回表中小于给定键的数量.由于使用二分查找可以大大减少每次查找的时候所需要的比较次数,因此采用二分查找来作为rank方法的基本实现.下面是代码:

    public class BinarySearchST<Key extends Comparable<Key>,Value> {
        private static final int INIT_CAPACITY = 2;
        private Key[] keys;
        private Value[] vals;
        private int N = 0;
    
        // create an empty symbol table with default initial capacity
        public BinarySearchST() { this(INIT_CAPACITY); }
    
        // create an empty symbol table with given initial capacity
        public BinarySearchST(int capacity) {
            keys = (Key[]) new Comparable[capacity];
            vals = (Value[]) new Object[capacity];
        }
    
    
        private void resize(int capacity) /*动态的调整数组大小*/{
            assert capacity >= N;
            Key[]   tempk = (Key[])   new Comparable[capacity];
            Value[] tempv = (Value[]) new Object[capacity];
            for (int i = 0; i < N; i++) {
                tempk[i] = keys[i];
                tempv[i] = vals[i];
            }
            vals = tempv;
            keys = tempk;
        }
    
    
        // 是否包含指定的键
        public boolean contains(Key key) {
            return get(key) != null;
        }
    
        // 符号表所有的键的数量
        public int size() {
            return N;
        }
    
        // 符号表是否为空
        public boolean isEmpty() {
            return size() == 0;
        }
    
        //返回键对应的值,如果没有则返回null
        public Value get(Key key) {
            if (isEmpty()) return null;
            int i = rank(key);
            if (i < N && keys[i].compareTo(key) == 0) return vals[i];
            return null;
        }
    
        // 采用二分查找法,获取key的位置
        public int rank(Key key) {
            int lo = 0, hi = N-1;
            while (lo <= hi) {
                int m = lo + (hi - lo) / 2;
                int cmp = key.compareTo(keys[m]);
                if      (cmp < 0) hi = m - 1;
                else if (cmp > 0) lo = m + 1;
                else return m;
            }
            return lo;
        }
    
    
        //寻找键,找到修改值,没有找到则新创建一个键值对.
        public void put(Key key, Value val)  {
            if (val == null) { delete(key); return; }
    
            int i = rank(key);
    
            // 键以及存在于符号表
            if (i < N && keys[i].compareTo(key) == 0) {
                vals[i] = val;
                return;
            }
    
            // 插入键值对
            if (N == keys.length) resize(2*keys.length);
    
            for (int j = N; j > i; j--)  {
                keys[j] = keys[j-1];
                vals[j] = vals[j-1];
            }
            keys[i] = key;
            vals[i] = val;
            N++;
    
            assert check();
        }
    
    
        // 移除键
        public void delete(Key key)  {
            if (isEmpty()) return;
    
            // 获取位置
            int i = rank(key);
    
            // 不在符号表中
            if (i == N || keys[i].compareTo(key) != 0) {
                return;
            }
    
            for (int j = i; j < N-1; j++)  {
                keys[j] = keys[j+1];
                vals[j] = vals[j+1];
            }
    
            N--;
            keys[N] = null;
            vals[N] = null;
    
            // resize if 1/4 full
            if (N > 0 && N == keys.length/4) resize(keys.length/2);
    
            assert check();
        }
    
        // 删除最小的键对应的值
        public void deleteMin() {
            if (isEmpty()) throw new RuntimeException("Symbol table underflow error");
            delete(min());
        }
    
        // 删除最大的键对应的值
        public void deleteMax() {
            if (isEmpty()) throw new RuntimeException("Symbol table underflow error");
            delete(max());
        }
    
        //最小的键
        public Key min() {
            if (isEmpty()) return null;
            return keys[0];
        }
        //最大的键
        public Key max() {
            if (isEmpty()) return null;
            return keys[N-1];
        }
    }

      对于N个键的有序数组中进行二分查找最多需要(lgN+1)次比较,向大小为N的有序数组中插入一个新的元素在最坏的情况下,需要访问~2N次数组,因此一个空符号表中插入N个元素在最坏情况下需要访问~N2次数组.

  • 相关阅读:
    virtualenv+pyenv管理python工作环境
    OpenStack 安装:neutron服务
    OpenStack 安装:nova服务
    node.js工具 nodemon使用
    js的数据类型及数据类型检测
    Git 工具
    Object.prototype.toString.call() 、 instanceof 以及 Array.isArray()判断数组的方法的优缺点
    重绘和回流及优化
    jsapi 调起微信支付的的踩坑
    总结Jquery中获取自定义属性使用.attr()和.data()以及.prop()的区别
  • 原文地址:https://www.cnblogs.com/hlhdidi/p/5649636.html
Copyright © 2020-2023  润新知