Leetcode高频算法题:数组篇(三)

目录

1. 轮转数组:给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数

示例:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

解法1:

public void rotate(int[] nums, int k) {
    int len = nums.length;
    int[] newArr = new int[len];
    for (int i = 0; i < len; i++) {
        newArr[(i + k) % len] = nums[i];
    }
    System.arraycopy(newArr, 0, nums, 0, len);
}

解法2:

public void rotate1(int[] nums, int k) {
    int len = nums.length;
    k = k % len;
    reverse(nums, 0, len - 1);
    reverse(nums, 0, k - 1);
    reverse(nums, k, len - 1);
}

public void reverse(int[] nums, int start, int end) {
    while (start < end) {
        int temp = nums[start];
        nums[start] = nums[end];
        nums[end] = temp;
        start++;
        end--;
    }
}

解法3:

public void rotate2(int[] nums, int k) {
    int len = nums.length;
    k = k % len;
    int count = gcd(k, len);
    for (int i = 0; i < count; i++) {
        int pre = nums[i];
        int current = i;
        do {
            int next = (current + k) % len;
            int temp = nums[next];
            nums[next] = pre;
            current = next;
            pre = temp;
        } while (i != current);
    }
}

private int gcd(int x, int y) {
    return y > 0 ? gcd(y, x % y) : x;
}

补充说明:
在这里插入图片描述
在这里插入图片描述

2. 下一个更大元素:nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素

示例:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:

  • 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
  • 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
  • 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    Deque<Integer> stack = new ArrayDeque<Integer>();
    for (int i = nums2.length - 1; i >= 0; --i) {
        int num = nums2[i];
        while (!stack.isEmpty() && num >= stack.peek()) {
            stack.pop();
        }
        map.put(num, stack.isEmpty() ? -1 : stack.peek());
        stack.push(num);
    }
    int[] res = new int[nums1.length];
    for (int i = 0; i < nums1.length; ++i) {
        res[i] = map.get(nums1[i]);
    }
    return res;
}

补充说明:
在这里插入图片描述

3. 图像渲染

示例:
在这里插入图片描述
输入: image = [[1,1,1],[1,1,0],[1,0,1]],sr = 1, sc = 1, newColor = 2
输出: [[2,2,2],[2,2,0],[2,0,1]]
解析: 在图像的正中间,(坐标(sr,sc)=(1,1)),在路径上所有符合条件的像素点的颜色都被更改成2。
注意,右下角的像素没有更改为2,因为它不是在上下左右四个方向上与初始点相连的像素点。

private int[][] array = new int[][]{{0, -1}, {0, 1}, {1, 0}, {-1, 0}};
private int rowLen = 0;
private int colLen = 0;
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
    int originColor = image[sr][sc];
    if (originColor == color) {
        return image;
    }
    rowLen = image.length;
    colLen = image[0].length;
    dealArray(image, sr, sc, originColor, color);
    return image;
}
private void dealArray(int[][] image, int sr, int sc, int originColor, int color) {
    if (sr < 0 || sc < 0 || sr >= rowLen || sc >= colLen
            || image[sr][sc] != originColor) {
        return;
    }
    image[sr][sc] = color;
    for (int i = 0; i < array.length; i++) {
        dealArray(image, sr + array[i][0],
                sc + array[i][1], originColor, color);
    }
}

4. 最长递增子序列

示例:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

public int lengthOfLIS1(int[] nums) {
    int len = nums.length;
    int[] dp = new int[len];
    int result = 1;
    dp[0] = 1;
    for (int i = 1; i < len; i++) {
        dp[i] = 1;
        for (int j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
        result = Math.max(result, dp[i]);
    }
    return result;
}

补充说明:
在这里插入图片描述

5. 最小K个数

示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

解法1:堆排序

public int[] smallestK(int[] arr, int k) {
    int[] result = new int[k];
    if (k == 0) {
        return result;
    }
    PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    });
    for (int i = 0; i < k; i++) {
        queue.offer(arr[i]);
    }
    for (int i = k; i < arr.length; i++) {
        if (arr[i] < queue.peek()) {
            queue.poll();
            queue.offer(arr[i]);
        }
    }
    Iterator<Integer> integerIterator = queue.iterator();
    int index = 0;
    while (integerIterator.hasNext()) {

        result[index++] = integerIterator.next();
    }
    return result;
}

解法2:快排思想

public static int[] smallestK1(int[] arr, int k) {

    int[] result = new int[k];
    if (k == 0) {
        return result;
    }
    quickDealArr(arr, 0, arr.length - 1, k);
    for (int i = 0; i < k; i++) {
        result[i] = arr[i];
    }
    return result;
}

private static void quickDealArr(int[] arr, int left, int right, int k) {
    if (left >= right) {
        return;
    }
    int index = randomPartition(arr, left, right);
    int num = index - left + 1;
    if (num == k) {
        return;
    } else if (num > k) {
        quickDealArr(arr, left, index - 1, k);
    } else {
        quickDealArr(arr, index + 1, right, k - num);
    }
}

private static int randomPartition(int[] arr, int left, int right) {
    int random = new Random().nextInt(right - left + 1) + left;
    spwap(arr, random, right);

    return spliteArray(arr, left, right);
}

private static int spliteArray(int[] arr, int left, int right) {
    int index = left;
    for (int i = left; i <= right - 1; i++) {
        if (arr[i] <= arr[right]) {
            spwap(arr, index, i);
            index++;
        }
    }
    spwap(arr, index, right);
    return index;
}

private static void spwap(int[] arr, int random, int right) {
    int temp = arr[random];
    arr[random] = arr[right];
    arr[right] = temp;
}

6. 救生艇

示例:
输入:people = [3,2,2,1], limit = 3
输出:3
解释:3 艘船分别载 (1, 2), (2) 和 (3)

public int numRescueBoats(int[] people, int limit) {
    int result = 0;
    Arrays.sort(people);
    int left = 0;
    int right = people.length - 1;
    while(left <= right){
        if(people[left] + people[right] <= limit){
            left ++;
        }
        result ++;
        right --;
    }
    return result;
}

7. 组合总和

示例:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

public List<List<Integer>> combinationSum(int[] candidates, int target) {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    List<Integer> combine = new ArrayList<Integer>();
    dfs(candidates, target, ans, combine, 0);
    return ans;
}

public void dfs(int[] candidates, int target, List<List<Integer>> ans, List<Integer> combine, int idx) {
    if (idx == candidates.length) {
        return;
    }
    if (target == 0) {
        ans.add(new ArrayList<Integer>(combine));
        return;
    }
if(target < 0){
    return;
}

    // 直接跳过
    dfs(candidates, target, ans, combine, idx + 1);
    // 选择当前数
    if (target - candidates[idx] >= 0) {
        combine.add(candidates[idx]);
        dfs(candidates, target - candidates[idx], ans, combine, idx);
        combine.remove(combine.size() - 1);
    }
}

补充说明:
在这里插入图片描述

8. 螺旋矩阵

示例:
在这里插入图片描述
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

public static List<Integer> spiralOrder(int[][] matrix) {
    List<Integer> res = new ArrayList<>();
    int m = matrix.length;
    int n = matrix[0].length;
    boolean[][] flag = new boolean[m][n];
    int[] dirRow = new int[]{0, 1, 0, -1};
    int[] dirCol = new int[]{1, 0, -1, 0};
    int startDir = 0;
    int startRow = 0;
    int starCol = 0;
    while (res.size() < m * n) {
        res.add(matrix[startRow][starCol]);
        flag[startRow][starCol] = true;

        int nextRow = startRow + dirRow[startDir];
        int nextCol = starCol + dirCol[startDir];
        if (nextRow < 0 || nextRow >= m ||
                nextCol < 0 || nextCol >= n
                || flag[nextRow][nextCol]) {
            startDir = (startDir + 1) % 4;
        }
        startRow = startRow + dirRow[startDir];
        starCol = starCol + dirCol[startDir];
    }
    return res;
}

9. 四数之和

示例:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

public List<List<Integer>> fourSum(int[] nums, int target) {
    List<List<Integer>> quadruplets = new ArrayList<List<Integer>>();
    if (nums == null || nums.length < 4) {
        return quadruplets;
    }
    Arrays.sort(nums);
    int length = nums.length;
    for (int i = 0; i < length - 3; i++) {
        if (i > 0 && nums[i] == nums[i - 1]) {
            continue;
        }
        if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
            break;
        }
        if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
            continue;
        }
        for (int j = i + 1; j < length - 2; j++) {
            if (j > i + 1 && nums[j] == nums[j - 1]) {
                continue;
            }
            if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
                break;
            }
            if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
                continue;
            }
            int left = j + 1, right = length - 1;
            while (left < right) {
                int sum = nums[i] + nums[j] + nums[left] + nums[right];
                if (sum == target) {
                    quadruplets.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    left++;
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    right--;
                } else if (sum < target) {
                    left++;
                } else {
                    right--;
                }
            }
        }
    }
    return quadruplets;
}

补充说明:
在这里插入图片描述
在这里插入图片描述

10. 加油站在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的

示例:
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。

public int canCompleteCircuit(int[] gas, int[] cost) {
    int n = gas.length;
    int i = 0;
    while (i < n) {
        int sumOfGas = 0, sumOfCost = 0;
        int cnt = 0;
        while (cnt < n) {
            int j = (i + cnt) % n;
            sumOfGas += gas[j];
            sumOfCost += cost[j];
            if (sumOfCost > sumOfGas) {
                break;
            }
            cnt++;
        }
        if (cnt == n) {
            return i;
        } else {
            i = i + cnt + 1;
        }
    }
    return -1;
}

补充说明:
若从位置i能到达i + cnt,但不能到达i + cnt+1。
则i~i+cnt的任意位置,都无法到达i + cnt+1。则可以直接
跳过i~i+cnt,令i + cnt+1为新的起点。
在这里插入图片描述

11. 有效的数独

示例:
在这里插入图片描述
输入:board =
[[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”]
,[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”]
,[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”]
,[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”]
,[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”]
,[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”]
,[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”]
,[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”]
,[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
输出:true

public boolean isValidSudoku(char[][] board) {
    int[][] rows = new int[9][9];
    int[][] columns = new int[9][9];
    int[][][] subboxes = new int[3][3][9];
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            char c = board[i][j];
            if (c != '.') {
                int index = c - '0' - 1;
                rows[i][index]++;
                columns[j][index]++;
                subboxes[i / 3][j / 3][index]++;
                if (rows[i][index] > 1 || columns[j][index] > 1 || subboxes[i / 3][j / 3][index] > 1) {
                    return false;
                }
            }
        }
    }
    return true;
}

12. 只出现一次的数字 III

示例:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。

解法1:

public int[] singleNumber(int[] nums) {
    int [] result = new int[2];
    int index = 0;
    Set<Integer> set = new HashSet<>();
    for (int i = 0; i < nums.length; i++) {
        if(set.contains(nums[i])){
            set.remove(nums[i]);
        }else{
            set.add(nums[i]);
        }
    }
    Iterator iterator = set.iterator();
    while(iterator.hasNext()){
        result[index ++] = (int) iterator.next();
    }
    return result;
}

解法2:

public int[] singleNumber2(int[] nums) {
    int [] result = new int[2];
    int index = 0;
    Arrays.sort(nums);
    int len = nums.length;
    for (int i = 0; i < len; i++) {
        if(i == 0){
            if(nums[i] != nums[i + 1]){
                result[index ++] = nums[i];
            }
        }else if(i == len - 1){
            if(nums[i] != nums[i - 1]){
                result[index ++] = nums[i];
            }
        }else{
            if(nums[i] != nums[i - 1] && nums[i] != nums[i + 1]){
                result[index ++] = nums[i];
            }
        }
    }
    return result;
}

解法3:

public int[] singleNumber(int[] nums) {
    int xorsum = 0;
    for (int num : nums) {
        xorsum ^= num;
    }
    // 防止溢出
    int lsb = (xorsum == Integer.MIN_VALUE ? xorsum : xorsum & (-xorsum));
    int type1 = 0, type2 = 0;
    for (int num : nums) {
        if ((num & lsb) != 0) {
            type1 ^= num;
        } else {
            type2 ^= num;
        }
    }
    return new int[]{type1, type2};
}

补充说明:
一个数和它的相反数做与运算,能把最低位得1提取出来。
对Integer.MIN_VALUE取反,会出现溢出得问题,值仍为Integer.MIN_VALUE。
且Integer.MIN_VALUE仅有最高位存在唯一得1,所以没有必要做与运算。

13. 二维数组中的查找

示例:

现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。

public boolean findNumberIn2DArray(int[][] matrix, int target) {
    if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
        return false;
    }
    int rows = matrix.length, columns = matrix[0].length;
    int row = 0, column = columns - 1;
    while (row < rows && column >= 0) {
        int num = matrix[row][column];
        if (num == target) {
            return true;
        } else if (num > target) {
            column--;
        } else {
            row++;
        }
    }
    return false;
}

14. 螺旋矩阵 II:给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

示例:
在这里插入图片描述
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

public int[][] generateMatrix(int n) {
    int[][] res = new int[n][n];
    boolean[][] flag = new boolean[n][n];
    int startNum = 1;
    int[][] dir = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    int dirNum = 0;
    int row = 0;
    int col = 0;
    while (startNum <= n * n) {
        res[row][col] = startNum ++;
        flag[row][col] = true;
        int nextRow = row + dir[dirNum][0];
        int nextCol = col + dir[dirNum][1];
        if (nextRow < 0 || nextRow >= n || nextCol < 0 || nextCol >= n
                || flag[nextRow][nextCol]) {
            dirNum = (dirNum + 1) % 4;
        }
        row = row + dir[dirNum][0];
        col = col + dir[dirNum][1];
    }
    return res;
}

补充说明:
可以不声明二维数组进行标识,利用数组本身元素值大于0即可代表已赋值过这一性质即可。
boolean[][] flag = new boolean[n][n]

15. 第 N 位数字

示例:
输入:n = 11
输出:0
解释:第 11 位数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, … 里是 0 ,它是 10 的一部分。

解法1:

class Solution {
    public int findNthDigit(int n) {
        int low = 1, high = 9;
        while (low < high) {
            int mid = (high - low) / 2 + low;
            if (totalDigits(mid) < n) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        int d = low;
        int prevDigits = totalDigits(d - 1);
        int index = n - prevDigits - 1;
        int start = (int) Math.pow(10, d - 1);
        int num = start + index / d;
        int digitIndex = index % d;
        int digit = (num / (int) (Math.pow(10, d - digitIndex - 1))) % 10;
        return digit;
    }

    public int totalDigits(int length) {
        int digits = 0;
        int curLength = 1, curCount = 9;
        while (curLength <= length) {
            digits += curLength * curCount;
            curLength++;
            curCount *= 10;
        }
        return digits;
    }
}

补充说明:
在这里插入图片描述
在这里插入图片描述
解法2:

class Solution1 {
    public int findNthDigit(int n) {
        int d = 1, count = 9;
        while (n > (long) d * count) {
            n -= d * count;
            d++;
            count *= 10;
        }
        int index = n - 1;
        int start = (int) Math.pow(10, d - 1);
        int num = start + index / d;
        int digitIndex = index % d;
        int digit = (num / (int)(Math.pow(10, d - digitIndex - 1))) % 10;
        return digit;
    }
}

在这里插入图片描述

16. 前 K 个高频元素:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案

示例:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

解法1:快排思想

public int[] topKFrequent(int[] nums, int k) {
    int[] result = new int[k];
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
    }
    ArrayList<int[]> list = new ArrayList<>();
    for (Integer key : map.keySet()) {
        int[] arr = new int[]{key, map.get(key)};
        list.add(arr);
    }
    dealNums(list, 0, list.size() - 1, k);
    for (int i = 0; i < k; i++) {
        result[i] = list.get(i)[0];
    }
    return result;
}

private void dealNums(ArrayList<int[]> list, int left, int right, int k) {
    int index = getIndexByList(list, left, right);
    int num = index - left + 1;
    if (num == k) {
        return;
    } else if (num > k) {
        dealNums(list, left, index - 1, k);
    } else {
        dealNums(list, index + 1, right, k - num);
    }
}

private int getIndexByList(ArrayList<int[]> list, int left, int right) {
    int randomIndex = getRandom(left, right);
    Collections.swap(list, randomIndex, right);
    int num = list.get(right)[1];
    int index = left;
    for (int i = left; i < right; i++) {
        if (list.get(i)[1] >= num) {
            Collections.swap(list, index, i);
            index++;
        }
    }
    Collections.swap(list, index, right);
    return index;
}

private int getRandom(int left, int right) {
    return new Random().nextInt(right - left + 1) + left;
}

解法2:堆排思想(类库实现)

public static int[] topKFrequent(int[] nums, int k) {
    int len = nums.length;
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < len; i++) {
        map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
    }
    PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
        @Override
        public int compare(int[] o1, int[] o2) {
            return o1[1] - o2[1];
        }
    });
    for (int key : map.keySet()) {
        queue.offer(new int[]{key, map.get(key)});
        if (queue.size() > k) {
            queue.poll();
        }
    }
    int[] re = new int[k];
    int index = 0;
    while (!queue.isEmpty()) {
        re[index++] = queue.poll()[0];
    }
    return re;
}

解法3:堆排思想(手写堆排)

public int[] topKFrequent2(int[] nums, int k) {
    int len = nums.length;
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < len; i++) {
        map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
    }
    ArrayList<int[]> list = new ArrayList<>();
    for (int key : map.keySet()) {
        int[] arr = new int[]{key, map.get(key)};
        list.add(arr);
    }
    int listIndex = list.size() - 1;
    buildMaxList(list, listIndex);
    Collections.swap(list, 0, listIndex);
    for (int i = listIndex - 1; i >= listIndex - k + 1; i--) {
        buildList(list, 0, i);
        Collections.swap(list, 0, i);
    }

    int[] result = new int[k];
    int index = 0;
    for (int i = listIndex; i >= listIndex - k + 1; i--) {
        result[index++] = list.get(i)[0];
    }
    return result;
}

private void buildMaxList(ArrayList<int[]> list, int maxIndex) {
    for (int i = maxIndex / 2; i >= 0; i--) {
        buildList(list, i, maxIndex);
    }
}

private void buildList(ArrayList<int[]> list, int root, int maxIndex) {
    int left = 2 * root + 1;
    int right = 2 * root + 2;
    int max = root;
    if (left <= maxIndex && list.get(left)[1] > list.get(root)[1]) {
        max = left;
    }
    if (right <= maxIndex && list.get(right)[1] > list.get(max)[1]) {
        max = right;
    }
    if (max != root) {
        Collections.swap(list, max, root);
        buildList(list, max, maxIndex);
    }
}

17. 三数之和:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。注意:答案中不可以包含重复的三元组

示例:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

public List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);
    int len = nums.length;
    for (int i = 0; i < len - 2; i++) {
        if(i > 0 && nums[i] == nums[i - 1]){
            continue;
        }
        if(nums[i] + nums[i + 1] + nums[i + 2] > 0){
            break;
        }
        if(nums[i] + nums[len - 1] + nums[len - 2] < 0){
            continue;
        }
        int left = i + 1;
        int right = len - 1;
        while(left < right){
            int sum = nums[i] + nums[left] + nums[right];
            if(sum == 0){
                List<Integer> cur = new ArrayList<>();
                cur.add(nums[i]);
                cur.add(nums[left]);
                cur.add(nums[right]);
                list.add(cur);
                while(left < right && nums[left] == nums[left + 1]){
                    left++;
                }
                left++;
                while(left < right && nums[right] == nums[right - 1]){
                    right--;
                }
                right--;
            }else if(sum > 0){
                right--;
            }else{
                left++;
            }
        }
    }
    return list;
}

18. 四数之和:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复)

示例:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

public static List<List<Integer>> fourSum(int[] nums, int target) {
    List<List<Integer>> list = new ArrayList<>();
    int len = nums.length;
    if (len <= 3) {
        return list;
    }
    Arrays.sort(nums);
    for (int i = 0; i < len - 3; i++) {
        while (i > 0 && i < len - 3 && nums[i - 1] == nums[i]) {
            i++;
        }
        if (i < len - 3 && (long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
            break;
        }
        if (i < len - 3 && (long) nums[i] + nums[len - 3] + nums[len - 2] + nums[len - 1] < target) {
            continue;
        }
        for (int j = i + 1; j < len - 2; j++) {
            while (j > i + 1 && j < len - 2 && nums[j - 1] == nums[j]) {
                j++;
            }
            if (j < len - 2 && (long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
                break;
            }
            if (j < len - 2 && (long) nums[i] + nums[j] + nums[len - 2] + nums[len - 1] < target) {
                continue;
            }
            int left = j + 1;
            int right = len - 1;
            int remain = target - nums[i] - nums[j];
            while (left < right) {
                int tempValue = nums[left] + nums[right];
                if (tempValue == remain) {
                    List<Integer> temp = new ArrayList<>();
                    temp.add(nums[i]);
                    temp.add(nums[j]);
                    temp.add(nums[left]);
                    temp.add(nums[right]);
                    list.add(temp);
                }
                if (tempValue >= remain) {
                    int rightLess = right - 1;
                    while (rightLess > left && nums[rightLess] == nums[right]) {
                        rightLess--;
                    }
                    right = rightLess;
                }
                if (tempValue <= remain) {
                    int leftMore = left + 1;
                    while (right > leftMore && nums[leftMore] == nums[left]) {
                        leftMore++;
                    }
                    left = leftMore;
                }
            }

        }
    }
    return list;
}

补充说明:

此题有两个需要注意的地方:
1.以0, 0, 0, 1000000000, 1000000000, 1000000000, 1000000000为例,在此处将出现数值溢出的问题。所以要先转成long。
在这里插入图片描述

2.对于重复元素的处理
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值