数据结构与算法 22 十大算法 分治算法 汉诺塔问题 动态规划 背包问题 字符串匹配 暴力匹配 kmp算法

分治算法

divide and conquer

将复杂问题分为两个或多个相同或相似的子问题,再把子问题分成更小的子问题,直到最后的子问题可以简单的直接求解,原问题的解即子问题的解的合并。应用:快速排序、归并排序、傅立叶变换、汉诺塔


基本步骤:

分治算法在每一层递归上都有三个步骤

  1. 分解:将原问题分解为若干个规模较小,互相独立,与原问题形式相同的子问题
  2. 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
  3. 合并:将各个子问题的解合并为原问题的解

汉诺塔问题

  1. 如果有一个盘,A - C
  2. 如果有两个或以上的盘,可以看作是两个盘:最下边的盘,上面的盘
  3. 先把最上面的盘从 A 移动到 B
  4. 把最下边的盘从 A 移动到 C
  5. 把 B 塔的所有盘从 B 移动到 C

package divideandconquer;

public class Hanoitower {
    public static void main(String[] args) {
        hanoiTower(5,'A','B','C');
    }
    // hanoitower move
    // divide and conquer
    public static void hanoiTower(int num,char a, char b, char c){
        // if only have one plate
        if(num == 1){
            System.out.println("the first plate move "+a+" -> "+c);
        } else{
            // 1. put all upper plates from A-B, use C for help
            hanoiTower(num-1,a,c,b);
            // 2. put the lowest plate from A-C
            System.out.println("the " + num +" move from "+a+" -> "+c);
            // 3. move all upper plates from B-C, A for help
            hanoiTower(num-1,b,a,c);
        }
    }
}

动态规划

Dynamic Programming:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法

与分治算法的不同:适用于动态规划求解的问题,经分解得到子问题往往不是相互独立的(下一子阶段的求解是建立在上一子阶段的解的基础上,进行下一步的求解

可以通过填表进行逐步推导


背包问题

01背包:放入背包的物品不可以重复

完全背包:每件物品都有无限件可用

思路分析

每次遍历到第 i 个物品,根据 w[i](第 i 个商品的重量) 和 v[i](第 i 个商品的价值) 来确定是否需要将该物品放入背包中。C 为背包的容量,令 v/[i]/[j] 表示在前 i 个物品中能够装入容量为 j 的背包中的最大价值


  1. v/[i]/[0] = v/[0]/[j] = 0; 表示填入表的第一行和第一列都是0

  2. 当 w[i] > j 时,v/[i]/[j] = v/[i-1]/[j]; 当准备加入新增的商品的容量大于当前背包的容量时,就直接使用上一个单元格所使用的装入策略

  3. 当 j >= w[i] 时,v[i]/[j] = max{v[i-1]/[j] , v[i-1]/[j-w[i]] + v[i]}; 当准备加入的新增的商品的容量小于等于当前背包的容量,装入的方式为求最大值:

    v[i-1]/[j] :上一个单元格装入的最大值策略

    v[i] : 当前商品的价值

    v[i-1]/[j-w[i]] :装入 i-1 个商品到剩余空间 j-w[i] 的最大值


package algorithm.dynamic;

public class KnapSackProblem {
    public static void main(String[] args) {
        int[] w = {1,4,3}; // weight of items
        int[] val = {1500,3000,2000}; // value of items
        int m = 4; // capacity
        int n = val.length; // number of items
        int[][] v = new int[n+1][m+1];
        // v[i][j] means the max value of pack with a capacity of j among the first i items
        // 2-d array for recording items position
        int[][] path = new int[n+1][m+1];

        // initialize first row and col
        for (int i = 0; i < v.length; i++) {
            v[i][0] = 0; // set first col to 0
        }
        for (int i = 0; i < v[0].length; i++) {
            v[0][i] = 0; // set first row to 0
        }

        // dynamic programming
        for (int i = 1; i < v.length; i++) { // ignore the first row
            for (int j = 1; j < v[0].length; j++) { // ignore the first col
                if(w[i-1]>j){ // i is from 1, so w[i] should be w[i-1]
                    v[i][j] = v[i-1][j];
                } else{
                    // v[i][j] = Math.max(v[i-1][j],val[i-1]+v[i-1][j-w[i-1]]);
                    // index of val and w are from 0, but in this loop, i is start from 1
                    // so val[i] need to be val[i-1] and w[i] should be w[i-1]

                    // if-else
                    if(v[i-1][j] < val[i-1]+v[i-1][j-w[i-1]]){
                        v[i][j] = val[i-1]+v[i-1][j-w[i-1]];
                        path[i][j] = 1;
                    } else{
                        v[i][j] = v[i-1][j];
                        path[i][j] = 1;
                    }
                }
            }

        }

        for (int i = 0; i < v.length; i++) {
            for (int j = 0; j < v[0].length; j++) {
                System.out.print(v[i][j]+" ");
            }
            System.out.println();
        }

        int i = path.length - 1; // max index of row
        int j = path[0].length - 1; // max index of col
        while(i>0 && j>0){
            if(path[i][j] == 1){
                System.out.printf("the %d item in bag\n",i);
                j -= w[i-1];
            }
            i--;
        }
    }
}

字符串匹配算法

字符串匹配问题

暴力匹配算法

假设现在str1匹配到i位置,子串str2匹配到j位置,则:

  1. 如果当前字符匹配成功(即str1[i] == str2[j]),则 i++,j++,继续匹配下一个字符
  2. 如果失配(即str1[i] != str2[j]),令 i = i - (j - 1),j = 0 相当于每次匹配失败时,i回溯,j被置为0
  3. 用暴力方法解决的话就会有大量的回溯,每次只能移动一位,若是不匹配,移动到下一位接着判断,浪费了大量的时间

package algorithm.kmp;

public class ViolenceMatch {
    public static void main(String[] args) {
        String str1 = "i love you like what you do";
        String str2 = "like";
        int index = violentMatch(str1,str2);
        System.out.println("index "+index);
    }
    public static int violentMatch(String str1,String str2){
        char[] s1 = str1.toCharArray();
        char[] s2 = str2.toCharArray();
        int s1Len = s1.length;
        int s2Len = s2.length;
        int i = 0; // index points to s1
        int j = 0; // index points to s2
        while(i<s1Len && j<s2Len){ // not out of bound when matching

            if(s1[i] == s2[j]){ // match
                i++;
                j++;
            } else{ // not match
                i = i - (j - 1);
                j = 0;
            }
        }
        // whether match successfully
        if(j == s2Len){
            return i-j;
        } else{
            return -1;
        }
    }
}

KMP算法

利用之前判断过的信息,通过一个next数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过next数组找到,前面匹配过的位置,省去了大量的计算时间


部分匹配表

搜索词ABCDABD
部分匹配值0000120

后移位数公式:移动位数 = 已匹配的字符数 - 对应的部分匹配值

str1 = BBC ABCDAB ABCDABCDABDE

str2 = ABCDABD


部分匹配值:前缀和后缀的最长共有元素的长度

package algorithm.kmp;

import java.util.Arrays;

public class KmpDemo {
    public static void main(String[] args) {
        String str1 = "BBC ABCDAB ABCDABCDABDE";
        String str2 = "ABCDABD";
        int[] next = kmpNext(str2);
        System.out.println(Arrays.toString(next));
        int index = kmpSearch(str1,str2,next);
        System.out.println("index "+ index);
    }

    // kmp search
    /**
     * kmp search
     * @param str1 source string
     * @param str2 string to be searched
     * @param next partial match table of str2
     * @return first index matched, or return -1
     */
    public static int kmpSearch(String str1,String str2,int[] next){
        // traverse str1
        for (int i = 0,j=0; i<str1.length();i++){
            // need to handle with str1.charAt(i) != str2.charAt(j)
            while(j>0 && str1.charAt(i) != str2.charAt(j)){
                j = next[j-1];
            }
            if(str1.charAt(i) == str2.charAt(j)){
                j++;
            }
            if(j == str2.length()){
                return i-j+1;
            }
        }
        return -1;
    }


    // get partial match table of a string(substring)
    public static int[] kmpNext(String dest){
        // create an array store partial match value
        int[] next = new int[dest.length()];
        next[0] = 0; // if length of string is 1, partial match value is 0
        for(int i = 1,j = 0; i < dest.length(); i++){
            // we need to get new j from next[j-1] when dest.charAt(i) != dest.charAt(j)
            // quit when found dest.charAt(i) == dest.charAt(j)
            while(j>0 && dest.charAt(i) != dest.charAt(j)){
                j = next[j-1];
            }
            if(dest.charAt(i) == dest.charAt(j)){
                j++;
            }
            next[i] = j;
        }
        return next;
    }
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值