递归实现快速排序
有三种实现:双向遍历-左右挖坑互填、双向遍历-左右交换、单向遍历
这三种方法只有partition不一样,实现递归调用的函数是一样的
(对于循环不变量理解在LC34题笔记中)
c++/python3/java (1)大根堆–调库 (2)手撸大根堆 (3)快排3种写法 - 数组中的第K个最大元素 - 力扣(LeetCode)
(80条消息) 算法学习 | 快速排序(单向扫描分区法、双向扫描分区法、三指针分区法)+三种优化方法(java描述)_双向扫描算法_RUI_NOBUG的博客-CSDN博客
双向遍历-左右挖坑互填
// 双向遍历-左右挖坑互填
int doublePartition01(vector<int>& arr,int l,int r)
{
int pivot = arr[l]; //选取左侧元素为轴
while (l<r) // 注意此处是<,而不是<=
{
// 此处必须要先从右侧开始,因为l处的数已经存到了pivot中,故此时l处已经是一个坑了
// 而r处不是,故需要将r处的数填到l处,而不能反过来
// 若是将右侧作为轴,则应该先从左侧开始
while(l<r && arr[r]>=pivot)
{
r--;
}
// right指向最后一个小于pivot的元素,将其填到左侧,然后右侧产生一个坑
arr[l] = arr[r];
while(l<r && arr[l]<=pivot)
{
l++;
}
// 此时left指向第一个大于pivot的元素
arr[r] = arr[l];
}
arr[l] = pivot;
return l;
}
// 循环不变量:
// r右侧(不包括r)都大于等于pivot
// l左侧(不包括l)都小于等于pivot
// 并且,当l==r时,这个位置的数一定已经填到过其他位置了(即此处一定是一个坑)
// 因为有两种情况,
// 即while(l<r && arr[r]>=pivot) 这个循环 或 while(l<r && arr[l]<=pivot) 这个循环得到l=r
// 如果是while(l<r && arr[r]>=pivot)得到了l=r,
// 则除了完全有序这种情况,此时l处的数已经被记录到pivot中,相当于l处的数已经填到过别的位置,故l处是一个坑
// 其他情况下前面一定进入过while(l<r && arr[l]<=pivot)这个循环,则l处的数已经填到过别的位置,故l处一定是一个坑
// 同理可以知道若是另一个循环得到l=r,则一定不是完全有序的情况,故r处也一定是一个坑
void doubleQuickSort01(vector<int>& arr,int l,int r)
{
if(l>=r)
{
return;
}
int pivotIndex = doublePartition01(arr,l,r);
doubleQuickSort01(arr,l,pivotIndex-1);
doubleQuickSort01(arr,pivotIndex+1,r);
}
双向遍历-左右交换
// 双向遍历-左右交换
int doublePartition02(vector<int>& arr,int l,int r)
{
int temp_l = l;
int pivot = arr[l];
while (l<r)
{
while (l<r && arr[r]>=pivot)
{
r--;
}
while (l<r && arr[l]<=pivot)
{
l++;
}
if(l<r)
{
swap(arr[l],arr[r]);
}
}
swap(arr[temp_l],arr[l]);
return l;
}
// 循环不变量:
// r右侧(不包括r)都大于等于pivot
// l左侧(不包括l)都小于等于pivot
// 此时得到l=r仍然有两种情况while (l<r && arr[r]>=pivot)和while (l<r && arr[l]<=pivot)
// 如果是第一个循环,则r=l处元素 < pivot,且右侧和左侧均满足循环不变量,故和最左侧交换即可
// 如果是第二个循环,因为已经退出了上一个循环则r=l处元素 < pivot,故和第一种情况一样
void doubleQuickSort02(vector<int>& arr,int l,int r)
{
if(l>=r)
{
return;
}
int pivotIndex = doublePartition02(arr,l,r);
doublePartition02(arr,l,pivotIndex-1);
doublePartition02(arr,pivotIndex+1,r);
}
单向遍历
// 单向遍历
int singlePartition(vector<int>& arr,int l,int r)
{
int pivot = arr[l]; //选取左侧元素为轴
int left = l+1;
int right = r;
while (left <= right)
{
if(arr[left]<=pivot)
left++; //小于轴元素,则left+1
else
{
// 大于轴元素,则交换左右指针元素,并让右指针-1
// 而不让左指针+1,因为交换过来的仍需要判断
swap(arr[left],arr[right]);
right--;
}
}
swap(arr[l],arr[right]);
return right;
}
// 循环不变量是:
// right右侧元素(不包括right)都大于pivot
// left左侧元素(不包括left)都小于等于pivot
// 最后left在right右侧一个位置
// 满足左指针指向第一个大于pivot的元素
// 右指针指向最后一个小于等于pivot的元素,故和轴交换即可
void singleQuickSort(vector<int>& arr,int l,int r)
{
// 当左边界>=右边界时结束程序,因为此区间不需要排序
if (l >= r)
{
return;
}
int pivotIndex = singlePartition(arr,l,r);
singleQuickSort(arr,l,pivotIndex-1);
singleQuickSort(arr,pivotIndex+1,r);
}