3. 数组类问题

253 阅读3分钟

参考

  1. 二维数组的花式遍历技巧

问题总览

序号题目完成

一、TopK

参考:TopK

二、区间和

参考:区间和

三、二分查找

参考:查找算法及应用

四、遍历与轮转

参考:数组遍历与翻转

五、子数组

5.1 重复子数组

参考:重复数

5.2 其他

序号题目完成
560. 和为K的子数组
152. 乘积最大子数组
209. 长度最小的子数组
907. 子数组的最小值之和
974. 和可被 K 整除的子数组
1438. 绝对差不超过限制的最长连续子数组
面试题 17.24. 最大子矩阵
918. 环形子数组的最大和
300. 最长上升子序列
53. 最大子序和

↑↑↑ 频率大于2 ↑↑↑

560. 和为K的子数组

// 暴力解法
class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0;
        for (int start = 0; start < nums.length; start++) {
            int sum = 0;
            for (int end = start; end >= 0; end--) {
                sum += nums[end];
                if (sum == k) {
                    count++;
                }
            }
        }
        return count;
    }
}

还可以用前缀和+hashMap的方法,不写了,比较简单。

↓↓↓ 频率等于1 ↓↓↓

序号题目完成
1574. 删除最短的子数组使剩余数组有序
862. 和至少为 K 的最短子数组
918. 环形子数组的最大和
1493. 删掉一个元素以后全为1的最长子数组
1248. 统计「优美子数组」
643. 子数组最大平均数 I
992. K 个不同整数的子数组
713. 乘积小于K的子数组
剑指 Offer 42. 连续子数组的最大和

其他

序号题目完成
连续数字846. 一手顺子
1296. 划分数组为连续数字的集合
1060. 有序数组中的缺失元素
合并数组88. 合并两个有序数组
56. 合并区间
4. 寻找两个正序数组的中位数
384. 打乱数组
311. 稀疏矩阵的乘法
73. 矩阵置零
85. 最大矩形
1497. 检查数组对是否可以被k整除

88. 合并两个有序数组

// 利用原数组,从后往前赋值
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int x = m - 1;
        int y = n - 1;
        int i = m + n - 1;
        while (x >= 0 && y >= 0) {
            if (nums1[x] >= nums2[y]) {
                nums1[i--] = nums1[x--];
            } else {
                nums1[i--] = nums2[y--];
            }
        }
        while (y >= 0) {
            nums1[i--] = nums2[y--];
        }
    }
}
// 时间复杂度:O(m+n),每一个元素都要被访问一次
// 空间复杂度:O(1)

1296. 划分数组为连续数字的集合

相同问题:846. 一手顺子

class Solution {
    public boolean isPossibleDivide(int[] nums, int k) {
        int len = nums.length;
        // 数组的数量是k的整数倍
        if (len % k != 0) {
            return false;
        }

        // 先将所有元素出现的次数放到map里
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            int val = map.getOrDefault(num, 0);
            map.put(num, val + 1);
        }

        // 排序
        Arrays.sort(nums);
        int idx = 0;
        while (!map.isEmpty()) {
            while (!map.containsKey(nums[idx])) {
                idx++;
            }
            //找到第一个最小的元素
            int min = nums[idx];
            // 检查这个元素是否有连续的k个兄弟
            for (int i = min; i < min + k; i++) {
                // 发现不连续,立刻抛出异常
                if (!map.containsKey(i)) {
                    return false;
                }
                // 如果存在,则更新map中的值,表示被用过了
                int val = map.get(i);
                if (val == 1) {
                    // 如果这个元素只有一个,说明要用完了,从map里删除
                    map.remove(i);
                } else {
                    map.put(i, val - 1);
                }
            }
            idx++;
        }
        // 检查到最后都没有不连续的,那肯定可以满足
        return true;
    }
}

384. 打乱数组

class Solution {
    int[] source;
    int[] nums;
    Random random;

    public Solution(int[] nums) {
        int len = nums.length;
        source = new int[len];
        this.nums = nums;
        System.arraycopy(nums, 0, source, 0, len);
        random = new Random();
    }

    public int[] reset() {
        return source;
    }

    public int[] shuffle() {
        int n = source.length;
        for (int i = 0; i < n; i++) {
            int next = i + random.nextInt(n - i);
            int tmp = nums[i];
            nums[i] = nums[next];
            nums[next] = tmp;

        }
        return nums;
    }
}

56. 合并区间

class Solution {
    public int[][] merge(int[][] intervals) {
        // 第一个元素升序,第二个元素也是升序
        Arrays.sort(intervals, (o1,o2)->{
            if(o1[0] == o2[0]){
                return o1[1] - o2[1];
            }
            return o1[0] - o2[0];
        });
        List<int[]> inters = new LinkedList<>();
        inters.add(new int[]{intervals[0][0],intervals[0][1]});
        for(int i=1;i<intervals.length;i++){
            // 当前队列中最后一个元素
            int[] last = inters.get(inters.size()-1);
            if(intervals[i][0] <= last[1]){
                // 和前一个区间有重合,合并区间
                last[1] = Math.max(last[1], intervals[i][1]);
                inters.set(inters.size()-1, last);
            }else{
                // 和前一个区间没有重合
                inters.add(new int[]{intervals[i][0], intervals[i][1]});
            }
        }
        int[][] res = new int[inters.size()][2];
        for(int i=0;i<inters.size();i++){
            res[i] = inters.get(i);
        }
        return res;
    }
}