算法系列:(一).联机算法之>>求字符串数组中只出现过一次的字符串及索引

前言:

    最近阅读 <数据结构与算法分析(java语言描述)> 一书, 其在 "算法分析" 一章中, 用一个求 "数值字符串最大子串和" 的例子演示了算法的奇妙.:

    相同的目的, 四种算法时间复杂度从O(N^{3})演变为O(N^{2})再到O(N logN), 最后直接到O(N), 从而也顺便介绍了一种叫做联机算法的 "几乎完美" 的算法;

    那什么叫联机算法呢?

 

正文:

     联机算法: 是在任意时刻算法对要操作的数据只读入(扫描)一次,一旦被读入并处理,它就不需要再被记忆(第二次使用)了。而在此处理过程中算法能对它已经读入的数据立即给出相应子序列问题的正确答案 (前n个值的正确答案)。图示如下:

    

受此启发, 想起最近其他人发的一道面试题: 编写代码, 统计字符串数组Str[100]中所有只出现过一次的字符串及字符串在数组中的出现位置:

 

一般算法:

    看到此题, 博主第一时间随手给出以下代码:

    public static void main(String[] args) {
        //源数组生成
        final int SIZE=50000;
        String[] strings=new String[SIZE];
        Random random = new Random();
        for (int i = 0; i < SIZE; i++) {
            strings[i]=random.nextInt(SIZE/2)+"";
        }
        
        //开始算法
        long start = System.currentTimeMillis();
        int length = strings.length;//获取数组长度
        Map<Integer, String> resultMap = new LinkedHashMap<>();
        boolean isSingle=true;

        for (int i = 0; i < length; ++i , isSingle=true) {//包括boolean的初始化
            if(strings[i]!=null&&!strings[i].equals("")){//去除无效元素
                for (int j = 1; j < length - i; j++) {//遍历判断:如果重复 则置空
                    if(strings[i].equals(strings[i+j])){
                        strings[i+j]=null;
                        isSingle=false;//确定当前元素不是单个
                    }
                }
                if (isSingle){//否则为单个元素 则加入结果集
                    resultMap.put(i,strings[i]);
                }
            }
        }
        //算法结束
        long mild = System.currentTimeMillis();
        resultMap.keySet().forEach(key-> System.out.println("index:"+key+" == value:"+resultMap.get(key)));
        //值提取结束
        long end = System.currentTimeMillis();
        System.out.println("算法时间:"+(mild-start));
        System.out.println("值提取时间:"+(end-mild));
    }

为了测试算法效率,将数组长度改为50000,算法时间如下:

算法时间:16855毫秒
值提取时间:222毫秒

 

联机算法:

    博主发现当字符串长度增加后,上述算法时间大大延长, 甚至当数据达到百万千万时失去了实际使用的意义, 当看到联机算法, 恍然大悟, 于是给出真对该面试题的联机算法:

    public static void main(String[] args) {
        //源数组生成
        final int SIZE=50000;
        String[] strings=new String[SIZE];
        Random random = new Random();
        for (int i = 0; i < SIZE; i++) {
            strings[i]=random.nextInt(SIZE/2)+"";
        }

        //开始算法
        long start = System.currentTimeMillis();
        int length = strings.length;//获取数组长度
        Map<String, Integer> resultMap = new LinkedHashMap<>();
        Integer term;

        for (int index = 0; index < length; index++) {
            if (strings[index]!=null&&!"".equals(strings[index])){
                term=resultMap.get(strings[index]);
                if (term==null) {//如果没遇到过
                    resultMap.put(strings[index],index);
                }else {//如果已经存了
                    resultMap.put(strings[index],-1);
                }
            }
        }

        //算法结束
        long mild = System.currentTimeMillis();
        resultMap.keySet().forEach(key-> {
            if (resultMap.get(key)>0){
                System.out.println("index:"+resultMap.get(key)+"    value:"+key);
            }
        });

        //值提取结束
        long end = System.currentTimeMillis();
        System.out.println("算法时间:"+(mild-start));
        System.out.println("值提取时间:"+(end-mild));
    }

    为了测试算法效率,将数组长度改为50000,算法时间如下 (当然博主电脑已经是七年的古董了):

算法时间:53毫秒
值提取时间:247毫秒

那么联机算法如果花相同的时间, 可以对多大的数组完成不重复值的筛选呢?结果是当博主将数组长度改为10000000(一千万)时, 得到如下结果 (即相同时间, 处理的数据量相差200倍): 

算法时间:11790毫秒
值提取时间:13357毫秒

    不难看出, 之所以有如此大的差距, 还是时间复杂度问题:

    前者时间复杂度为: O(N^{2}), 后者 "联机算法" 时间复杂度为 O(N);  所以, 个人理解: 算法究其根本还是降低待处理集合中每个元素的平均被操作次数;

    当摩尔定律一去不复返, 人们开始普遍相信Amdahl定律对性能的提升, 此时的算法之美显得尤为突出!

----------------------------------------------------------------------------完--------------------------------------------------------------------------------------------
作者:划船一哥 
来源:CSDN 
https://me.csdn.net/weixin_42711325
版权声明:本文为博主原创文章,转载请附上博文链接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章