对于时间复杂度为O(nlogn)的高级排序算法,虽然排序原理了解,但是落实到代码上还是会有一写生疏,最近在刷leetcode时候看到一个排序题,所以有重新研究了一下快排的代码,想学习或者重温排序算法,可以去这里练习。快速排序使用了分治的思想,先选取一个主原pivot,比主原小的放在左边,比主原大的放在右边,在递归的对左边和右边进行同样的操作,最后就可以得到一个有序数组,关于排序的算法,可以参看我之前的文章,里边介绍了各种排序算法的操作步骤,配合动图可以快速的理解。
快排首先要选取主原,主原的选取会直接影响算法的性能,慕课上浙大数据结构中,主原选取的是数组前中后三个数的中位数作为主原
如果仅仅用于刷题我们可以使用随机数来选取主原,虽然随机数的生成也会耗时,但是作为刷题或者面试,我们只要展示出选取主原的思想就可以了。
我们首先使用随机数生成函数rand()%(r-l+1)产生一个在0到数组长度之间的整数,代表主原的位置,然后,把主原放在数组的最右边,之后定义两个指针i,j同时指向数组的初始位置,j一直向后遍历,如果遍历的值小于主原,那么与i与j交换,同时i指向下一个需要交换的位置,注意i所指的位置是大于主原的,他代表下一个小于主原的值变换后的位置,i左边都小于主原。因为最后一个是主原,当j遍历到倒数第二个值则停止遍历,我们看一下此时的i指针是大于主原的,所以将i和主原nums[r]位置互换,这样i的位置左边小于主原,右边大于主原,第一趟分治结束,之后对左边的和右边的部分数组分别进行递归,得到有序数组nums。
代码如下:
class Solution {
public:
void qsort(vector<int>& nums , int l ,int r){
//递归终止条件,如果左边界不小于右边界,递归终止
if(l>=r) return;
//通过随机数选取主原位置,主原值为povit
int pos = rand()%(r-l+1) + l , povit = nums[pos];
//将主原放在数组最右边
swap(nums[pos],nums[r]);
//定义两只指针i,j 都指向数组的最左端
int i =l;
//如果当前值小于主原,则当前位置与i互换,i自增指向下一个要互换的位置
for(int j= l ; j<r;j++){
if(nums[j] < povit){
swap(nums[i], nums[j]);
i++;
}
}
//将主原和i互换,此时完成一趟操作,i左边小于主原,i右边大于主原
swap(nums[i],nums[r]);
//对左右两个子数组进行同样的操作
qsort(nums,l,i-1);
qsort(nums,i+1,r);
}
vector<int> sortArray(vector<int>& nums) {
//随机数种子
srand(time(0));
//调用快排函数
qsort(nums,0,nums.size()-1);
return nums;
}
};