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

本文详细介绍了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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值