快速排序
递归法
1、快排
快速排序算法使基于分治策略的一个排序算法,其基本思想是,对于输入的子数组 nums[left : right] 按以下3个步骤排序:
(1)分解:以 nums[left] 为基准元素将 nums[left : right] 划分成3段 nums[left: mid-1],nums[mid] 和 nums[mid+1:right],使得nums[left:mid-1] 中任何元素小于等于 nums[mid], nums[mid+1:right] 中任何元素大于等于 nums[right]。下标 mid 在划分过程中确定。
(2)递归求解: 通过递归调用快速排序算法,分别对 nums[left:mid-1] 和 nums[mid+1:right] 进行排序。
(3)合并 : 由于对 nums[left:mid-1] 和 nums[mid+1:right] 的排序是就地进行的,所以在 nums[leftmid-1] 和 nums[mid+1:right] 都已排好的序后不需要执行任何计算,nums[left:right] 就已排好序。
//递归法,双向开工,left向左 right向右
//打印函数
void PrintInt(const int* nums, int n)
{
for (int i = 0; i < n; i++)
{
printf("%5d", nums[i]);
}
printf("\n");
}
//划分函数的功能是将小于tmp的元素放在左半部分,大于tmp放在右边部分
int Partition(int* nums,int left, int right)//划分函数,i==j 时while循环推出,此时i or j左边均小于nums[i],youbianjun
{
int i = left, j = right;
int tmp = nums[i];
while (i < j)// i < j是为了不发生划分时交叉了
{
while (i < j && nums[j] > tmp)
--j;
if (i < j)
nums[i] = nums[j];
while (i < j && nums[i] <= tmp)
++i;
if (i < j)
nums[j] = nums[i];
}
nums[i] = tmp;
return i;//return j;
}
//快排
void QuickSort(int *nums, int left, int right)
{
if (left < right)
{
int mid = Partition( nums, left, right);
QuickSort(nums, left,mid - 1);
QuickSort(nums, mid + 1, right);
}
}
主函数
int main()
{
int ar[] = { 56,23,78,45,90,89,12,34,56,67,89,100 };
int n = sizeof(ar) / sizeof(ar[0]);
PrintInt(ar, n);
QuickSort(ar, 0, n - 1);
PrintInt(ar, n);
}
2、快排的优化
快
排
的
退
化
,
当
数
据
有
序
时
,
快
排
的
速
度
越
慢
,
快排的退化,当数据有序时,快排的速度越慢,
快排的退化,当数据有序时,快排的速度越慢,
分析:
快排的的运行时间于划分是否对称性有关,最坏的情况是划分中两个区域分别包含n-1个元素和1个元素的情况,Partition的计算时间为O(n),若每一步都出现这种不对称划分,计算时间为O(n^2).
解决方法: 利用随机选择策略使初始数组变的无序。
修改Partition算法,在数组还没有被划分时,可以在nums[left : right]中随机选择一个元素作为划分基准,这样可以使划分基准的选择是随机的,划分比较对称。
//随机策略,使数据更有序
int RandPartition(int* nums, int left, int left)
{
srand(time(nullptr));
int ropos = (rand() % (right - left + 1)) + left;//随机位置,加left是因为right - left是相对法,要加上此时的left
std::swap(nums[left], nums[ropos]);//交换随机位置和left的值
return Partition(nums, left, right);//调用划分函数
}
//递归法,双向开工,left向左 right向右
//打印函数
void PrintInt(const int* nums, int n)
{
for (int i = 0; i < n; i++)
{
printf("%5d", nums[i]);
}
printf("\n");
}
//划分函数的功能是将小于tmp的元素放在左半部分,大于tmp放在右边部分
int Partition(int* nums,int left, int right)//划分函数,i==j 时while循环推出,此时i or j左边均小于nums[i],youbianjun
{
int i = left, j = right;
int tmp = nums[i];
while (i < j)// i < j是为了不发生划分时交叉了
{
while (i < j && nums[j] > tmp)
--j;
if (i < j)
nums[i] = nums[j];
while (i < j && nums[i] <= tmp)
++i;
if (i < j)
nums[j] = nums[i];
}
nums[i] = tmp;
return i;//return j;
}
//快排
void QuickSort(int *nums, int left, int right)
{
if (left < right)
{
int mid = Partition( nums, left, right);
QuickSort(nums, left,mid - 1);
QuickSort(nums, mid + 1, right);
}
}
主函数
int main()
{
int ar[] = { 56,23,78,45,90,89,12,34,56,67,89,100 };
int n = sizeof(ar) / sizeof(ar[0]);
PrintInt(ar, n);
QuickSort(ar, 0, n - 1);
PrintInt(ar, n);
}
3、非递归法
利用队列或栈来实现非递归快排
#include<iostream>
#include<stdio.h>
#include<queue>
//非递归
void PrintInt(const int* nums, int n)
{
for (int i = 0; i < n; i++)
{
printf("%5d", nums[i]);
}
printf("\n");
}
int Partition(int* nums, int left, int right)//划分函数,i==j 时while循环推出,此时i or j左边均小于nums[i],youbianjun
{
int i = left, j = right;
int tmp = nums[i];
while (i < j)// i < j是为了不发生划分时交叉了
{
while (i < j && nums[j] > tmp)
--j;
if (i < j)
nums[i] = nums[j];
while (i < j && nums[i] <= tmp)
++i;
if (i < j)
nums[j] = nums[i];
}
nums[i] = tmp;
return i;//return j;
}
void QuickSort(int *nums, int left, int right)
{
std::queue<int> qu;
qu.push(left);
qu.push(right);
while (!qu.empty())//为空跳出
{
int sleft = qu.front();//取队头
qu.pop();//出队
int sright = qu.front();
qu.pop();
int mid = Partition(nums, sleft, sright);
if (sleft < mid - 1)
{
qu.push(sleft);
qu.push(mid - 1);
}
if (mid + 1 < sright)
{
qu.push(mid + 1);
qu.push(sright);
}
}
}
int main()
{
int ar[] = { 56,23,78,45,90,89,12,34,56,67,89,100 };
int n = sizeof(ar) / sizeof(ar[0]);
PrintInt(ar, n);
QuickSort(ar, 0, n - 1);
PrintInt(ar, n);
}
结
果
:
结果:
结果: