Easy | 剑指 Offer 40. 最小的k个数 | 快排

剑指 Offer 40. 最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]

限制:

  • 0 <= k <= arr.length <= 10000
  • 0 <= arr[i] <= 10000

解题思路

快速排序。

public int[] getLeastNumbers(int[] arr, int k) {
    if (arr == null || arr.length == 0 || k == 0) {
        return new int[0];
    }      
    return quickSearch(arr, 0, arr.length-1, k);
}

private int[] quickSearch(int[] arr, int lower, int upper, int k) {
    int pos = partition(arr, lower, upper);
    if (pos == k -1) {
        return Arrays.copyOf(arr, k);
    }
    return pos > k - 1 ? quickSearch(arr, lower, pos-1, k) : quickSearch(arr, pos + 1, upper, k);
}
// 这是一个直接赋值的写法
public int partition(int[] arr, int left, int right) {
    int pivot = arr[left];
    // 维持两个指针l和r, 在运动的过程中,保证l左边的元素都小于等于pivot, r右边的元素都大于pivot
    int l = left, r = right;
    while(l < r) {
        // 先把右边小于枢纽值的元素放到左边
        while(arr[r] >= pivot && l < r) r--;
        arr[l] = arr[r];
        // 再把左边大于枢纽值的元素放到右边
        while(arr[l] <= pivot && l < r) l++;
        arr[r] = arr[l];
    }
    arr[l] = pivot;
    return l;   
}

另外一种写法

public int[] getLeastNumbers(int[] arr, int k) {
    randomizedSelected(arr, 0, arr.length - 1, k);
    return Arrays.copyOf(arr, k);
}

public void randomizedSelected(int[] arr, int l, int r, int k) {
    if (l >= r) {
        return;
    }
    int pos = randomizedPartition(arr, l, r);
    int num = pos - l + 1;
    if (k == num) {
        return;
    } else if (k < num) {
        randomizedSelected(arr, l, pos - 1, k);
    } else {
        randomizedSelected(arr, pos + 1, r, k - num);
    }
}

// 基于随机的划分
public int randomizedPartition(int[] nums, int l, int r) {
    int i = new Random().nextInt(r - l + 1) + l;
    swap(nums, r, i);
    return partition(nums, l, r);
}

public int partition(int[] nums, int l, int r) {
    // 以最右边的元素做枢纽值
    int pivot = nums[r];
    int i = l - 1;
    // 从左往右扫描, 
    for (int j = l; j <= r - 1; ++j) {
        if (nums[j] <= pivot) {
            // 直到遇到一个小于枢纽值的元素, 把其交换到i的下一个位置
            i = i + 1;
            swap(nums, i, j);
        }
        // 如果遇到的值大于枢纽值, 则j指针向前走, 
        // i 指针仍然指向那个不超过枢纽值的最大元素
    }
    // 当数据遍历完成时, 所有小于枢纽值的元素全部都放到[l, i]区间了。
    // 那么只需要将枢纽值交换到 i + 1的位置, 就可以了。
    swap(nums, i + 1, r);
    return i + 1;
}

private void swap(int[] nums, int i, int j) {
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}
上一篇:快速排序的基本实现


下一篇:dataframe pivot table