快速排序的一些典型算法
常规写法,未随机选取pivot
vector<int>sortArray(vector<int>&nums)
{
quickSorts2(nums,0,nums.size()-1);
return nums;
}
void quickSorts(vector<int>&nums,int left,int right)
{
//当左边大于右边,那么就可以跳出循环
if(left>=right)
{
return;
}
//设定划分的元素的值
int pivotIndex=partitionDouble(nums,left,right);
//继续递归进行排序
quickSorts(nums,left,pivotIndex-1);
quickSorts(nums,pivotIndex+1,right);
}
//寻找目标的中枢值
int partition(vector<int>&nums,int left,int right)
{
//选择切分的元素
int pivot=nums[left];
// 将pivot后面的元素划分成两个区间
// all in nums[left+1,j]<=pivot
//all in nums (j,i)>pivot
//遍历区间中的所有元素
//初始化 j 的值,使得两个区间初始化的时候都为空区间
//j位于第一个区间的最后一个元素
int j=left;
for(int i=left+1;i<=right;i++)
{//大放过,小交换到前面
if(nums[i]<pivot)
{
//先将j++
j++;
swap(nums,i,j);
}
}
swap(nums,left,j);
return j;
}
void swap(vector<int>&nums,int index1,int index2)
{
int temp=nums[index1];
nums[index1]=nums[index2];
nums[index2]=temp;
}
随机选用区间元素,降低对于顺序数组的时间复杂度
//寻找目标的中枢值
int partition2(vector<int>&nums,int left,int right)
{
//随机选择
randomPartiton(nums,left,right);
//选择切分的元素
int pivot=nums[left];
// 将pivot后面的元素划分成两个区间
// all in nums[left+1,j)<=pivot
//all in nums [j,i)>pivot
//遍历区间中的所有元素
//初始化 j 的值,使得两个区间初始化的时候都为空区间
//j位于第二个区间的第一个元素
int j=left+1;
for(int i=left+1;i<=right;i++)
{//大放过,小交换到前面
if(nums[i]<pivot)
{
//先将j++
swap(nums,i,j);
j++;
}
}
swap(nums,left,j-1);
return j-1;
}
void swap(vector<int>&nums,int index1,int index2)
{
int temp=nums[index1];
nums[index1]=nums[index2];
nums[index2]=temp;
}
//划分在顺序数组和逆序数组上表现很差的
//解决方法随机选择切分元素
void randomPartiton(vector<int>&nums,int left,int right)
{
int i=left+rand()%(right-left+1);
swap(nums,i,left);
}
双路快排
//双路快排:把与pivot相等的元素平均分到数组的两侧
//左边:遇到严格小于pivot的元素则纳入左边区间,否则停下
//右边:遇到严格大于pivot的元素则纳入右边区间,否则停下
//交换,左边区间和右边区间各增加一个元素
//等于pivot的元素,通过交换,平均地来到了数组两边
//寻找目标的中枢值
int partitionDouble(vector<int>&nums,int left,int right)
{
randomPartiton(nums,left,right);
//选择切分的元素
int pivot=nums[left];
// 将pivot后面的元素划分成两个区间
//all in nums[left+1,le)<=pivot;
//all in nums(ge,right]>=pivot;
//设置le和ge的初值,使得,两歌区间初始化为空区间
int le=left+1; //le:less equals
int ge=right; //ge:greate eauqls
while(true)
{
//遇到比pivot的大的时候停下来
while(le<=ge&&nums[le]<pivot)
{
le++;
}
//遇到比pivot的小的时候停下来
while(le<=ge&&nums[ge]>pivot)
{
ge--;
}
if(le>=ge)
{
break;
}
//le 来到了第一个大于等于pivot的位置
//ge 来到了第一个小于pivot的位置
swap(nums,le,ge);
le++;
ge--;
}
swap(nums,left,ge);
return ge;
}
三路快排
//三路快排,把与pivot相等的元素挤到中间
//情况1:当看到的元素<piviot时,把它交换到 等于pivot的区间 的第一个位置
//情况2:当看到的元素=pivot时,什么都不用做,看下一个元素
//情况3:当看到的元素>pivot时,把它交换到大于pivot的区间的前一个位置
//好处,把数值相等的元素挤到中间,每一次可以使得确定的元素变多,以后递归处理的区间长度变少了
void quickSorts2(vector<int>&nums,int left,int right)
{
//当左边大于右边,那么就可以跳出循环
if(left>=right)
{
return;
}
randomPartiton(nums,left,right);
int pivot=nums[left];
// 将pivot后面的元素划分成三个区间
//all in nums[left+1,lt)<pivot;
//all in nums[lt,i)=pivot;
//all in nums(gt,right]>pivot;
//设定边界的初始条件
int lt=left+1;//lt=less than
int gt=right; //gt= greater than
int i=left+1;
while(i<=gt)
{
//当元素小于当前划分的值时
if(nums[i]<pivot)
{
//交换到等于pivot区间的第一个位置
swap(nums,i,lt);
lt++;
i++;
}
else if(nums[i]==pivot)
{
//划分在第二个区间里
i++;
}
else{
//划分到数组的末尾
swap(nums,i,gt);
gt--;
}
}
//交换 pivot
swap(nums,left,lt-1);
//继续递归进行排序
quickSorts2(nums,left,lt-2);
quickSorts2(nums,gt+1,right);
}
};