随机快速排序+随机选择算法

本文详细介绍了C++中随机快速排序算法的原理,包括随机选取基准值、分区操作和递归调用,以及与LeetCode中第k个大小的数问题相关的randselect算法。同时讨论了两种算法的时间复杂度,特别是随机性对时间效率的影响。
摘要由CSDN通过智能技术生成

随机快速排序的原理:在数组中随机选取一个数作为分隔值a,将小于a的数放到a的左边,大于a

的数放到a的右边,之后再递归排列右数组和左数组。

注:关于c语言中随机选择的函数rand需注意:rand()%(n)就会随机选取0~n-1中的任意一个数,

其头文件是#include<cstdlib>(c++中也有自己的随机选择函数库)

#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
int first,last;//记录每次排序后左右数组的末尾和开头的下标
void swap(vector<int>& nums, int first, int i) {//交换两数
	int a = nums[first];
	nums[first] = nums[i];
	nums[i] = a;
}
void partion(vector<int>& nums, int l, int r, int x) {
	 first = l;
	 last = r;
	int i = l;
	while (i <= last) {
		if (nums[i] == x)//等于x的数值不动,等下面碰到小于x的数值时,交换nums[i]和nums[first]
			i++;         //相当于推着等于x 的数向前走
		else if (nums[i] < x) {//
			swap(nums,first, i);
			first++;
			i++;
		}
		else {
			swap(nums, last, i);
			last--;//为什么不i++?因为交换后原last位置的数还没判断与x的大小。与first不一样,
		}          //first的数交换到后面未来还会进行判断  
			
	}

 }
void quicksort(vector<int>& nums,int l,int r) {
	if (l >= r) return;
	int x = nums[l + rand() % (r - l + 1)];//在l~r的数组区间内随机选择一个数
	 partion(nums, l, r, x);//将数值等于x的所有数都放在一起
	 int left = first - 1;//first为x左边小于x的数的第一个下标
	 int right = last + 1;//last为x右边大于x的数的第一个下标
	 quicksort(nums, l, left);
	 quicksort(nums, right, r);


 }
int main() {
	vector<int> nums = {1,5,2,6,12,5,0};
	quicksort(nums, 0, nums.size() - 1);
	for (int i = 0; i < nums.size(); i++)
		cout << nums[i] << " ";
	return  0;


}

 上述随机排序是将每次等于取得的随机值的所有的数都放在一起,也叫"荷兰国旗优化随机排序算

法"其实也可以每次只将一个等于x的数值操作,其他等于x的数值在下一次递归中进行排列,但如

果给的数据全部都是同一个值,那么就会调用多次递归,不如一次性处理好所有等于x的元素,所

以不再介绍一次递归只处理一个数据的写法。

随机快排的时间复杂度:

随机快排作为一个随机性过程,不能取其最差情况来估计其时间复杂(只有当代码的执行过程是固

定流程时,才可以取其最差的情况估计其时间复杂度)。对于随机过程,我们应取其期望值来估计

其时间复杂度。如果我们每次取的x值正好是区间的中间大小的值,那么根据master公式可以得出

其时间复杂度为T(N)=2*T(N/2)+O(N)(中间每次将大于x和小于x的数分布到两侧的时间复杂度为

O(N),因为只遍历了一遍)=O(N*logN);但当我们取得x值每次都是区间的最大值时,比如区间的范

围为1~n,第一次如果排n,需遍历1~n-1;第二次如果排n-1,需要遍历1~n-2.....;依次类推,可

见其时间复杂度为O(N^2)。数学家们推算其时间复杂的期望为O(N*logN)(过程很复杂,知不知

道不影响后面的学习)。由此我们得出一个重要的结论:在进行随机快排时,我们会想一个问题,

就是为什么要 "随机",时间复杂度就是原因,如果我们固定流程,那如果我提供一组数据1~n(从

小到大已经排好序了),显然根据上面的推算,其最坏的时间复杂度的情况就是O(N^2),所以我

们选择随机排序。

练习随机排序链接数组排序

随机选择算法

以leetcode"第k个大小的数"为例 第k大的数

#include<iostream>
#include <vector>
using namespace std;
int first, last;

void swap(vector<int>& nums, int left, int right) {
	int  rem = nums[left];
	nums[left] = nums[right];
	nums[right] = rem;
}
void partion(vector<int>& nums, int l, int r, int x) {//和随机排序算法一样,将等于x的数
                                                      //放在一起
	first = l, last = r ; 
	int i = l;
	while (i <= last) {
		if (nums[i] == x)
			i++;
		else if (nums[i] > x) {
			swap(nums, i, last);
			last--;
		}
		else {
			swap(nums, first, i);
			first++;
			i++;
		}
	}
	
}
int randselect(vector<int>& nums,int k) {
	int l = 0, r = nums.size() - 1;
	while (l <= r) {
		int a = nums[l+rand() %(r-l+1)];
		partion(nums, l, r, a);
		if (k < first)//排好序后将返回的first和last与要求的下标比较
			r = first;
		else if (k > last)
			l = last + 1;
		else {//处于last和first之间就说明下标为k的数就是区间内的数
			return nums[k];
		}

	  }
	
}
int main() {
	vector<int>nums = { 2,5,1,11,3,0 };
	int k;
	cin >> k;
	int ans = randselect(nums, nums.size() - k);//第k大的数,就是排好序后下标为nums.size()-k 
                                                //的数
	for (int i = 0; i < nums.size(); i++)
		cout << nums[i]<<" ";
	cout << endl;
	cout << ans;

	return 0;
}

 时间复杂度:

如果每次随机出来的数都在最右端,而我们要选的数为最小的那个,第一次递归要遍历n-1个数;

第二次要遍历n-2个数.....,所以最差情况的时间复杂度为O(N^2)。而好点的的就是我们每次随机

的数都在最中间,那么每次都折半查找,第一次找n个,第二次n/2个.....,所以其时间复杂度为O(N)

(因为我们只排序一半,不需要两边都排。不像随机快排一样,每次两边都要排序)。根据概率计

算,其时间复杂度期望为O(N)

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
舍伍德随机快速排序算法是一种基于快速排序的改进算法,它通过随机选择枢轴元素来提高排序的效率和稳定性。下面是舍伍德随机快速排序算法的编程实验介绍: 1. 首先,我们需要定义一个函数来实现舍伍德随机快速排序算法。函数的输入参数包括待排序数组和数组的起始位置和结束位置。 2. 在函数内部,我们首先需要判断起始位置是否小于结束位置,如果不是,则直接返回。 3. 接下来,我们需要选择一个随机的枢轴元素。可以使用随机数生成器来生成一个在起始位置和结束位置之间的随机索引,然后将对应的元素作为枢轴元素。 4. 将枢轴元素与数组的最后一个元素进行交换,这样可以方便地将枢轴元素放在正确的位置上。 5. 接下来,我们需要遍历数组,将小于枢轴元素的元素放在左边,大于枢轴元素的元素放在右边。可以使用两个指针,一个指向左边的起始位置,一个指向右边的起始位置。 6. 当左指针小于右指针时,我们需要进行以下操作: - 从左边开始找到第一个大于枢轴元素的元素,将其与右指针指向的元素进行交换。 - 从右边开始找到第一个小于枢轴元素的元素,将其与左指针指向的元素进行交换。 7. 重复步骤6,直到左指针大于等于右指针。 8. 将枢轴元素放在正确的位置上,即将其与左指针指向的元素进行交换。 9. 现在,枢轴元素左边的元素都小于它,右边的元素都大于它。我们可以递归地对左右两个子数组进行排序。 10. 最后,我们可以通过递归调用舍伍德随机快速排序函数来完成整个排序过程。 下面是一个示例的舍伍德随机快速排序算法的实现: ```python import random def shuffle_quick_sort(arr, start, end): if start < end: pivot_index = random.randint(start, end) arr[pivot_index], arr[end] = arr[end], arr[pivot_index] pivot = arr[end] i = start - 1 for j in range(start, end): if arr[j] < pivot: i += 1 arr[i], arr[j] = arr[j], arr[i] arr[i+1], arr[end] = arr[end], arr[i+1] pivot_index = i + 1 shuffle_quick_sort(arr, start, pivot_index - 1) shuffle_quick_sort(arr, pivot_index + 1, end) # 测试 arr = [5, 2, 9, 1, 7, 6, 3] shuffle_quick_sort(arr, 0, len(arr) - 1) print(arr) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值