【Algorithms公开课学习笔记12】 符号表part5——符号表的应用

符号表的应用

0.前言

经过几篇文章的分析,我们已经清楚了符号表的几种实现方法了。本文将着重介绍符号表的实际应用。在本文的代码中,如果不特别强调,其符号表的实现方式都是红黑树来实现的。

1.集合set

集合最大的特征就是{in, not in}两种属性,因此非常适合做关键字筛选/过滤。关键字筛选/过滤在实际应用中非常广泛,例如垃圾邮件过滤、拼写纠正、浏览器痕迹等等。

我们模拟一个场景:输入一个文件和一些关键字,白名单将筛选出这些关键字并显示,黑名单将过滤这些关键字。

//白名单
public class WhiteList{
    public static void main(String[] args){
        SET<String> set = new SET<String>();
        In in = new In(args[0]);
        //读入关键字,存储在白名单中
        while (!in.isEmpty())
            set.add(in.readString());
        //读入文件,在白名单中匹配
        while (!StdIn.isEmpty()){
            String word = StdIn.readString();
            if (set.contains(word))
            //输出匹配的
                StdOut.println(word);
        }
    }
}
//黑名单
public class BlackList{
    public static void main(String[] args){
        SET<String> set = new SET<String>();
        In in = new In(args[0]);
        //读入关键字,存储在白名单中
        while (!in.isEmpty())
            set.add(in.readString());
        //读入文件,在白名单中匹配
        while (!StdIn.isEmpty()){
            String word = StdIn.readString();
            if (!set.contains(word))
            //输出不匹配的
                StdOut.println(word);
        }
    }
}

模拟运行结果如下图:

2.字典查找dictionary client

在实际应用中,经常需要挑选出一份文件的一列作为key,另若干列作为value,以方便分析或生成报表。字典查找就可以完成此类工作。

//针对CSV文件,用逗号分隔字符串的文件
public class LookupCSV{
    public static void main(String[] args){
        //读入文件
        In in = new In(args[0]);
        //读入key列
        int keyField = Integer.parseInt(args[1]);
        //读入value列
        int valField = Integer.parseInt(args[2]);
        //定义符号表
        ST<String, String> st = new ST<String, String>();
        while (!in.isEmpty()){
            String line = in.readLine();
            String[] tokens = line.split(",");
            String key = tokens[keyField];
            String val = tokens[valField];
            //符合条件的插入符号表
            st.put(key, val);
        }
        //输出显示
        while (!StdIn.isEmpty()){
            String s = StdIn.readString();
            if (!st.contains(s)) StdOut.println("Not found");
            else StdOut.println(st.get(s));
        }
    }
}

模拟运行结果如下图:

3.索引indexing client

在实际应用中,关键字索引也是很常用的。输入关键字,输出关键字所在文件或者所在的句子。

//索引关键字所在的文件
public class FileIndex{
    public static void main(String[] args){
        //符号表,key存字符串,value存文件File集合
        ST<String, SET<File>> st = new ST<String, SET<File>>();
        //遍历每一个文件
        for (String filename : args) {
            File file = new File(filename);
            //读入文件内容
            In in = new In(file);
            while (!in.isEmpty()){
                String key = in.readString();
                //如果key不存在,则创建节点
                if (!st.contains(key))
                    st.put(key, new SET<File>());
                //如果key已经存在,则往文件集合里添加文件
                SET<File> set = st.get(key);
                set.add(file);
            }
        }
        //输出显示
        while (!StdIn.isEmpty()){
            //输入关键字
            String query = StdIn.readString();
            StdOut.println(st.get(query));
        }
    }
}

//索引关键字所在的句子
public class Concordance{
    public static void main(String[] args){
        //读入文件
        In in = new In(args[0]);
        //预先读取文件的所有字符串
        String[] words = in.readAllStrings();
        //符号表,key为字符串,value为整数集合
        ST<String, SET<Integer>> st = new ST<String, SET<Integer>>();
        for (int i = 0; i < words.length; i++){
            String s = words[i];
            //如果字符串不存在,则创建节点
            if (!st.contains(s))
                st.put(s, new SET<Integer>());
            //如果字符串已经存在,则添加索引到整数集合
            SET<Integer> set = st.get(s);
            set.add(i);
        }
        //输出显示
        while (!StdIn.isEmpty()){
            //输入关键字
            String query = StdIn.readString();
            SET<Integer> set = st.get(query);
            for (int k : set)
            // 输入关键字位置的前后4个字符串(形成一句话)
        }
    }
}

模拟运行结果如下图:

4.稀疏向量sparse vectors

常见的矩阵运算很容易实现:

...
double[][] a = new double[N][N];
double[] x = new double[N];
double[] b = new double[N];
...
//初始化 a[][] 和 x[]
...
//行循环,时间复杂度的平方级的N^2
for (int i = 0; i < N; i++){
    sum = 0.0;
    //列循环
    for (int j = 0; j < N; j++)
        sum += a[i][j]*x[j];
    b[i] = sum;
}

但是,对于稀疏矩阵,其本身的存储就很浪费空间。所谓的稀疏矩阵是指少数项存放了有效数字,大多数的项都为零的矩阵。

分析稀疏矩阵之前,我们先来看看只有一行的情况,即稀疏向量。

稀疏向量

使用一维数组来存储稀疏向量,其占用空间为向量大小N。

使用符号表来存储稀疏向量,key表示index,value表示项数据,其占用空间正比于向量的非零项的个数。

public class SparseVector{
    //定义哈希表,可以存储index,value存储项数据
    private HashST<Integer, Double> v;

    public SparseVector(){
        v = new HashST<Integer, Double>();
    }
    //插入key-value对
    public void put(int i, double x){
        v.put(i, x);
    }
    //根据index查找
    public double get(int i){
        if (!v.contains(i)) return 0.0;
        else return v.get(i);
    }
    //迭代key
    public Iterable<Integer> indices(){
        return v.keys();
    }
    //点积
    public double dot(double[] that){
        double sum = 0.0;
        for (int i : indices())
            sum += that[i]*this.get(i);
        return sum;
    }
}

稀疏矩阵

使用二维数组来存储稀疏矩阵,即每一行都是一个数组,其占用空间正比于矩阵大小N^2。

使用符号表来存储稀疏矩阵,即每一行都是一个稀疏向量,其占用空间正比于N+非零项的个数(N是行数组的大小,即符号表的个数)。关键字筛选

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值