数据结构与算法之经典算法

1 求1000之内的所有水仙花数

 /**
     * 输入描述:求1000之内的所有水仙花数
     *
     * 水仙花数,是指一个三位整数,它的各位数字的立方和等于这个数本身.
       例如:371就是一个水仙花数,因为371=3*3*3+7*7*7+1*1*1.
     */
    public static void NumberOfDaffodils() {
        int hundred, ten, bits;
        for (int i = 100; i <= 999; i++) {
            hundred = i / 100;
            ten = i % 100 / 10;
            bits = i % 10;
            if (i == Math.pow(hundred,3) + Math.pow(ten,3) + Math.pow(bits,3)) {
                Log.d(TAG, "NumberOfDaffodils " + i);
            }
        }
    }
NumberOfDaffodils 153
NumberOfDaffodils 370
NumberOfDaffodils 371
NumberOfDaffodils 407

2 蒙特卡洛算法

2.1 背景

  蒙特卡洛算法概是以率统计理论为指导,使用随机数模拟来解决问题的一种方法。
  求圆周率π , 如果半径为1,,根据勾股定理,x² + y² <= 1的落点都在圆内。正方形面积:2*2=4,圆面积πr² = π。
  随机生成一个坐标,次数为a,假设有b次在圆内,那么 b/a(落在圆内部次数/落在正方形内部次数) = π/4(圆面积/正方形面积),计算可得π=4*b/a。
这里写图片描述

2.2 源码

/**
     * 蒙特卡洛算法
     *
     * 判断落点是否在圆内部 勾股定理 x² + y² = r² ,那么 x² + y² <= r² 都视作在圆内
     * count = 1000000 +
     */
    public double getPi(int count) {
        double in = 0;
        for (int i = 0; i < count; i++) {
            double x = Math.random();
            double y = Math.random();
            if (Math.pow(x, 2) + Math.pow(y, 2) <= 1) {
                in ++;
            }
        }
        return 4 * in / count;
    }

3 蚁群算法

3.1 概念

  蚁群算法(ant colony optimization, ACO),又称蚂蚁算法,是一种用来在图中寻找优化路径的机率型算法。它由Marco Dorigo于1992年在他的博士论文中提出,其灵感来源于蚂蚁在寻找食物过程中发现路径的行为。蚁群算法是一种模拟进化算法,初步的研究表明该算法具有许多优良的性质,并且现在已用于我们生活的方方面面。

3.2 参考链接

自话蚁群算法(带简单模拟实验)

4 KMP经典算法

4.1 KMP算法求解什么类型问题

  字符串匹配。给你两个字符串,寻找其中一个字符串是否包含另一个字符串,如果包含,返回包含的起始位置。

4.2 算法说明  

  一般匹配字符串时,我们从目标字符串str(假设长度为n)的第一个下标选取和ptr长度(长度为m)一样的子字符串进行比较,如果一样,就返回开始处的下标值,不一样,选取str下一个下标,同样选取长度为n的字符串进行比较,直到str的末尾(实际比较时,下标移动到n-m)。这样的时间复杂度是O(n*m)。
  KMP算法:可以实现复杂度为O(m+n)。为何简化了时间复杂度:
充分利用了目标字符串ptr的性质(比如里面部分字符串的重复性,即使不存在重复字段,在比较时,实现最大的移动量)。

4.3 next数组

4.3.1 next数组就是求前面串中前后缀相等的最大长度

这里写图片描述

4.3.2 next数组的作用

这里写图片描述
  next数组就是保存着当主串和模式串不匹配时,接下来与主串j比较的模式串中s的位置,即s=next[s]。

4.3.3 next数组的求法

//next数组的求解
    private static void getNext(int[] next, String str) {
        next[0] = -1;//初始化
        int k = -1;//记录当前位的next
        int j = 0;//当前位下标
        while (j < str.length() - 1) {//求解完所有字符的next
            if (k == -1 || str.charAt(j) == str.charAt(k)) {//比较当前位与当前位next字符是否相等
                j++;
                k++;//当前位的next值+1作为下一位的next值
                next[j] = k;//这个是把算的k的值(就是相同的最大前缀和最大后缀长)赋给next[j]
            } else {
                k = next[k];////往前回溯,回到前一个前后缀相等位置:如果下一个不同,那么k就变成next[k],注意next[k]是小于k的,无论k取任何值。
            }
        }
    }

4.4 完整的KMP算法

  简言之,将主串与模式串匹配,相等的话彼此都+1,如果不等,主串j不回溯,将模式串的s=next[s]与主串j比较,相等彼此+1,以此类推,直到模式串完全被匹配,或者主串匹配到最末,结束。算法如下:

public void main8() {
        String str1 = "ababbabababbbab";//主串
        String str2 = "abababbbab";//模式串
        int next[] = new int[str2.length()];
        getNext(next, str2);//求解next数组

        System.out.println("next数组" + java.util.Arrays.toString(next));
        List<Integer> pos = new ArrayList<>();//可能存在多个位置起始的字符串与模式串匹配,记录这些在主串中的位置

        ifMatch(str1, str2, next, pos);//字符串匹配过程

        System.out.println("匹配位置:" + pos);//输出所有匹配的位置
    }

    private static void ifMatch(String str1, String str2, int[] next, List<Integer> pos) {
        int j = 0;//主串初始位置
        int s = 0;//匹配串初始位置
        while (j < str1.length()) {
            if (s == -1 || str1.charAt(j) == str2.charAt(s)) {//比较字符是否相等
                j++;
                s++;
                if (s >= str2.length()) {//模式串被完全匹配
                    pos.add(j - str2.length());
                    s = 0;
                    j--;
                }
            } else {
                s = next[s];//不等,主串j不变,模式串s变
            }
        }
    }

    //next数组的求解
    private static void getNext(int[] next, String str) {
        next[0] = -1;//初始化
        int k = -1;//记录当前位的next
        int j = 0;//当前位下标
        while (j < str.length() - 1) {//求解完所有字符的next
            if (k == -1 || str.charAt(j) == str.charAt(k)) {//比较当前位与当前位next字符是否相等
                j++;
                k++;//当前位的next值+1作为下一位的next值
                next[j] = k;//这个是把算的k的值(就是相同的最大前缀和最大后缀长)赋给next[j]
            } else {
                k = next[k];////往前回溯,回到前一个前后缀相等位置:如果下一个不同,那么k就变成next[k],注意next[k]是小于k的,无论k取任何值。
            }
        }
    }

4.5 参考链接

【算法】KMP经典算法,你真的懂了吗?

KMP算法最浅显理解——一看就明白

怎么理解kmp算法中的next数组?

5 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

5.1 问题

  假如每个url大小为10bytes,那么可以估计每个文件的大小为50G×64=320G,远远大于内存限制的4G,所以不可能将其完全加载到内存中处理,可以采用分治的思想来解决。

5.2 解决步骤

  (1)遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,…,a999,每个小文件约300M);
  (2)遍历文件b,采取和a相同的方式将url分别存储到1000个小文件(记为b0,b1,…,b999)。
  巧妙之处:这样处理后,所有可能相同的url都被保存在对应的小文件(a0 vs b0,a1 vs b1,…,a999 vs b999)中,不对应的小文件不可能有相同的url。然后我们只要求出这个1000对小文件中相同的url即可。
  (3)求每对小文件ai和bi中相同的url时,可以把ai的url存储到hash_set/hash_map中。然后遍历bi的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
这里写图片描述

5.3 参考链接

面试- 阿里-. 大数据题目- 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
  

猜你喜欢

转载自blog.csdn.net/chenliguan/article/details/79407277