算法_符号表

  符号表是一种存储键值对的数据结构,支持两种操作:插入(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次数组.

时间: 2024-08-29 09:50:22

算法_符号表的相关文章

算法实例_线性表 By:比方

算法实例_线性表 By:比方 什么是线性表? 从线性表的功能逻辑上来看,线性表就是由n(n>=0)个数据元素的排序组合,数据由x1,x2,x3,...,xn结构有序的顺序排列. 线性表的结构和特点 1.              仅有一个开始节点x1,没有直接前趋节点,有妾只有一个直接后续节点x2: 2.              仅有一个终结节点xn,仅有一个前趋节点xn-1; 3.              对于同一个线性表,其中没一个数据的元素,都必须具备相同的数据结构类型, 且没一个元素

《 常见算法与数据结构》符号表ST(3)——二叉查找树 (附动画)

符号表(3)--二叉查找树 本系列文章主要介绍常用的算法和数据结构的知识,记录的是<Algorithms I/II>课程的内容,采用的是"算法(第4版)"这本红宝书作为学习教材的,语言是java.这本书的名气我不用多说吧?豆瓣评分9.4,我自己也认为是极好的学习算法的书籍. 通过这系列文章,可以加深对数据结构和基本算法的理解(个人认为比学校讲的清晰多了),并加深对java的理解. 符号表3二叉查找树 二叉查找树 1 代码框架 2 节点表示 3 取得操作 4 插入操作 5 动

浅谈算法和数据结构: 六 符号表及其基本实现

http://www.cnblogs.com/yangecnu/p/Introduce-Symbol-Table-and-Elementary-Implementations.html 浅谈算法和数据结构: 六 符号表及其基本实现 前面几篇文章介绍了基本的排序算法,排序通常是查找的前奏操作.从本文开始介绍基本的查找算法. 在介绍查找算法,首先需要了解符号表这一抽象数据结构,本文首先介绍了什么是符号表,以及这一抽象数据结构的的API,然后介绍了两种简单的符号表的实现方式. 一符号表 在开始介绍查找

算法-符号表的实现(顺序查找和二分查找)

符号表是一种存储键值对的数据结构,支持两种操作插入和查找,就是将一组新的键值对存入表中然后根据给定的键得到对应的值,在编程语言中常用Dictionary原理类似.符号表是一种典型的抽象数据结构,在生活之中应用的场景也很多,可以根据钥匙开门,域名解析的时候的IP地址查询,字典和图书的简介和页数,key和value是密不可分的,通过key我们可以很快找到我们需要的value. 无序链表的顺序查找 主要通过Node节点存储数据,之前的博客中有关于链表的实现,详情可参考之前的博客,代码有注释就解释太多了

Symbol Table(符号表)

一.定义 符号表是一种存储键值对的数据结构并且支持两种操作:将新的键值对插入符号表中(insert):根据给定的键值查找对应的值(search). 二.API 1.无序符号表 几个设计决策: A.泛型 在设计方法时没有指定处理对象,而是使用了泛型. 并且将键(Key)和值(Value)区分开来. B.重复键的处理 规则: 每个值(Value)都只对应一个键(Key)(即不允许存在重复的键). 当用例代码向表中存入的键值对和表中的已有的键(以及关联的值)冲突时,新的值替代旧的值. C.Null 键

扩充C0文法编译器开发笔记(一)符号表

零.简介 这是一个编译大作业. 一.扩充C0文法 文法包括 常量说明和定义.变量说明和定义.无返回值函数定义和调用.有返回值函数定义和调用.条件语句.while循环语句.情况语句.赋值语句.返回语句.读语句.写语句,支持加减乘除四则运算.整数比较运算.包含一维数组.不包含实型.不包含for循环语句.程序由main方法进入.具体文法见下: 1 <加法运算符> ::= +|- 2 <乘法运算符> ::= *|/ 3 <关系运算符> ::= <|<=|>|&

算法_散列表

使用散列的查找算法分为两步,第一步用散列函数将被查找的键转化为数组的一个索引,理想情况下不同的键都被转化为不同的索引值.而当多个键散列到相同的索引值的情况下,就需要处理碰撞冲突,为此有两种方法,拉链法和线性探测法. 散列函数用于通过键来获取其对应的索引值.好的散列函数应该具有计算简便,等价的键必然产生相等的散列值,均匀的散列所有的键的条件. 一.基于拉链法的散列表. 拉链法对于碰撞处理的解决方法是将大小为M的数组中的每个元素都指向一个链表,链表中的每一个节点都存储了散列值为该元素的索引的键值对.

搜索(1):符号表

符号表的概念 符号表的顺序搜索 1 基于有序数组的符号表 2 基于无序链表的符号表 参考资料 1. 符号表的概念 搜索:我们把处理的数据划分为记录或数据项(item),每个数据项都有一个用于搜索的关键字(key).搜索的目标是找出目标关键字所匹配的数据项.搜索的目的是访问这个数据项(不仅是关键字)中的信息. 符号表:它是一种数据结构,其中数据项含有关键字.它支持两个基本的操作:插入一个新的数据项和搜索返回给定关键字的数据项.(字典) 如何处理符号表中相同关键字? 在符号表的实现中应注意考虑可能楚

《Algorithms 4th Edition》读书笔记——3.1 符号表(Elementary Symbol Tables)-Ⅲ

3.1.3 用例举例 在学习它的实现之前我们还是应该先看看如何使用它.相应的我们这里考察两个用例:一个用来跟踪算法在小规模输入下的行为测试用例和一个来寻找更高效的实现的性能测试用例. 3.1.3.1 行为测试用例 为了在小规模的的输入下跟踪算法的行为,我们用一下测试用例测试我们对符号表的所有实现.这段代码会从标准输入接受多个字符串,构造一张符号表来将i 和第i 个字符串相关联,然后打印符号表.我们假设所有的字符串都只有一个字母.一般我们会使用”S E A R C H E X A M P L E”