快速排序的划分方法(5种)
自己做的笔记,来自看过的博客和刷题的时候的发现。
划分的方法
对于元素进行比较,只有三种结果 >/=/<
所以我们可以假设<pivot的元素是蓝色,==是白色,>是红色
数组划分完之后应该是 蓝/蓝白+pivot+红/红白
1、单指针从左向右扫描跳过 蓝白
最终划分结果: 蓝白混合+pivot+红
int partition(vector<int>& arr, int l, int r) {
int pivot = arr[l]; //最左元素 选为pivot
int pivot_pos = l;//先记录位置
while (l <= r) // 当左右指针交错时退出循环
{
if (arr[l] <= pivot)
++l;//遇到>pivot的元素(红色),就停下
else
{
swap(arr[l], arr[r]); // 否则左右指针内容交换
--r; // 右指针--
}
}
swap(arr[pivot_pos], arr[r]); // 退出循环时最后把pivot与r交换,此时l与r交错,l指向右部分第一元素,r指向左部分最后元素
return r;//返回当前pivot位置
}
void quickSort(vector<int>& arr, int begin, int end) {
if (end > begin) //递归终止条件,即只有一个元素,或 end<begin(<0,无元素)时退出
{
int pos = partition(arr, begin, end);
quickSort(arr, begin, pos - 1);//继续划左部分
quickSort(arr, pos + 1, end);//继续划分右部分
}
}
注:也可以试试只跳过蓝 最终结果会是:蓝+pivot+红白混合
2、单指针从左向右扫描跳过 红白 (第一种变种)
最终划分结果:蓝+pivot+红白混合
int partition(vector<int>& arr, int l, int r) {
int& pivot = arr[r]; //最右元素 选为pivot
int i = l - 1;//i指向蓝色部分最后一个元素,初始化为l-1表示刚开始蓝色部分元素0个
while (l <= r) //l用于遍历从最左遍历到最有
{
if(arr[l] < pivot || l==r)//遇到<pivot的元素(蓝色) 或遍历到pivot,就停下
{
++i;//暂时进入红色部分,因为此时l指向蓝色元素
swap(arr[i], arr[l]); //互换,此时i指向蓝色部分最后一个元素
}
++l;
}
return i;//返回当前pivot位置
}
void quickSort(vector<int>& arr, int begin, int end) {
if (end > begin) //递归终止条件,即只有一个元素,或 end<begin(<0,无元素)时退出
{
int pos = partition(arr, begin, end);
quickSort(arr, begin, pos - 1);//继续划左部分
quickSort(arr, pos + 1, end);//继续划分右部分
}
}
注:也可以试试只跳过红,碰到蓝白停下 最终结果会是:蓝白混合+pivot+红
3、双指针 互换
最终划分结果:蓝白混合+pivot+红白混合
int partition(vector<int> &arr, int l, int r) //进入这个函数必须要至少要2个元素
{
int pivot = arr[l]; //最左元素选做pivot
int pivot_pos = l;
++l;
while (l <= r) { // 当左右指针交错时退出循环
//交错的检测要先放在前面
//因为如果l==r且此时指向数组最后一个元素,执行了++l后超出了数组边界
//此时l>r可以通过检测停下循环,防止arr[l] <= pivot这个条件访问数组越界
while (l <= r && arr[l] <= pivot) ++l; //两指针交错 或者 找到红色元素 ,就停下
while (l <= r &&arr[r] > pivot ) --r; //两指针交错 或者 找到蓝色元素 ,就停下
if (l < r) { // 如果满足交换条件则交换左右元素
swap(arr[l], arr[r]);
}
}
swap(arr[r], arr[pivot_pos]); // 最后把主元与右指针的元素交换
return r;
}
void quickSort(vector<int>& arr, int begin, int end)
{
if (end > begin) {
int pos = partition(arr, begin, end);
quickSort(arr, begin, pos - 1);
quickSort(arr, pos + 1, end);
}
}
注:可以试试 【蓝白混合+pivot+红】 或【蓝+pivot+红白混合】
4、抽位填充(双指针变种)
最终划分结果: 蓝白混合+pivot+红白混合
int quickSort(vector<int> &arr, int l, int r) {
int temp = arr[l];//l位置元素抽出来,做标兵,l位置可当作空位随意填入
while (l < r) {
while (l < r && arr[r] >= temp) r--;//找到<的元素(蓝),就停下
arr[l] = arr[r];//填入l之后,r当前位置作新的空位
while (l < r && arr[l] <= temp) l++;//找到>的元素(红),就停下
arr[r] = arr[l];//填入r之后,l当前位置作新的空位
}
arr[l] = temp;
return l;
}
可以试试 【蓝白混合+pivot+红】 或【蓝+pivot+红白混合】
5、三指针(唯一一个 蓝白红三段分明)
最终划分结果: 蓝+白+红
注:白色部分含pivot,位置随意,因为函数不需要返回pivot,只需返回白色部分的第一个和最后一个元素
void partition(vector<int>& arr, int l, int r, int &equal_begin, int &equal_end) //注意不需要返回pivot位置了
{
int pivot = arr[l]; // 最左元素选为pivot
int equal = l; // equal是“等于”指针,指向白色部分的第一个元素,因为pivot等于自身,所以equal初始化指向pivot
while (l <= r) { // 当左右指针相等时退出循环
if (arr[l] == pivot) //若等于pivot,则跳过
{
++l;
}
else if (arr[l] < pivot) { // 若小于主元
swap(arr[equal], arr[l]); // 将“等于”指针与左指针的元素交换
++equal; // “等于”指针++
++l; // 左指针++
}
else { // 若大于主元
swap(arr[l], arr[r]); // 将左指针与右指针的元素交换
--r; // 右指针--
}
}
//退出循环后l指向 由于l跳过白色元素,所以l会指向白色部分最后一个元素的下一个,即红色部分第一个
//r与l交错,指向白色部分最后一个
//equal指向白色部分第一个
equal_begin = equal; // equal 指向 白色部分第一个元素
equal_end = l-1; // r/l-1 指向 白色部分最后一个元素
}
void quickSort(vector<int>&arr, int begin, int end) {
if (end > begin) {
int pos1;
int pos2;
partition(arr, begin, end, pos1, pos2);
quickSort(arr, begin, pos1 - 1);
quickSort(arr, pos2 + 1, end);
}
}