赞
踩
在属于数组考点系列的题目中,划分为四个常考问题:子数组问题、矩阵问题、O(n)类型问题和思维转换类型问题。
题号:53,难度:简单
题目描述:
解题思路:
本题最为经典和广泛的解法是应用动态规划的思想来解答,其时间复杂度为O(n)。题目中鼓励尝试使用更为精妙的分治法求解,通过翻阅相关解答和评论发现,分治法并没有动态规划解答的优雅,其时间复杂度为O(nlogn),也并不是最优。所以,介绍一下应用动态规划解题的思路。
从数组第一个元素开始遍历,用一个一维数组存储遍历到当前元素的最大连续子数组的和。
当遍历到第i个元素时,如果前i-1和元素中连续子数组和加上第i个元素时比第i个元素的值要大,那么就更新dp[i] = dp[i-1] + nums[i],否则dp[i] = nums[i]。
具体代码:
- class Solution {
- public int maxSubArray(int[] nums) {
- int[] dp = new int[nums.length + 1];
- int result = nums[0];
- for(int i = 0;i < nums.length;i++) {
- dp[i+1] = Math.max(dp[i]+nums[i], nums[i]);
- result = Math.max(dp[i+1], result);
- }
-
- return result;
- }
- }
执行结果:
题号:152,难度:中等
题目描述:
解题思路:
这题其实是例1 最大子序和一个变例,由加法变换成了乘法操作(依旧是应用动态规划的思路)。此时需要做的改变是定义两个变量来存储当前子序列的乘积,一个是保存最大值,一个是保存最小值(包含负数的子序列)。
具体代码:
class Solution { public int maxProduct(int[] nums) { int result = nums[0], n_max = 1, n_min = 1; for(Integer n: nums) { if(n < 0) { int temp = n_max; n_max = Math.max(n_min * n, n); n_min = Math.min(temp * n, n); } else { n_max = Math.max(n_max * n, n); n_min = Math.min(n_min * n, n); } result = Math.max(n_max, result); } return result; } }
执行结果:
题号:78,难度:中等。(可参考子集II, 题号90,难度:中等)
题目描述:
解题思路:
本题考查我们应用回溯来求解所有子集的问题,在一些算法教材中最经典的问题时求解全排列的问题,解法和这道题类似。
此题需要特别注意的是,首先采用链表在递归过程中添加元素,在回溯时删除元素,能够有效提高时间效率。其次,给递归调用程序设计一个start参数,可以避免同一个元素被重复递归调用,达到了剪枝效果。
最后,在结果列表中采用重新创建一个列表存储子集的结果,是因为在递归函数中列表参数只对应一个地址,采用重新创建相当于应用了深拷贝的思想,避免了结果均为空集的情况。
具体代码:
- class Solution {
- private List<List<Integer>> result;
-
- public List<List<Integer>> subsets(int[] nums) {
- result = new ArrayList<>();
- if(nums.length <= 0)
- return result;
- dfs(nums, 0, new LinkedList<Integer>());
-
- return result;
- }
-
- public void dfs(int[] nums, int start, LinkedList<Integer> list) {
- result.add(new ArrayList<Integer>(list));
- for(int i = start;i < nums.length;i++) {
- list.addLast(nums[i]);
- dfs(nums, i + 1, list);
- list.removeLast();
- }
- }
- }

执行结果:
题号:128,难度:困难
题目描述:
解题思路:
采用哈希表存储数组中所有元素,然后应用哈希表查询当前元素的左右两边序列数字是否存在,查询操作的时间复杂度为O(1),所以整体的时间复杂度为O(n)。
具体代码:
- class Solution {
- public int longestConsecutive(int[] nums) {
- int result = 0;
- Set<Integer> set = new HashSet<>();
- for(Integer n: nums)
- set.add(n);
- for(Integer n: nums) {
- if(set.contains(n)) {
- int len = 1;
- int temp = n;
- while(set.contains(--temp)) {
- len++;
- set.remove(temp);
- }
- temp = n;
- while(set.contains(++temp)) {
- len++;
- set.remove(temp);
- }
- result = Math.max(result, len);
- }
- }
-
- return result;
- }
- }

执行结果:
题号:713,难度:中等
题目描述:
解题思路:
本题考查应用双指针的思想,一前一后同时往后遍历。
具体代码:
- class Solution {
- public int numSubarrayProductLessThanK(int[] nums, int k) {
- int result = 0, left = 0, right = 0;
- int target = 1;
- while(right < nums.length) {
- target *= nums[right++];
- while(left < right && target >= k)
- target = target / nums[left++];
- result += (right - left);
- }
-
- return result;
- }
- }
执行结果:
题号:560,难度:中等
题目描述:
解题思路:
本题采用哈希表存储从数组第一个元素不断往后的子序列和,然后判断到当前元素的序列总和减去K的值在哈希表中有多少个,即为包含当前元素的子序列可以得到目标结果,利用前后子序列的差可以得到目标子序列和为K。
具体代码:
- class Solution {
- public int subarraySum(int[] nums, int k) {
- Map<Integer, Integer> map = new HashMap<>();
- map.put(0, 1);
- int sum = 0, result = 0;
-
- for(int i = 0; i < nums.length; ++i) {
- sum += nums[i];
- if(map.containsKey(sum-k))
- result += map.get(sum-k);
- map.put(sum, map.getOrDefault(sum, 0)+1);
- }
-
- return result;
- }
- }

执行结果:
题号:974,难度:中等
题目描述:
解题思路:
从第一个元素开始,求取连续子数组的余数(sum % k),采用Map存储每个余数的个数。
相同余数的子数组个数大于等于2时,任意选取其中两个子数组余数相减,即余数抵消,可得到一个符合题目要求的sum。(此处的个数计算方式为:n*(n-1) / 2)
但是,此处有两个需要注意的点:
(1) 如果余数为0,最终0的余数个数只有一个时(1*(1-1)/2 = 0),这样计算会漏掉(如果为多个,也会有遗漏,可以自己计算,可以自己稍微琢磨)。所以,在初始化Map时,添加以下代码:
map.put(0, 1);
(2) 如果余数为负数,就不能执行相同余数相减抵消的操作。此时,需要做以下处理:
- // sum % K 正常计算方法
-
- ((sum % K) + K) % K // 如果为负数时,需要转换为正数,这个转换原
具体代码:
- class Solution {
- public int subarraysDivByK(int[] A, int K) {
- Map<Integer, Integer> map = new HashMap<>();
- map.put(0, 1);
- int result = 0;
- int sum = 0;
-
- for(Integer a: A) {
- sum += a;
- map.put(((sum % K) + K) % K , map.getOrDefault(((sum % K) + K) % K, 0)+1);
- }
- // System.out.println("map = "+map);
- for(Integer key: map.keySet())
- result += map.get(key) * (map.get(key) - 1) / 2;
-
- return result;
- }
- }

执行结果:
题号:689,难度:困难
题目描述:
解题思路:
采用动态规划求解,状态转移方程:dp[2][n] = max(dp[2][n-1], dp[1][n-k] + sumRange(n, n -k+1))。其中一维长度为3,表示三个子数组。
具体代码(代码引用自LeetCode的一个题解):
class Solution { public int[] maxSumOfThreeSubarrays(int[] nums, int k) { int[][] dp = new int[3][nums.length]; int[] cummulative = new int[nums.length]; int sum = 0; for (int i = 0; i < nums.length; i++) { sum += nums[i]; cummulative[i] = sum; } for (int i = 0; i < 3; i++) { for (int j = 0; j < nums.length; j++) { if (j < (i + 1) * k - 1) { dp[i][j] = 0; } else { if (i == 0) { // 易错点: 当k=1的时候,边界条件需要处理一下。 dp[i][j] = Math.max(j > 0 ? dp[i][j - 1] : 0, rangeSum(cummulative, j - k + 1, j)); } else { dp[i][j] = Math.max(j > 0 ? dp[i][j - 1]: 0, rangeSum(cummulative, j - k + 1, j) + dp[i - 1][j - k]); } } } } int[] ans = new int[3]; int length = dp[2].length - 1; for (int i = 2; i >= 0; i--) { int[] row = dp[i]; for (int j = length - 1; j >= 0; j--) { if (row[j] != row[length]) { ans[i] = j - k + 2; length = j - k + 1; break; } } } return ans; } private int rangeSum(int[] cummulative, int left, int right) { if (left == 0) { return cummulative[right]; } else { return cummulative[right] - cummulative[left - 1]; } } }
执行结果:
题号:718,难度:中等
题目描述:
解题思路:
本题既可以用哈希表来解答,也可以用动态规划的思想来解答。应用动态规划的思路解答的时间效率最高。此处介绍一下动态规划的解题思路。dp[i][j]表示A [i-1]为终点,B[j-1]为终点时两者的最长公共子数组。具体更新策略见代码。
具体代码:
class Solution { public int findLength(int[] A, int[] B) { int[][] dp = new int[A.length + 1][B.length + 1]; int res = 0; for (int i = 1; i <= A.length; i++) for (int j = 1; j <= B.length; j++) { if (A[i - 1] == B[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1; res = Math.max(res, dp[i][j]); } return res; } }
执行结果:
题号:792,难度:中等
题目描述:
解题思路:
要特别注意子序列的含义,子序列是按照从前往后的顺序任意多个元素组成的序列,其中的顺序不能更改。因此,不能应用哈希表统计字母的个数来判断是否包含某个单词。此处可采用暴力法直接匹配查找,时间效率较低。此处可采用二分查找来优化匹配结果,能提高时间效率。
具体代码(贴一个LeetCode上评论的代码):
- class Solution {
- List<Integer> index[]=new ArrayList[26];
-
- public int numMatchingSubseq(String S, String[] words) {
- for(int i=0;i<S.length();i++){
- char ch=S.charAt(i);
- if(index[ch-'a']==null)
- index[ch-'a']=new ArrayList();
- index[ch-'a'].add(i);
- }
- int res=0,pre;
- for(String str:words){
- pre=-1;
- for(int i=0;i<str.length();i++){
- pre=helper(str.charAt(i)-'a',pre);
- if(pre==-1)
- break;
- }
- if(pre!=-1)
- res++;
- }
- return res;
- }
-
- private int helper(int i,int pre){
- if(index[i]==null)
- return -1;
-
- int l=0,r=index[i].size()-1;
- if(pre==-1)
- return index[i].get(0);
- if(index[i].get(r)<=pre)
- return -1;
-
- while(l<r){
- int mid=(l+r)/2;
- if(index[i].get(mid)<=pre)
- l=mid+1;
- else
- r=mid;
- }
-
- return index[i].get(l);
- }
- }

执行结果:
题号:795, 难度:中等
题目描述:
解题思路:
最大元素满足大于等于L小于等于R的子数组个数 = 最大元素小于等于R的子数组个数 - 最大元素小于L的子数组个数。
具体代码:
- class Solution {
- public int numSubarrayBoundedMax(int[] A, int L, int R) {
- return numSubarrayBoundedMax(A, R) - numSubarrayBoundedMax(A, L - 1);
- }
-
- private int numSubarrayBoundedMax(int[] A, int Max) {
- int res = 0;
- int numSubarry = 0;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。