没事就多看
相关leetcode算法题:
参考链接:快速排序亲兄弟:快速选择算法 :: labuladong的算法小抄
方法一 二叉堆(小顶堆) √
方法二 快速选择
快速排序的逻辑
void quickSort(int[] arr, int l, int r) {
// 子数组长度为 1 时终止递归
if (l >= r) return;
// 哨兵划分操作(以 arr[l] 作为基准数)
int i = l, j = r;
while (i < j) {
while (i < j && arr[j] >= arr[l]) j--;
while (i < j && arr[i] <= arr[l]) i++;
swap(arr, i, j);
}
swap(arr, i, l);
// 递归左(右)子数组执行哨兵划分
quickSort(arr, l, i - 1);
quickSort(arr, i + 1, r);
}
void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
快速排序的逻辑是要对数组arr[l...r]排序,就需要找一个分界点p,使得
arr[l...p-1] 都小于等于arr[p]
arr[p+1...r]都大于等于arr[p]
然后递归地在arr[l...p-1] 和 arr[p+1...r]中寻找
快速排序中partition的写法
1、初始化“哨兵”索引位置,以arr[l]为基准数
2、循环交换,两哨兵相遇时跳出
从右向左 查找 首个小于基准数的元素
从左向右 查找 首个大于基准数的元素
交换 arr[i] 和 arr[j]
3、交换基准数 arr[i] 和 arr[l]
while(i<j){
while(i<j && arr[j]>=arr[l]) j--;
while(i<j && arr[i]<=arr[l]) i++;
swap(arr,i,j);
}
swap(arr,i,l);
文中的写法这两个 while【指的是第一个嵌套的while】执行完, i j 同时指向一个 < arr[l] 的数,因此最后再执行 arr[l], arr[i] = arr[i], arr[l]
可以把哨兵交换到正确的位置。 而如果互换这两句,那么就是 i 先向右遍历,两个 while 执行完, i j 同时指向一个 > arr[l] 的数,那么就不对了。如果要交换写,那么同时也要把哨兵换成数组的末元素,让整个哨兵划分操作对称。
swap函数必须传入引用类型 也就是数组才可以实现交换,不可以写两个int直接交换
void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
原因:
引用类型才可以交换。因为原生数据类型是直接赋值,而引用类型直接赋的是地址,地址指向的是同一个对象,所以能改变。
快速排序的非递归写法
class Solution {
public static void main(String[] args) {
int[] nums=new int[]{9,1,8,2,7,3};
Solution.stackQuickSort(nums,0, nums.length-1);
for (int num:nums){
System.out.println(num);
}
}
public static void stackQuickSort(int[] nums,int left,int right){
Stack<Integer> stack=new Stack<>();
if (left<right){
stack.push(right);
stack.push(left);
}
while (!stack.isEmpty()){
int l=stack.pop();
int r=stack.pop();
int index=quickSort(nums,l,r);
if (l<index-1){
stack.push(index-1);
stack.push(l);
}
if (r>index+1){
stack.push(r);
stack.push(index+1);
}
}
}
public static int quickSort(int[] nums,int left,int right){
int center=nums[left];
int i=left;
int j=right;
while (i<j){
while (i<j&&nums[j]>=center)j--;
while (i<j&&nums[i]<=center)i++;
int temp=nums[j];
nums[j]=nums[i];
nums[i]=temp;
}
nums[left]=nums[i];
nums[i]=center;
return i;
}
}
快速选择的写法
partition
函数会将 nums[p]
排到正确的位置,使得 nums[lo..p-1] < nums[p] < nums[p+1..hi]
。
二分搜索+partition
那么我们可以把 p
和 k
进行比较,如果 p < k
说明第 k
大的元素在 nums[p+1..hi]
中,如果 p > k
说明第 k
大的元素在 nums[lo..p-1]
中。
所以我们可以复用 partition
函数来实现这道题目,不过在这之前还是要做一下索引转化。
public int findKthLargest(int[] nums, int k) {
//随机打乱
shuffle(nums);
int lo = 0;
int high = nums.length-1;
int index = nums.length-k;
while(lo<=high){
int p = partition(nums,lo,high); //确定了分界点索引p
if(p<index){
lo=p+1;
}else if(p>index){
high=p-1;
}else{
return nums[index];
}
}
return -1;
}