目录
一、荷兰国旗问题
1.问题描述:
2.思路:
3.合并实现
二、快速排序3.0(基准数随机产生)
三、快速排序思路
1.本质:
2.代码:
3.效果~
给定一个数组,随机在数组中选择一个元素,使得数组被分为三块区域,以被随机选择的元素为基准,左边的元素小于它,右边的元素大于它。
(1).我们人为划定一个界限,姑且叫他左边界吧,同理再划定一个右边界。
这个两个边界线的意义就是,下标<=边界线下标的元素小于3,下标>=边界线下标的元素大于3,中间位置在处理结束后都等于3。
(2).定义一个 i 指针从头遍历到尾部。
(3).每次 i 位置只会遇到三种情况:元素小于3,元素等于3,元素大于3。
如果元素小于3,把他放到left区域,将指针位置的元素和left下一位交换,left后移,指针后移。
如果元素等于3,把他放到中间区域,指针直接后移即可。
如果元素大于3,把他放到right区域。将指针位置元素和right前一位交换,right前移,指针不
动!!
不难发现,其实这就是一个两边区域向中间区域逼近的过程,但是要注意,遇到大于3的元素时,我们进行了这样一个操作:
将指针位置元素和有边界左边的元素交换,然后有边界扩大,目的是让这个大于3的元素被囊括到有边界>3区域中,但是,这时把一个从未遇到过的新元素扔到了指针的位置上,那么我们的 i 指针就不能后移了,继续进行下一次判断,没错就是判断这个数!!
边界判断:1.指针超出数组范围,不搞了,摊牌走人。
2.指针跑到右边大于3的区域,那么此时 i 的工作肯定已经完成了,下班走人。
1.随机函数randint
int randint(int a, int b) { return rand() % (b - a + 1) + a; }//熟悉的随机生成函数
2.分区中
vector partition(vector& arr, int L, int R) {
int i = L, left = L - 1, right = R + 1; //定义左边界有边界
int target = arr[randint(L, R)]; //默认用最后一个元素选定中间值
vector res;
res.push_back(0);
res.push_back(0);
while (i < right) {
if (arr[i] < target) {
swap(arr[i++], arr[left + 1]);
left++;
}
else if (arr[i] > target) {
swap(arr[i], arr[right - 1]);
right--;
}
//注意↑扩展有边界时i不要移动
else i++; //相等时指针后移
}
res[0] = left + 1;
res[1] = right - 1;
return res;
}//分区功能实现
3.分区结束,将边界信息存入res临时数组并返回,用于快排中递归的设计。
可以看到,我这里写的是快排3.0,那么可能会有人疑惑,凭什么就3.0了?
实际上,不是我吹,我这个快排,就是好。为什么呢?快排可以写出很多种,下面我列出几个版本:
1.快排1.0,每次都取R位置,也就是最右边的数作为基准数,将数组分为<=基准数和>基准数的两个区域。
2.快排2.0,同样也是取R位置为基准数,但是将数组分为了三个部分,类似荷兰国旗问题。
但是仔细分析,你会发现啊,他们虽然有一些微小的差别,但是最差的情况时间复杂度都是O(n^2),这种情况的原因:每次在的基准数都为数组的最右边的元素,那要是这个数就是最大的值,我快排还递归个锤子,不要面子了,和那些个选择,插入,冒泡有什么区别?
所以诞生了我们的快排3.0,只做了一行代码的改进,每次的基准数从数组中的全部元素中随机抽取。你说妙不妙,这时从“平均”的角度来讲,就不存在所谓的最差情况,哈哈,根据我们的概率学,它的算法复杂度就衡(平均)为O(n lgn)。
咳咳,不吹了,我们言归正传......
1.定义一个partitionSort函数,传入数组的引用,左边界和右边界。
2.递归条件:如果左边界 < 右边界进行递归,否则只有一种情况,左边界等于右边界,我们返回,进行回溯即可。3.递归部分设计:每次先将数组进行分区,分成三块。记录中间部分的下标,继续往左边和右边分别递归。
其实,通过这样的设计,我们可以窥透到快排的本质。那就是,每次让左边的数小于基准数,右边
的数大于基准数,此时左边子数组的最大数肯定是小于基准数的,同样,右边数的最小数也肯定大
于基准数,那么至少,以基准数为中心的三个数已经有序。那么继续重复,子数组的子数组右边最
大值又小于它的最小数......周而复始,最终根据我们的极限思维,数组一定会达到一个有序的状
态。嗯,不要问我为什么,不能再解释了。
#include
#include
#include
#include
using namespace std;
/*小功能*/
int randint(int a, int b) {
return rand() % (b - a + 1) + a;
}//熟悉的随机生成函数
void initArr(vector& arr, int N) {
for (int i = 0; i < N; i++) arr.push_back(randint(1, 100));
}//随机生成数组
void coutArr(vector& arr) {
vector::iterator it = arr.begin();
while (it < arr.end()) {
cout << *it << ' ';
it++;
}cout << endl;
}//打印数组
/*小功能*/
/*快排实现部分*/
vector partition(vector& arr, int L, int R) {
int i = L, left = L - 1, right = R + 1; //定义左边界有边界
int target = arr[randint(L, R)]; //默认用最后一个元素选定中间值
vector res;
res.push_back(0);
res.push_back(0);
while (i < right) {
if (arr[i] < target) {
swap(arr[i++], arr[left + 1]);
left++;
}
else if (arr[i] > target) {
swap(arr[i], arr[right - 1]);
right--;
}
//注意↑扩展有边界时i不要移动
else i++; //相等时指针后移
}
res[0] = left + 1;
res[1] = right - 1;
return res;
}//分区功能实现
void partitionSort(vector& arr, int L, int R) {
if (L < R) {
vector res;
res = partition(arr, L, R);
partitionSort(arr, L, res[0] - 1);
partitionSort(arr, res[1] + 1, R);
}
}//快排递归部分
/*快排实现部分*/
int main() {
srand(time(0)); //不必多说,随机种子来!
vector arr;
initArr(arr, 20);
cout << "Original array:" << endl;
coutArr(arr);
partitionSort(arr, 0, arr.size() - 1);
cout << "Processed array:" << endl;
coutArr(arr);
}