- 本周的学习内容包括:
1.堆排序
主要包括完全二叉树概念及其数组表示、堆的概念与堆的建立实现、堆排序、优先级队列
2.快速排序
主要包括快速排序的算法设计思路、快速排序的递归实现、快速排序的优化
3.归并排序
主要归并排序的设计思路、递归实现、循环实现
4.STL中排序sort
正文
今天学习:主要包括快速排序的算法设计思路、快速排序的递归实现、快速排序的优化
1、算法设计思路
快速排序一般是基于递归实现的排序算法。在一趟排序中,将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再递归重新对这两部分进行快速排序。
示意图:
给定一个数组,将其变为右边区间内每一个元素均比左边大,选定一个元素,通过调整,使当前元素左边均比他小,右边元素均比他大。
给定一个数组,将其变为右边区间内的每一个元素均比左边大
- 选定第一个元素为pivot;
- 给定两个数组指针,low和high,low从数组第一个元素,high为数组最后一个元素。
- high不断向前查找,发现比选定元素小,就与low指向元素交换;
- low不断向后查找,发现比选定元素大,就与high指向元素交换
- 不断循环3和4 ,直到low>=high
举例:
代码实现上面算法:
void swap(int* a, int* b)
{
int item = *a;
*a = *b;
*b = item;
}//交换函数,a,b为指针
//对a数组从low到high区间拆分成大小两半
int pivot_sperate(int *a, int low, int high)
{
int pivot = a[low];//区间内第一个元素设置为pivot
while (low < high)
{
while (low < high && a[high] >= pivot) high--;
swap(&a[low], &a[high]);
while (low < high && a[low] <= pivot) low++;
swap(&a[high], &a[low]);
}//将比pivot大的交换到右面,小的放在左边
return low;
}//返回最终pivot值对应数组下标
2、快速排序
- 快速排序一般基于递归实现的排序算法。具体思路如下:
- 选定一个合适的值(理想情况下中值最好,但实现中一般使用数组第一个值),称为piovt。
- 基于povit,将数组分为两个部分,较小的分在左边,较大的分在右边。
- 对两个子数组分别递归重复上述过程,直到每个数组只有一个元素。
- 排序完成
快速排序具体分析
快速排序代码实现:
void quick_sort(int *a, int low, int high)
{
int pivot;
if (low < high)
{
pivot = pivot_sperate(a, low, high);
quick_sort(a, low, pivot - 1);
quick_sort(a, pivot + 1, high);
}//继续递归排序pivot值左右区间
}
快速排序代码测试:
#include<stdio.h>
#include<malloc.h>
#include<string.h>
void swap(int* a, int* b)
{
int item = *a;
*a = *b;
*b = item;
}//交换函数,a,b为指针
//对a数组从low到high区间拆分成大小两半
int pivot_sperate(int *a, int low, int high)
{
int pivot = a[low];//区间内第一个元素设置为pivot
while (low < high)
{
while (low < high && a[high] >= pivot) high--;
swap(&a[low], &a[high]);
while (low < high && a[low] <= pivot) low++;
swap(&a[high], &a[low]);
}//将比pivot大的交换到右面,小的放在左边
return low;
}//返回最终pivot值对应数组下标
void quick_sort(int *a, int low, int high)
{
int pivot;
if (low < high)
{
pivot = pivot_sperate(a, low, high);
quick_sort(a, low, pivot - 1);
quick_sort(a, pivot + 1, high);
}//继续递归排序pivot值左右区间
}
int main()
{
int a[105] = { 4, 9, 1, 3, 5, 7, 6, 2, 8 };
int i;
quick_sort(a, 0, 8);//快速排序
for (i = 0; i < 9; i++)
{
printf("%d ", a[i]);//输出
}
printf("\n");
return 0;
}
测试结果:
快速排序复杂度分析:
- 最好的情况:
每次取得的数字刚刚好能够平分数组
递归遍历过程算法复杂度O(logn)
依据pivot的数组移动复杂度O(n)
综上,最优算法复杂度O(nlogn)
- 最差情况:
每次取得数组刚刚好的最大或最小数,排序过程趋向于冒泡排序
故最差算法复杂度O(n^2)
空间复杂度:O(1)
他是不稳定的排序算法:
3、快速排序优化
分三种情况,随机数组,升序数组,降序数组,重复数组。
在一些特定情况下,快速排序的时间复杂依然趋于n^2. 像升序和降序以及重复数组。
所以如何将之从n^2过度到nlog n呢?
可以从以下几个方面进行思考,从随机选取pivot、三位取中选取pivot、聚集相等元素等方面考虑,也是三个方法。
- 随机选取pivot
考虑到对于升序和降序数组,由于我们选取pivot的时候,默认选取第一个元素,使得序列划分非常不均等。
代码:
void swap(int* a, int* b)
{
int item = *a;
*a = *b;
*b = item;
}
int pivot_sperate(int *a, int low, int high)
{
int pivot_idx = (rand() % (high - low)) + low;//从low到high中随机选取一个数字作为pivot
int pivot;
swap(&a[pivot_idx], &a[low]);//交换随机选取的pivot和low的值
pivot = a[low]; //将随机选取的值作为pivot
while (low < high)
{
while (low < high && a[high] >= pivot) high--;//当low<high且high的值>pivot时,high左移
swap(&a[low], &a[high]);//发现high的值<pivot时,交换两则的值;
while (low < high && a[low] <= pivot) low++;//当low<high且low的值<pivot时,low右移
swap(&a[high], &a[low]);//发现low的值>pivot时,交换两则的值;
}
return low;//返回此时pivot的在数组中的下标
}
效果:
- 三位取中选取pivot
代码:
void swap(int* a, int* b)
{
int item = *a;
*a = *b;
*b = item;
}
int pivot_sperate(int *a, int low, int high)
{
int mid = (high + low) / 2;
int pivot;
if (a[mid] > a[high])
{
swap(&a[mid], &a[high]);
}
if (a[low] > a[high])
{
swap(&a[low], &a[high]);
}
if (a[mid] > a[low])
{
swap(&a[mid], &a[low]);
}
pivot = a[low];//将三位取中的值作为pivot
while (low < high)
{
while (low < high && a[high] >= pivot) high--;
swap(&a[low], &a[high]);
while (low < high && a[low] <= pivot) low++;
swap(&a[high], &a[low]);
}
return low;//返回pivot的下标作为分成两半的算法结果
}
void quick_sort(int *a, int low, int high)
{
int pivot;
if (low < high)
{
pivot = pivot_sperate(a, low, high);
quick_sort(a, low, pivot - 1);
quick_sort(a, pivot + 1, high);
}
}
效果:
- 聚集相等元素
示意图:
代码:
void swap(int* a, int* b)
{
int item = *a;
*a = *b;
*b = item;
}
int pivot_sperate(int *a, int low, int high)
{
int pivot;
int mid = (high + low) / 2;
if (a[mid] > a[high])
{
swap(&a[mid], &a[high]);
}
if (a[low] > a[high])
{
swap(&a[low], &a[high]);
}
if (a[mid] > a[low])
{
swap(&a[mid], &a[low]);
}
pivot = a[low];
while (low < high)
{
while (low < high && a[high] >= pivot) high--;
swap(&a[low], &a[high]);
while (low < high && a[low] <= pivot) low++;
swap(&a[high], &a[low]);
}
return low;
}
void quick_sort(int *a, int low, int high)
{
int i, pivot, recursion_pos, item;
if (low < high)
{
pivot = pivot_sperate(a, low, high);
recursion_pos = pivot - 1;
for (i = pivot - 1; i >= low; i--)
{
if (a[i] == a[pivot])
{
a[i] = a[recursion_pos];
a[recursion_pos--] = a[pivot];
}
}//左半边与pivot相等的元素靠右边
//只递归从low到不与pivot相对应值相等的最好一位
quick_sort(a, low, recursion_pos - 1);
recursion_pos = pivot + 1;
for (i = pivot + 1; i < high; i++)
{
if (a[i] == a[pivot])
{
a[i] = a[recursion_pos];
a[recursion_pos++] = a[pivot];
}//右半边与pivot对应值相等元素靠左放
}
quick_sort(a, recursion_pos + 1, high);
}//只递归pivot对应值不相等的位置到high的部分
}
效果:
这种方式对各种数组都是最好的方法!!!万德福!终于明白为啥有些算法毫无根据喜欢取中值啦 ,哈哈哈。