核心目标:在无序数组中,不用完全排序,找到第 k 大 / 第 k 小元素
推荐:数据结构合集 - 快速排序(算法过程, 效率分析, 稳定性分析)_哔哩哔哩_bilibili
int quickselect(int[] nums, int l, int r, int k) { if (l == r) return nums[k]; //找到目标,递归终止 // 基准 指针 指针 int x = nums[l], i = l - 1, j = r + 1; while (i < j) { do i++; while (nums[i] < x); //注意区分while(){...} do j--; while (nums[j] > x); //分别找到一个符合要求的 再向右/左移动 if (i < j){ int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } } if (k <= j) return quickselect(nums, l, j, k); else return quickselect(nums, j + 1, r, k); //分成了两个数组分别递归 }l就是视频中的中轴pivot的下标(注意不是pivot本身),就是随便取一个数当pivot取到了l这个位置的数字
l:当前处理区间左边界下标r:当前处理区间右边界下标k:目标下标(我们要找的是数组排序后,下标为k的元素)
难点:
1.思考nums[k]的含义:
即思考此排序是从小到大排的还是从大到小
2.理解下面代码:
do i++; while (nums[i] < x); do j--; while (nums[j] > x);if (i < j){ int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; }i找到一个大于等于基准 x的数;
j找到一个小于等于基准 x的数。
如果i<j(i 还在 j 左边),说明这两个数站错队了,把它们互相交换。
相当于前面都比较符合预期,不用自己调整就是符合排序的,现在遇到了不符合预期了,才真正需要调整了
4.下面咋理解
if (k <= j) return quickselect(nums, l, j, k); else return quickselect(nums, j + 1, r, k);怎么就已经分成两段了?
因为在上面while循环执行过后,i应该是 ≥ j 的,所以就依此划分
0-j段递归
i-结尾段递归
注意:上面一部分代码可不是两段分别递归了,而是分段后看k在哪一段就哪一段递归,另一段直接舍弃了(因为根本没必要)
但是如果需要完整排序的话可以另外写完整排序的代码,这里不适配
5.为啥是j+1,为啥不能写成i,或者写成i 和 i-1,这两种情况都直接报错(还不是普通的不全面)
仅仅j+1写成i:StackOverflowError栈溢出
j+1写成i且 j写成i-1:StackOverflowError栈溢出
弄不明白了豆包讲吧https://www.doubao.com/thread/x1tgmG6JTS16FKK5P