文章目录
- 一、快速排序算法的基本思想
- 二 快速排序的代码实现
- 三 快速排序的退化
- 四 非递归快速排序
一、快速排序算法的基本思想
快速排序算法是基于分治策略的一种排序算法。其基本思想是对数组nums[left:right]进行如下操作:
1 选取一个基准值mid(通常选为nums[left])将数组分为三个子数组nums[left:mid-1],nums[mid]和nums[mid+1:right] ,将原数组中所有小于等于nums[mid]的放到nums[left:mid-1]中,将原数组中所有大于nums[mid]的放到nums[mid+1:right]中。
2 通过递归调用快速排序算法,分别对nums[left:mid-1]和nums[mid+1:right]进行排序。
3 由于所有排序都在原数组中就地就行,所以在对nums[left:mid-1]和nums[mid+1:right]排序完成后,原数组已经有序。
如图所示,以ar[] = { 9,10,8,6,7,9,5,2 }为例
将数组首元素设置为基准值,定义两个指针分别指向数组首元素和尾元素,然后我们用j所指向的值和tmp进行比较,如果j所指向的值比tmp大,j继续向前移动,否则,我们交换j和i所指向的值,接着,我们操作i,如果i指向的值小于等于tmp,i继续向前移动,否则,我们交换i和j所指向的值,直到i和j相遇。
第一趟排序,由于j所指向的值小于tmp,所以我们交换i和j所指向的值,让i指针向后移动
第二趟排序,由于i所指向的值大于tmp,交换i和j所指向的值,让j指针向前移动
第三趟排序,由于j所指向的值小于tmp,所以我们交换i和j所指向的值,让i指针向后移动
第四趟排序,i在移动过程中,指向的值均小于等于tmp,所以i指针一直走,直到i和j相遇。此时
i和j所指向的位置即为基准值所在位置,此时上面所说的第一点完成,第二步同理,只需对基准值左右各进行如此操作,排序完成。
二、快速排序的代码实现
//划分函数,功能将是小于等于tmp的元素放到数组左边,大于tmp的放右边
int Partition(int* nums, int left, int right)
{
int i = left, j = right;
int tmp = nums[i];
while (i < j)
{
while (i < j && tmp < nums[j]) --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;
}
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[] = { 9,10,8,6,7,9,5,2 };
int n = sizeof(ar) / sizeof(ar[0]);
QuickSort(ar, 0, n-1);
for (auto x : ar)
{
cout <<x << " ";
}
cout << endl;
return 0;
}
代码运行结果如下
三、快速排序的退化
通过上面例子可以看出来,快速排序的运行时间与划分是否对称有关。数组越无序,快排速度越快。最坏情况元素全部有序时间复杂度为O(n2) 最好情况基值恰好为中值时间复杂度为O(nlog2n),所以基准值的选取十分重要,有两种解决办法1 随机选择策略 2 三位取中法。
1 随机选择策略,通过修改算法 Partition,可以设计出采用随机选择策略的快速排序算法。在快速排序算法的每一步中,当数组还没有被划分时,可以在nums[left:right]中随机选出一个元素作为划分基准,这样可以使划分基准的选择是随机的,从而可以期望划分是比较对称的。
//随机选择策略
int RandomPartition(int* nums, int left, int right)
{
srand(time(nullptr));
int rapos = rand() % ((left + right) / 2);
std::swap(nums[left], nums[rapos]);
return Partition(nums, left, right);
}
2 三位取中法,我们只需在首中尾三个数据中,选择中间的值作为基准值进行排序,即可提高排序效率。
//三位取中法
int MedionThree(int* nums, int left, int right)
{
static int mid = (left + right) / 2;
if (nums[left] > nums[mid])
{
if (nums[mid] > nums[right])
return mid;
else
{
if (nums[right] > nums[left])
return left;
else
return right;
}
}
else
{
if (nums[left] > nums[right])
return left;
else
{
if (nums[right] > nums[mid])
return mid;
else
return right;
}
}
}
int MedionPartition(int* nums, int left, int right)
{
MedionThree(nums, left, right);
return Partition(nums, left, right);
}
四、非递归快排
我们可以采用队列或栈来进行
void queue_QuickSort(int* nums, int left, int right)
{
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);
}
}
}
感谢观看!