排序思想-快排

快速排序

912.排序数组 

快速排序核心思想就是每次在当前区间 [left, right] 中选择出一个元素 nums[p],然后将区间内所有大于它的元素和所有小于它的元素都放到其两侧【具体放在哪一侧取决于是升序还是降序】,然后再递归去处理两侧区间。

class Solution {
    public int[] sortArray(int[] nums) {
        quickSort2(nums,0,nums.length-1);
        return nums;
    }
    // 荷兰国旗问题
	public static int first, last;
    public static void quickSort2(int[] arr, int l, int r) {
		if(l>=r) return;
		// 随机这一下,常数时间比较大
		// 但只有这一下随机,才能在概率上把快速排序的时间复杂度收敛到O(n * logn)
		int x = arr[l + (int) (Math.random() * (r - l + 1))];
		partition2(arr, l, r, x);
		// 为了防止底层的递归过程覆盖全局变量
		// 这里用临时变量记录first、last
		int left = first;
		int right = last;
		quickSort2(arr, l, left - 1);
		quickSort2(arr, right + 1, r);
	}

	// 已知arr[l....r]范围上一定有x这个值
	// 划分数组 <x放左边,==x放中间,>x放右边
	// 把全局变量first, last,更新成==x区域的左右边界
	public static void partition2(int[] arr, int l, int r, int x) {
		first = l;
		last = r;
		int i = l;
		while (i <= last) {
			if (arr[i] == x) {
				i++;
			} else if (arr[i] < x) {
				swap(arr, first++, i++);
			} else {
				swap(arr, i, last--);
			}
		}
	}
    public static void swap(int[] nums, int i, int j){
        if(i==j) return;
        nums[i] ^= nums[j];
        nums[j] ^= nums[i];
        nums[i] ^= nums[j];
    }
    
}

双指针交换的方式划分左右区间

215.数组中第K个最大元素.

将大于切分值的元素都放到左侧,小于切分值得元素都放到右侧。因此我们可以使用双指针分别从两端往中间搜索,分别找到左侧小于切分值的元素和右侧大于切分值的元素,交换之,直到两个指针相遇。

对于这道题,我们可以选择区间的中间值作为切分元素,并且我们要事先将切分值交换到区间的右边界:

  1. 避免在划分左右区间的时候,将切分值给覆盖掉。
  2. 最终才可以将切分值放到正确的位置。
class Solution {
    public int findKthLargest(int[] nums, int k) {
        return quickSortKth(nums,k,0,nums.length-1);
    }
    private int quickSortKth(int[] nums, int k, int left, int right){
        int mid =left + (right-left) / 2; 
        swap(nums,mid, right);  // 将切分值放到右边界避免加入元素的划分
        int partition = nums[right], i=left, j=right;  // 双指针从左右边界开始,分别找到要交换的元素
        while(i<j){
            while(i<j && nums[i]>=partition) i++;
            while(j>i && nums[j]<=partition) j--;  // 找到右侧大于切分值的元素【因为是找大于,即使j从right开始,right也不会被选中】
            swap(nums,i,j);
        }
        swap(nums,i,right); // i最后停留的位置一定是右侧首个小于切分值的元素,与切分值交换,则[left, i)都是大于(等于)切分值,[i+1, right]都是小于(等于)切分值

        if(i==k-1) return nums[i];
        if(i<k-1) return quickSortKth(nums,k,i+1,right);
        return quickSortKth(nums,k,left,i-1);

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

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于快排思想的选择算法(QuickSelect)是一种寻找无序数组中第 k 小(或第 k 大)元素的算法。其基本思想是:快速排序每次将数组分为两个部分,左边部分小于等于右边部分,因此我们可以利用这一特性来寻找第 k 小元素。 以下是基于快排思想的选择算法的Java实现: ``` public static int quickSelect(int[] arr, int k) { int left = 0; int right = arr.length - 1; while (left <= right) { int pivotIndex = partition(arr, left, right); if (pivotIndex == k - 1) { return arr[pivotIndex]; } else if (pivotIndex < k - 1) { left = pivotIndex + 1; } else { right = pivotIndex - 1; } } return -1; // 表示未找到第 k 小元素 } private static int partition(int[] arr, int left, int right) { int pivot = arr[left]; int i = left + 1; int j = right; while (i <= j) { if (arr[i] <= pivot) { i++; } else if (arr[j] > pivot) { j--; } else { swap(arr, i++, j--); } } swap(arr, left, j); return j; } private static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } ``` 在上述代码中,我们定义了一个 `quickSelect` 方法,该方法接收两个参数,一个是无序数组 `arr`,另一个是第 k 小元素的位置 `k`。首先,我们定义两个指针 `left` 和 `right`,分别指向数组的左端和右端。然后,我们进入循环,每次调用 `partition` 方法将数组分为两个部分,并返回枢轴元素的下标 `pivotIndex`。如果 `pivotIndex` 等于 `k - 1`,则找到了第 k 小元素,返回其值;如果 `pivotIndex` 小于 `k - 1`,则第 k 小元素在枢轴元素的右侧,将 `left` 指针指向 `pivotIndex + 1`;如果 `pivotIndex` 大于 `k - 1`,则第 k 小元素在枢轴元素的左侧,将 `right` 指针指向 `pivotIndex - 1`。最后,如果循环结束仍未找到第 k 小元素,则返回 `-1`。 在 `quickSelect` 方法中,我们调用了 `partition` 方法,将数组分为两个部分。`partition` 方法的实现与快速排序类似,首先选定一个枢轴元素 `pivot`(这里我们选取数组的左端元素),然后定义两个指针 `i` 和 `j`,分别指向数组的左右两端。循环中,我们将 `i` 指针向右移动,直到找到第一个大于枢轴元素的元素;将 `j` 指针向左移动,直到找到第一个小于等于枢轴元素的元素;然后交换 `i` 和 `j` 指向的元素,使得左侧元素小于等于枢轴元素,右侧元素大于枢轴元素。重复以上步骤,直到 `i` 指针超过 `j` 指针,此时将枢轴元素与 `j` 指针指向的元素交换位置,使得枢轴元素左侧元素小于等于它,右侧元素大于它,然后返回 `j` 的值作为枢轴元素的下标。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值