算法强化班全解

Posted 创业者-春跃-增长黑客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法强化班全解相关的知识,希望对你有一定的参考价值。

------------------------------------------------------------
第七周:Follow up question

1,寻找峰值

 

寻找峰值

 描述
 笔记
 数据
 评测
你给出一个整数数组(size为n),其具有以下特点:

相邻位置的数字是不同的
A[0] < A[1] 并且 A[n - 2] > A[n - 1]
假定P是峰值的位置则满足A[P] > A[P-1]且A[P] > A[P+1],返回数组中任意一个峰值的位置。

 注意事项

数组可能包含多个峰值,只需找到其中的任何一个即可

您在真实的面试中是否遇到过这个题? Yes
样例
给出数组[1, 2, 1, 3, 4, 5, 7, 6]返回1, 即数值 2 所在位置, 或者6, 即数值 7 所在位置.
View Code

 

答案和思路:利用二分来做。如果mid刚好是峰值,那么就return出来,不然的话就往比他大的一遍走。这个做法一定正确是因为峰值是一定存在的。而且左右边界是下降的。注意从1开始,length - 2结束。因为头尾都不可能是所求。

class Solution {
    /**
     * @param A: An integers array.
     * @return: return any of peek positions.
     */
    public int findPeak(int[] A) {
        // write your code here
        if (null == A || A.length < 3) {
            return -1;
        }
        int left = 1;
        int right = A.length - 2;
        // 注意上面是从1开始,length - 2 结束。因为第一个最后一个一定不是值。
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (A[mid - 1] < A[mid] && A[mid] > A[mid + 1]) {
                return mid;
            } else if (A[mid] > A[mid + 1]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }
}
View Code

2,寻找峰值II

找峰值 II

 描述
 笔记
 数据
 评测
一个整数矩阵有如下一些特性:

相邻的整数都是不同的
矩阵有 n 行 m 列。
对于所有的 i < m, 都有 A[0][i] < A[1][i] && A[n - 2][i] > A[n - 1][i].
对于所有的 j < n, 都有 A[j][0] < A[j][1] && A[j][m - 2] > A[j][m - 1].
我们定义一个位置 P 是一个峰,如果有 A[j][i] > A[j+1][i] && A[j][i] > A[j-1][i] && A[j][i] > A[j][i+1] && A[j][i] > A[j][i-1]。

找出该矩阵的一个峰值元素,返回他的坐标。

 注意事项

可能会存在多个峰值,返回任意一个即可。

您在真实的面试中是否遇到过这个题? Yes
样例
给一个矩阵:

[
  [1 ,2 ,3 ,4 ,5],
  [16,41,23,22,6],
  [15,17,24,21,7],
  [14,18,19,20,8],
  [13,12,11,10,9]
]
返回 41 的坐标[1,1], 或者 24 的坐标[2,2]。
View Code

答案和思路:

对row进行二分,到了mid这一行,对这一行的列进行二分找。

class Solution {
    /**
     * @param A: An integer matrix
     * @return: The index of the peak
     */
    public List<Integer> findPeakII(int[][] A) {
        // write your code here
        // binary search to row
        int low = 1;
        int high = A.length - 2;
        List<Integer> ans = new ArrayList();
        while (low <= high) {
            int mid = low + (high - low) / 2;
            // find the peek of the row
            int col = findPeak(A[mid]);
            if (A[mid - 1][col] < A[mid][col] && A[mid][col] > A[mid + 1][col]) {
                ans.add(mid);
                ans.add(col);
                break;
            } else if (A[mid][col] > A[mid + 1][col]) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return ans;
        
    }
    public static int findPeak(int[] A) {
        // write your code here
        if (null == A || A.length < 3) {
            return -1;
        }
        int left = 1;
        int right = A.length - 2;
        // 注意上面是从1开始,length - 2 结束。因为第一个最后一个一定不是值。
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (A[mid - 1] < A[mid] && A[mid] > A[mid + 1]) {
                return mid;
            } else if (A[mid] > A[mid + 1]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }
}
View Code

 

3,找数组第k大

第k大元素

 描述
 笔记
 数据
 评测
在数组中找到第k大的元素

 注意事项

你可以交换数组中的元素的位置

您在真实的面试中是否遇到过这个题? Yes
样例
给出数组 [9,3,2,4,8],第三大的元素是 4

给出数组 [1,2,3,4,5],第一大的元素是 5,第二大的元素是 4,第三大的元素是 3,以此类推
View Code

答案和思路:小顶堆

class Solution {
    /*
     * @param k : description of k
     * @param nums : array of nums
     * @return: description of return
     */
    public int kthLargestElement(int k, int[] nums) {
        // write your code here
        if (k <= 0 || null == nums || nums.length == 0) {
            return -1;
        }
        PriorityQueue<Integer> minHeap = new PriorityQueue();
        for (int i = 0; i < nums.length; ++i) {
            minHeap.add(nums[i]);
            if (minHeap.size() > k) {
                minHeap.poll();
            }
        }
        return minHeap.peek();
    }
};
View Code

 

4, 子数组和

 子数组之和

给定一个整数数组,找到和为零的子数组。你的代码应该返回满足要求的子数组的起始位置和结束位置
View Code

答案和思路:利用hashmap来存sum。也就是说如果当前为sum,过了一段时间又加到了sum。说明从sum开始的这一段加起来为0。

public class Solution {
    /**
     * @param nums: A list of integers
     * @return: A list of integers includes the index of the first number 
     *          and the index of the last number
     */
    public ArrayList<Integer> subarraySum(int[] nums) {
        // write your code here
        ArrayList<Integer> ans = new ArrayList();
        if (nums.length == 0 || nums == null) {
            return ans;
        }
        // 思路:用一个hashmap来存。从头到尾的sum值。只要出现sum相等,就说明从上一个sum到这里的这一段加起来是0
        HashMap<Integer, Integer> map = new HashMap();
        map.put(0, -1); // 结果是从sum的下一个坐标开始,所以这里是-1
        int sum = 0;
        for (int i = 0; i < nums.length; ++i) {
            sum += nums[i];
            if (map.containsKey(sum)) {
                ans.add(map.get(sum) + 1);
                ans.add(i);
                return ans;
            }
            map.put(sum, i);
        }
        return ans;
    }
}
View Code

 

5,和为0的子矩阵

 和为零的子矩阵

 描述
 笔记
 数据
 评测
给定一个整数矩阵,请找出一个子矩阵,使得其数字之和等于0.输出答案时,请返回左上数字和右下数字的坐标。

您在真实的面试中是否遇到过这个题? Yes
样例
给定矩阵

[
  [1 ,5 ,7],
  [3 ,7 ,-8],
  [4 ,-8 ,9],
]
返回 [(1,1), (2,2)]
View Code

答案和思路:循环第low行到第high行之间。high行column列 - low行column列的差值,如果再次相等那么最右下角的这个正方形是0.

public class Solution {
    /**
     * @param matrix an integer matrix
     * @return the coordinate of the left-up and right-down number
     */
    public int[][] submatrixSum(int[][] matrix) {
        // Write your code here
        int[][] res = new int[2][2];
        if (matrix == null || matrix.length == 0) {
            return res;
        }
        int m = matrix.length;
        int n = matrix[0].length;
        int[][] sum = new int[m + 1][n + 1];
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                sum[i + 1][j + 1] = matrix[i][j] + sum[i + 1][j] + sum[i][j + 1] - sum[i][j];
            }
        }
        for (int low = 0; low < m; ++low) {
            for (int high = low + 1; hight <= m; ++h) {
                HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
                for (int column = 0; column <= n; ++j) {
                    int diff = sum[high][column] - sum[low][column];
                    if (map.containsKey(diff)) {
                        result[0][0] = low;
                        result[1][0] = high - 1;
                        result[0][1] = map.get(diff);
                        result[1][1] = j - 1;
                        return result;
                    }
                    map.put(diff, j);
                }
            }
        }
        return result;
    }
}
View Code

 

 

 

 

 

------------------------------------------------------------
第六周:动态规划 I

 1,石头归并

 石子归并

 描述
 笔记
 数据
 评测
有一个石子归并的游戏。最开始的时候,有n堆石子排成一列,目标是要将所有的石子合并成一堆。合并规则如下:

每一次可以合并相邻位置的两堆石子
每次合并的代价为所合并的两堆石子的重量之和
求出最小的合并代价。

您在真实的面试中是否遇到过这个题? Yes
样例
对于石子序列:[4, 1, 1, 4](每个数代表这堆石子的重量),最优合并方案下,合并代价为 181. 合并第2堆和第3堆 => [4, 2, 4], 代价 +2
2. 合并前两堆 => [6, 4],代价 +6
3. 合并剩下的两堆 => [10],代价 +10
其他例子:
[1, 1, 1, 1] 代价为 8
[4, 4, 5, 9] 代价为 43

标签 
View Code

答案和分析:

public class Solution {
    /**
     * @param A an integer array
     * @return an integer
     */
    public int stoneGame(int[] a) {
        // Write your code here
        if (a == null || a.length == 0) {
            return 0;
        }
        int n = a.length;
        int[][] dp = new int[n][n];
        boolean[][] visit = new boolean[n][n];
        int[][] sum = new int[n][n];
        for (int i = 0; i < n; ++i) {
            sum[i][i] = a[i];
            for (int j = i + 1; j < n; ++j) {
                sum[i][j] = sum[i][j - 1] + a[j];
            }
        }
        return search(0, n - 1, dp, visit, sum);
    }
    public static int search(int left, int right, int[][] dp, boolean[][] visit, int[][] sum) {
        if (visit[left][right]) {
            return dp[left][right];
        }
        if (left == right) {
            visit[left][right] = true;
            return dp[left][right];
        }
        dp[left][right] = Integer.MAX_VALUE;
        for (int k = left; k < right; ++k) {
            dp[left][right] = Math.min(dp[left][right], search(left, k, dp, visit, sum) + search(k + 1, right, dp, visit, sum) + sum[left][right]);
        }
        visit[left][right] = true;
        return dp[left][right];
    }
}
View Code

 

 2,爆炸气球

 Burst Balloons

 Description
 Notes
 Testcase
 Judge
Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent.

Find the maximum coins you can collect by bursting the balloons wisely.
- You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
- 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

Have you met this question in a real interview? Yes
Example
Given [4, 1, 5, 10]
Return 270

nums = [4, 1, 5, 10] burst 1, get coins 4 * 1 * 5 = 20
nums = [4, 5, 10]    burst 5, get coins 4 * 5 * 10 = 200 
nums = [4, 10]       burst 4, get coins 1 * 4 * 10 = 40
nums = [10]          burst 10, get coins 1 * 10 * 1 = 10

Total coins 20 + 200 + 40 + 10 = 270
View Code

答案和分析:dp[i][j] = Math.min(dp[i][j], search(left, k - 1) + search(k + 1, right) + value)

public class Solution {
    /**
     * @param nums a list of integer
     * @return an integer, maximum coins
     */
    public int maxCoins(int[] nums) {
        // Write your code here
        int n = nums.length;
        int[][] dp = new int[n + 2][n + 2];
        boolean[][] visited = new boolean[n + 2][n + 2];
        int[] arr = new int[n + 2];
        for (int i = 1; i <= n; i++) {
            arr[i] = nums[i - 1];
        }
        arr[0] = 1;
        arr[n + 1] = 1;
        return search(arr, dp, visited, 1, n);
    }
    public static int search(int[] arr, int[][] dp, boolean[][] visited, int left, int right) {
        if (visited[left][right]) {
            return dp[left][right];
        }
        int res = 0;
        for (int k = left; k <= right; ++k) {
            int midValue = arr[left - 1] * arr[k] * arr[right + 1];
            int leftValue = search(arr, dp, visited, left, k - 1);
            int rightValue = search(arr, dp, visited, k + 1, right);
            res = Math.max(res, leftValue + midValue + rightValue);
        }
        visited[left][right] = true;
        dp[left][right] = res;
        return res;
    }
}
View Code

 

 3,背包问题

背包问题

 描述
 笔记
 数据
 评测
在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]

 注意事项

你不可以将物品进行切割。

您在真实的面试中是否遇到过这个题? Yes
样例
如果有4个物品[2, 3, 5, 7]

如果背包的大小为11,可以选择[2, 3, 5]装入背包,最多可以装满10的空间。

如果背包的大小为12,可以选择[2, 3, 7]装入背包,最多可以装满12的空间。

函数需要返回最多能装满的空间大小。
View Code

答案和思路:dp[i][j] 表示前i个物品取出一些是否能够构成j价值。

dp[i + 1][j] = true:<=> dp[i][j - A[i]] == true;

dp[i + 1][j] = dp[i][j];

public class Solution {
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @return: The maximum size
     */
    public int backPack(int m, int[] A) {
        // write your code here
        if (A == null || A.length == 0) {
            return 0;
        }
        // dp[i][j] : 前i个物品是否能够组成j价值
        boolean[][] dp = new boolean[A.length + 1][m + 1];
        dp[0][0] = true;
        for (int i = 0; i < A.length; i++) {
            for (int j = 0; j < m + 1; j++) {
                dp[i + 1][j] = dp[i][j];
                if (j >= A[i] && dp[i][j - A[i]]) {
                    dp[i + 1][j] = true;
                }
            }
        }
        for (int j = m; j >= 0; j--) {
            if (dp[A.length][j]) {
                return j;
            }
        }
        return 0;
    }
}
View Code

 滚动数组优化:

public class Solution {
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @return: The maximum size
     */
    public int backPack(int m, int[] A) {
        // write your code here
        if (A == null || A.length == 0) {
            return 0;
        }
        // dp[i][j] : 前i个物品是否能够组成j价值
        boolean[][] dp = new boolean[2][m + 1];
        dp[0][0] = true;
        for (int i = 0; i < A.length; i++) {
            for (int j = 0; j < m + 1; j++) {
                dp[(i + 1) % 2][j] = dp[i % 2][j];
                if (j >= A[i] && dp[i % 2][j - A[i]]) {
                    dp[(i + 1) % 2][j] = true;
                }
            }
        }
        for (int j = m; j >= 0; j--) {
            if (dp[(A.length) % 2][j]) {
                return j;
            }
        }
        return 0;
    }
}
View Code

 

 4, 背包2

背包问题 II

 描述
 笔记
 数据
 评测
给出n个物品的体积A[i]和其价值V[i],将他们装入一个大小为m的背包,最多能装入的总价值有多大?

 注意事项

A[i], V[i], n, m均为整数。你不能将物品进行切分。你所挑选的物品总体积需要小于等于给定的m。

您在真实的面试中是否遇到过这个题? Yes
样例
对于物品体积[2, 3, 5, 7]和对应的价值[1, 5, 2, 4], 假设背包大小为10的话,最大能够装入的价值为9。
View Code

答案和思路:dp[j] 表示占用j的体积的时候的最大的价值。dp[j] = dp[j - A[i]] + V[i].一直把dp[j] 最大值存下来。对于j来说,对于每一个进来的A[i]他要看dp[j - A[i]]那个地方能够偶安多少然后加上V[I]。加上v[i] 就是把V[I] 加进来。

public class Solution {
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A & V: Given n items with size A[i] and value V[i]
     * @return: The maximum value
     */
    public int backPackII(int m, int[] A, int V[]) {
        // write your code here
        if (m == 0 || A.length == 0 || V.length == 0) {
            return 0;
        }
        int[] dp = new int[m + 1];
        for (int i = 0; i < A.length; ++i) {
            for (int j = m; j >= A[i]; --j) {
                dp[j] = Math.max(dp[j], dp[j - A[i]] + V[i]);
            }
        }
        return dp[m];
    }
}
View Code

 

5,最大子数组III

最大子数组 III

 描述
 笔记
 数据
 评测
给定一个整数数组和一个整数 k,找出 k 个不重叠子数组使得它们的和最大。每个子数组的数字在数组中的位置应该是连续的。

返回最大的和。

 注意事项

子数组最少包含一个数
View Code

答案和思路:

localMax[i][j] : 第j个元素必须取。前j个元素里面取出i个的max。

gloabMax

public class Solution {
    /**
     * @param nums: A list of integers
     * @param k: An integer denote to find k non-overlapping subarrays
     * @return: An integer denote the sum of max k non-overlapping subarrays
     */
    public int maxSubArray(int[] nums, int k) {
        // write your code here
        if (nums.length < k) {
            return 0;
        }
        int len = nums.length;
        // 前j个元素的i个子数组的max
        int[][] globalMax = new int[k + 1][len + 1];
        int[][] localMax = new int[k + 1][len + 1];
        for (int i = 1; i <= k; ++i) {
            localMax[i][i - 1] = Integer.MIN_VALUE;
            for (int j = i; j <= len; ++j) {
                localMax[i][j] = Math.max(localMax[i][j - 1], globalMax[i - 1][j - 1]) + nums[j - 1];
                if (j == i) {
                    globalMax[i][j] = localMax[i][j];
                } else {
                    globalMax[i][j] = Math.max(globalMax[i][j - 1], localMax[i][j]);
                }
            }
        }
        return globalMax[k][len];
    }
}
View Code

 

 

 

 

------------------------------------------------------------

 

第五周:动态规划 I

 

1,房子抢劫

 

打劫房屋

 描述
 笔记
 数据
 评测
假设你是一个专业的窃贼,准备沿着一条街打劫房屋。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警。

给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。

您在真实的面试中是否遇到过这个题? Yes
样例
给定 [3, 8, 4], 返回 8.
View Code

 

答案和思路:首先他这一家是否抢劫取决于,他如果抢这一家加上前前一家的钱和他采取抢前一家的钱谁多。这里优化用到滚动指针。因为他与他的前两个数字有关,可以通过%2来做。因为整个数组只需要一直有两个空位就够了。

注意:既然要用滚动指针来优化,那么开得空间的只需要2就行了。

public class Solution {
    /**
     * @param A: An array of non-negative integers.
     * return: The maximum amount of money you can rob tonight
     */
    public long houseRobber(int[] A) {
        // write your code here
        if (null == A || A.length == 0) {
            return 0;
        }
        if (A.length == 1) {
            return A[0];
        }
        if (A.length == 2) {
            return Math.max(A[0], A[1]);
        }
        long res = 0;
        long[] dp = new long[2];
        dp[0] = A[0];
        dp[1] = Math.max(A[0], A[1]);
        for (int i = 2; i < A.length; ++i) {
            //以上是关于算法强化班全解的主要内容,如果未能解决你的问题,请参考以下文章

强化学习全解

含泪提速!一文全解相似度算法跟踪算法在各个AI场景的应用(附代码)

含泪提速!一文全解相似度算法跟踪算法在各个AI场景的应用(附代码)

含泪提速!一文全解相似度算法跟踪算法在各个AI场景的应用(附代码)

深度强化学习-DDPG算法原理与代码

深度强化学习-DQN算法原理与代码