数据结构与算法学习笔记(训练营三)-经典面试三

  • 每种工作有难度和报酬,规定如下
    class Job {
    public int money;// 该工作的报酬
    public int hard; // 该工作的难度
    }
    给定一个Job类型的数组jobarr,表示所有岗位,每个岗位都可以提供任意份工作
    选工作的标准是在难度不超过自身能力值的情况下,选择报酬最高的岗位
    给定一个int类型的数组arr,表示所有人的能力
    返回int类型的数组,表示每个人按照标准选工作后所能获得的最高报酬
  • 先按照难度排序从小到大,难度相同的按照报酬从大到小,每个难度里面只选报酬最大的,组成一个数组,然后在新的数组中如果难度增大了,但是报酬没有增大,那么淘汰掉这个job,最后又会形成一个新数组,一定是相同难度下报酬最高的,且难度增大报酬必增大的数组,然后组成大跟堆,看能力数组能不能匹配上。
public class MaxSlary {

    public static int[] maxSlary(Job[] job,int[] ables){
        if(job == null || job == null || ables == null || ables.length == 0){
            return null;
        }
        // 先按照难度从小到大排序相同难度,报酬从大到小排序
        Arrays.sort(job,new MyHardC());
        TreeMap map = new TreeMap<>();
        // 每个相同难度都选择一个报酬最大的进有序表
        Job preHard = job[0];
        map.put(preHard.hard,preHard.money);
        for(Job item : job){
            if(preHard.hard != item.hard && item.money > preHard.money){
                map.put(item.hard,item.money);
                preHard = item;
            }
        }

        // 遍历数组
        int[] res = new int[ables.length];
        for (int i = 0; i < ables.length; i++) {
            if(map.floorKey(ables[i]) != null){
                res[i] = map.get(map.floorKey(ables[i]));
            }else {
                res[i] = 0;
            }
        }
        return res;
    }
    static class MyHardC implements Comparator{

        @Override
        public int compare(Job o1, Job o2) {
            return o1.hard != o2.hard ?o1.hard - o2.hard:o2.money -o1.money ;
        }
    }
    public static void main(String[] args) {

        Job[] jobs = new Job[]{new Job(1,2),new Job(1,3),new Job(2,2),new Job(2,4),new Job(3,5),new Job(5,3)};
        int[] ables = new int[]{1,1,2,3,4,5,6,7,8};
        int[] res = maxSlary(jobs, ables);

        for (int i : res){
            System.out.print(i+ " ");
        }
    }
}

  • 背包容量为w,一共有n袋零食, 第i袋零食体积为v[i]>0 ,总体积不超过背包容量的情况下,一共有多少种零食放法?(总体积为0也算一种放法)。
  • 从左到右的尝试模型,先写暴力接,再改动态规划。
/**
 * 背包容量为w
 * 一共有n袋零食, 第i袋零食体积为v[i]
 * 总体积不超过背包容量的情况下,
 * 一共有多少种零食放法?(总体积为0也算一种放法)。
 */
public class BagFood {


    public static int bagFood1(int[] v,int w){
        return process(v,0,w);
    }

    // 从index开始任意选零食,剩下的背包容量为rest,不超过容量的前提下有几种选法
    private static int process(int[] v,int index,int rest){
        if(rest < 0){
            // 如果已经没有剩余背包空间了,那么说明之前的选法有问题,可行方法数为0
            return 0;
        }
        // 到这里说明背包还有剩余空间
        // 如果此时已经没有零食了,说明前面的方案可行有一种方案
        int len = v.length;
        if(index == len){
            return 1;
        }

        // 背包既还有空间,零食也没有选完
        // 当前步骤可以选择要零食,和不要零食两种情况
        // 累加两种情况的数量
        // 不要零食
        int no = process(v,index+1,rest);
        int yes = process(v,index+1,rest-v[index]);
        return no+yes;
    }

    // 动态规划
    public static int dp(int[] v,int w){
        int row = v.length;
        int[][] dp = new int[row+1][w+1];
        int n = dp.length;
        // rest >=0 时 最后一行全是1,表示背包还有空间时,零食已经选完了达标
        for(int i = 0;i < dp[0].length;i++){
            dp[n-1][i] = 1;
        }
        for (int i = dp.length-2; i >= 0; i--) {
            for (int j = 0; j < dp[0].length; j++) {
                // 最后一行开始填
                dp[i][j] = dp[i+1][j]+ (j-v[i] >=0 ? dp[i+1][j-v[i]] : 0);
            }
        }

        return dp[0][w];

    }

    private static int[] getArr(int size,int max){
        int[] res = new int[(int)(Math.random()*size)+1];
        for (int i = 0; i < res.length; i++) {
            // Math.random() -> [0,1)
            res[i] =(int)(Math.random()*max);
        }
        return res;
    }

    private static int[] copyArr(int[] arr){
        int[] res = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            res[i] = arr[i];
        }
        return res;
    }
    public static void main(String[] args) {
        int size = 100;
        int max = 9999;
        System.out.println("statr");
        for (int i = 0; i < 10; i++) {
            int[] a = getArr(size, max);
            int[] c = copyArr(a);
            int w = (int)Math.random()*max;
            int i1 = bagFood1(a, w);
            int dp = dp(c, w);
            if(i1 != dp){
                System.out.println("error!");
            }

        }
        System.out.println("end");


    }
}

  • 给定一个二维数组matrix,其中每个数都是正数,要求从左上到右下
    每一步只能向右或者向下,沿途经过的数字要累加起来,最后请返回最小的路径和。动态规划的空间压缩技巧!
/**
 * 给定一个二维数组matrix,其中每个数都是正数,要求从左上到右下
 * 每一步只能向右或者向下,沿途经过的数字要累加起来
 * 最后请返回最小的路径和
 * 动态规划的空间压缩技巧!
 */
public class MinPath {
    // 暴力递归
    public static int minPath(int[][] matrix){
        if(matrix == null || matrix.length == 0){
            return 0;
        }
        return process(matrix,0,0);
    }

    // 当前来到了(i,j)位置,之前的路径已经求过了,求后续从(i,j)位置触发的最小路径和
    private static int process(int[][] matrix,int i,int j){
        // 如果已经到了终点,那么返回收集的答案
        int endi = matrix.length - 1;
        int endj = matrix[0].length - 1;
        if(i > endi || j > endj){
            return 0;
        }
        if(i == endi && j == endj){
            return matrix[i][j];
        }

        // 有两种选择,向下或者向右
        //1.向下
        int dowm = process(matrix, i + 1, j);
        //2.向右
        int right = process(matrix, i, j + 1);
        // 求两个中最小的
        // 如果返回0代表没有路径不收集答案
        if(dowm == 0 && right == 0){
            return 0;
        }
        dowm = dowm == 0 ? Integer.MAX_VALUE:dowm;
        right = right == 0 ? Integer.MAX_VALUE:right;
        return Math.min(dowm,right)+matrix[i][j];
    }

    public static int dp(int[][] matrix){
        if(matrix == null || matrix.length == 0){
            return 0;
        }
        int[][] dp = new int[matrix.length][matrix[0].length];
        int r = dp.length;
        int c = dp[0].length;
        // 从最后一行开始填
        for (int i = r - 1; i >= 0; i--) {
            for(int j = c-1;j >= 0;j--){
                int dowm = i+1 < r ? dp[i + 1][j] :0;
                int right = j+1 < c ?dp[i][j + 1]: 0 ;
                if(dowm == 0&&right==0){
                    // 右下角终点
                    dp[i][j] = matrix[i][j];
                    continue;
                }
                dowm = dowm == 0 ? Integer.MAX_VALUE:dowm;
                right = right == 0 ? Integer.MAX_VALUE:right;
                dp[i][j] = Math.min(dowm,right)+matrix[i][j];
            }
        }
        return dp[0][0];

    }


    // 另一种尝试
    // matrix[i][j]表示从起点开始到i,j点的最短路径
    // 由题可知,每个点只依赖他的左边个点,和上面个点
    private static int dp2(int[][] matrix){
        if(matrix == null || matrix.length == 0){
            return 0;
        }
        int r = matrix.length;
        int c = matrix[0].length;
        int[][] dp = new int[r][c];
        // 第一列
        for (int k = 0; k < r; k++) {
            dp[k][0] += k-1>=0 ? matrix[k-1][0]+matrix[k][0] : matrix[k][0];
        }
        // 第一行
        for (int k = 0; k < c; k++) {
            dp[0][k] = k-1>=0 ? matrix[0][k-1]+matrix[0][k]:matrix[0][k];
        }
        for (int k = 1; k < r; k++) {
            for (int l = 1; l < c; l++) {
                dp[k][l] = Math.min(dp[k-1][l],dp[k][l-1]) + matrix[k][l];
            }
        }
        return dp[r-1][c-1];
    }


    public static int dp3(int[][] matrix){
        if(matrix == null || matrix.length == 0){
            return 0;
        }
        int r = matrix.length;
        int c = matrix[0].length;
        int[] dp = new int[c];
        // 先填第一行
        // 第一行
        for (int k = 0; k < c; k++) {
            dp[k] = k-1>=0 ? matrix[0][k-1]+matrix[0][k]:matrix[0][k];
        }
        for (int i = 1; i < r; i++) {
            dp[0] += matrix[i][0];
            for (int j = 1; j < c; j++) {
                // 上
                int temp = dp[j];
                // 左 dp[j-1]
                dp[j] = Math.min(temp,dp[j-1]) + matrix[i][j];
            }
        }
        return dp[c-1];
    }

    public static void main(String[] args) {
        int[][]matrix = new int[][]{
                {1,2,3,4,5},
                {1,1,1,1,2},
                {1,1,2,2,2},
                {4,1,2,8,1}

        };

        System.out.println(minPath(matrix));
        System.out.println(dp(matrix));
        System.out.println(dp2(matrix));
        System.out.println(dp3(matrix));
    }
}

  • 请注意区分子串和子序列的不同,给定两个字符串str1和str2,求两个字符的最长公共子序列
    动态规划的空间压缩技巧!
  • 一个样本作行,一个样本作列的尝试模型。
/**
 *  请注意区分子串和子序列的不同,给定两个字符串str1和str2,
 * 求两个字符的最长公共子序列
 * 动态规划的空间压缩技巧!
 */
public class MaxSubSequence {

    // 暴力递归解
    public static String maxSubSequence(String str1,String str2){
        if(str1 == null || str1.equals("") || str1 == null || str2.equals("")){
            return "";
        }
        char[] c1 = str1.toCharArray();
        char[] c2 = str2.toCharArray();
        int len1 = c1.length;
        int len2 = c2.length;
        int[][] dp = new int[len1][len2];
        // dp[i][j] 表示,str1以结尾,str2以j结尾他们的最长子序列的长度是多少
        // 第一行
        for (int i = 0; i < len2; i++) {
            dp[0][i] = c2[i] == c1[0] ? 1 : dp[0][i-1];
        }
        // 第一列
        for (int i = 0; i < len1; i++) {
            dp[i][0] = c1[i] == c2[0] ? 1 : dp[i-1][0];
        }

        for (int i = 1; i < len1; i++) {
            for (int j = 1; j < len2; j++) {
                // 判断两个字符串的最长公共子序列是否已ij结尾
                int p1 = 0;
                int p2 = 0;
                int p3 = 0;
                int p4 = 0;
                if(c1[i] == c2[j]){
                    p1 = dp[i-1][j-1] + 1;
                }else if(c1[i-1] == c2[j]){
                    p2 = dp[i-1][j];

                }else if(c1[i] == c2[j-1]){
                    p3 = dp[i][j-1];
                }else {
                    p4 = dp[i-1][j-1];
                }
                dp[i][j] = Math.max(p1,Math.max(p2,Math.max(p3,p4)));

            }
        }
        int maxSubSeq = dp[len1-1][len2-1];
        char[] resC = new char[maxSubSeq];
        int index = maxSubSeq - 1;
        len1 = len1 - 1;
        len2 = len2 - 1;
        while (index >= 0){
            // 把结果数组填好
            if(len2 >0 && dp[len1][len2] == dp[len1][len2-1]){
                len2 --;
            }else if(len1 > 0&& dp[len1][len2] == dp[len1-1][len2]){
                len1 --;
            }else{
                resC[index--] = c1[len1];
                len1--;
                len2--;
            }
        }

        return String.valueOf(resC);
    }

    public static void main(String[] args) {
        String str1 = "amghnbcni";
        String str2 = "atlgnbfvi";
        System.out.println(maxSubSequence(str1,str2));
    }
}

  • 最长公共子串
/**
 * 请注意区分子串和子序列的不同,
 * 给定两个字符串str1和str2,求两个字符串的最长公共子串
 * 动态规划的空间压缩技巧!
 */
public class MaxSubStr {
    public static String maxSubStr(String str1, String str2) {
        if (str1 == null || str1.equals("") || str1 == null || str2.equals("")) {
            return "";
        }
        char[] c1 = str1.toCharArray();
        char[] c2 = str2.toCharArray();
        int len1 = c1.length;
        int len2 = c2.length;
        int[][] dp = new int[len1][len2];
        // dp[i][j] 表示,最长公共子串必须以ij结尾时的长度
        // 第一行

        for (int i = 0; i < len2; i++) {
            dp[0][i] = c2[i] == c1[0] ? 1 : 0;
        }
        // 第一列
        for (int i = 1; i < len1; i++) {
            dp[i][0] = c1[i] == c2[0] ? 1 : 0;
        }

        int max = 0;
        int endIndex = 0;
        for (int i = 1; i < len1; i++) {
            for (int j = 1; j < len2; j++) {
                if (c1[i] == c2[j]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                if(dp[i][j] > max){
                 max = dp[i][j];
                 endIndex = i;
                }

            }
        }

        return str1.substring(endIndex-max+1,endIndex+1);
    }

    public static String maxSubStr2(String str1,String str2){
        if (str1 == null || str1.equals("") || str1 == null || str2.equals("")) {
            return "";
        }
        char[] c1 = str1.toCharArray();
        char[] c2 = str2.toCharArray();
        int len1 = c1.length;
        int len2 = c2.length;
        int row = 0;
        int col = len2 - 1;
        int len = 0;
        int max = 0;
        int endIndex = 0;
        while (col >0 || row < len1){
            int i = row;
            int j = col;
            while (i < len1 && j < len2){
                if(c1[i] == c2[j]){
                    len ++;
                }else {
                    len = 0;
                }
                if(len > max){
                    max = len;
                    endIndex = i;
                }
                i++;
                j++;

            }
            if(col > 0){
                col --;
            }else {
                row ++;
            }

        }
        return str1.substring(endIndex-max+1,endIndex+1);
    }

    public static void main(String[] args) {
        String str1 = "bnbdnvflop";
        String str2 = "vblbdnvfmnk";
        System.out.println(maxSubStr(str1, str2));
        System.out.println(maxSubStr2(str1, str2));
    }
}
  • 给定一个由字符串组成的数组String[] strs,给定一个正数K,返回词频最大的前K个字符串,假设结果是唯一的
  • 先用hash表统计每个字符串的词频
    再用小根堆,围成前k个数的门槛,小根堆永远放的是当前遍历过的元素的前K大,堆顶是能进入小根堆的门槛。
/**
 * 给定一个由字符串组成的数组String[] strs,给定一个正数K
 * 返回词频最大的前K个字符串,假设结果是唯一的
 */
public class KOfWordFrequency {

    public static List kOfWordFrequency(String[] strs,int k){
        if(strs == null || strs.length == 0 || strs.length < k){
            return null;
        }
        HashMap map = new HashMap<>();
        for(String str : strs){
            if(map.containsKey(str)){
                map.put(str,map.get(str)+1);
            }else {
                map.put(str,1);
            }
        }
        PriorityQueue> queue = new PriorityQueue<>(new MyComparator());
        for (Map.Entry item :map.entrySet()){
            if(queue.size() < k){
                queue.offer(item);
            }else {
                if(queue.peek().getValue() < item.getValue()){
                    queue.poll();
                    queue.offer(item);
                }
            }
        }

        List res = new ArrayList<>();
        while (!queue.isEmpty()){
            res.add(queue.poll().getKey());
        }
        return res;
    }

    static class MyComparator implements Comparator>{
        @Override
        public int compare(Map.Entry o1, Map.Entry o2) {
            return o1.getValue() - o2.getValue();
        }
    }

    public static void main(String[] args) {
        String[] strs = new String[]{"abc","abc","abc","bfg","ngh","bfg","mk","mk","mk","k","o","ol","ol"};

        System.out.println(kOfWordFrequency(strs,3));

    }
}

  • 请实现如下结构:
    TopRecord{
    public TopRecord(int K) : 构造时事先指定好K的大小,构造后就固定不变了
    public void add(String str) : 向该结构中加入一个字符串,可以重复加入
    public List top() : 返回之前加入的所有字符串中,词频最大的K个
    }
    要求:
    add方法,复杂度O(log K);
    top方法,复杂度O(K)

// todo

你可能感兴趣的:(数据结构与算法学习笔记(训练营三)-经典面试三)