关于剑指 Offer 40. 最小的k个数的相关问题

来源:2-10 和 Select K 相关的三个问题

咸鱼味的猫

2022-08-07 00:30:05

波波老师,您好!

我先贴出来我的代码(基本和您的吻合)

相关代码:

/// Leetcode 剑指 Offer 40. 最小的k个数
/// https://leetcode.cn/problems/kth-largest-element-in-an-array/

import java.util.Arrays;
import java.util.Random;

class Solution {
    public int[] getLeastNumbers(int[] nums, int k) {
        Random rnd = new Random();
        selectK(nums, 0, nums.length - 1, k - 1, rnd);
        return Arrays.copyOf(nums, k);
    }

    private int selectK(int[] nums, int l, int r, int k, Random rnd) {
        int p = partition(nums, l, r, rnd);
        if (k == p) return nums[p];
        if (k < p) return selectK(nums, l, p - 1, k, rnd);
        return selectK(nums, p + 1, r, k, rnd);
    }

    private int partition(int[] nums, int l, int r, Random rnd) {
        int p = l + rnd.nextInt(r - l + 1);
        swap(nums, l, p);
        // arr[l+1...i-1] <= v; arr[j+1...r] >= v
        int i = l + 1, j = r;
        while (true) {
            while (i <= j && nums[i] < nums[l])
                i++;
            while (j >= i && nums[j] > nums[l])
                j--;
            if (i >= j) break;
            swap(nums, i++, j--);
        }
        swap(nums, l, j);
        return j;
    }

    private void swap(int[] nums, int i, int j) {
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}

测试用例是:

public class Main {
    public static void main(String[] args) {
        int[] nums = {0, 0, 0, 2, 0, 5};
        Solution solution = new Solution();
        int[] numbers = solution.getLeastNumbers(nums, 0);
        for (int i = 0; i < numbers.length; i++)
            System.out.print(numbers[i] + " ");
    }
}

报错是:

https://img.mukewang.com/climg/62ee8c44095ff5dd21410669.jpg

https://img.mukewang.com/climg/62ee8ced099ef07318310724.jpg

https://img.mukewang.com/climg/62ee8d5c0974e95726001303.jpg

我的调试过程:

传入的数组是

{0, 0, 0, 2, 0, 5}

传入的参数是0

  • selectK(nums.0,5,-1,rnd)

    • partition(nums,0,5,rnd):p=2,返回值为j=2

    • {0, 0, , 2, 0, 5}
  • k(-1)<p(j=2),selectK(nums,0,1,-1,rnd)

    • partition(nums,0,1,rnd):p=1,返回值为j=1

    • {0, , 0, 2, 0, 5}
  • k(-1)<p(j=1),selectK(nums,0,0,-1,rnd)

    • partition(nums,0,0,rnd):p=0,返回值为j=0

    • {, 0, 0, 2, 0, 5}
  • k(-1)<p(j=0),selectK(nums,0,-1,-1,rnd)

    • partition(nums,0,-1,rnd):在这里的第一行取随机数的代码就会出现索引问题

https://img.mukewang.com/climg/62ee9564096151d013420544.jpg

==============分界线================

波波老师,主要我觉得似乎在处理k=0的时候,无论数组是如何的,似乎都会出现这种问题。。。

下面是老师您的代码:

https://img.mukewang.com/climg/62ee970a09fa4f1c19361036.jpg


写回答

1回答

liuyubobobo

2022-08-07

需要对 k == 0 的情况做一个特判。加上下面一句话即可:


public int[] getLeastNumbers(int[] nums, int k) {
    
    if(k == 0) return new int[0]; // 加上这句话
        
    Random rnd = new Random();
    selectK(nums, 0, nums.length - 1, k - 1, rnd);
    return Arrays.copyOf(nums, k);
}


我们的 selectK(nums, l, r, k) 函数的语义是,在 nums 的 [l, r] 区间中,找到索引为 k 的元素。这个索引 k 必须是合法的。如果 k = 0,传入 k - 1,也就是传进 selectK 的 k 是 -1,是一个非法索引。


==========


这个“边界”测试用例很没有意思。因为返回最小的 0 个数字没有意义。最小的 0 个数字肯定就是什么都没有。我们特殊添加的一句话就是返回了一个含有 0 个元素的数组而已。


我忘记了这个测试用例是不是本身就有的测试用例,还是后来添加的。我怀疑是后来添加的。因为课程的代码在发布的时候,都经过我的测试的。


继续加油!:)

0
共1条回复

算法与数据结构

波波老师5年集大成之作,算法与数据结构系统学习,考试、面试、竞赛通用

2610 学习 · 1087 问题

查看课程