他来了,他来了,他带着快速排序走来了。下面讲讲快速排序,是怎么“折磨”我的。
目录
1. 快速排序的核心思想
原理:1)从待排的序列中选取一个基准元素。
2)扫描待排的序列,将比基准数小的元素放到它的左边,大于或者等于基准数的元素放到右边,得到左右两个区间。
3)对左右两个区间重复步骤 2),直到各个区间的元素个数小于2,完成排序。
第二步中将比基准数小的元素放到左边,大于或等于的放到右边,有多种实现的方法,我先讲一种简单的,好理解的方法。上图:
上面的图对这种简单的方法做了分析,想必聪明如你们肯定是能看得明明白白滴,下面就来分析代码哈。
2. 代码分析
想必大家都看出来了,这里面有重复的步骤----对每个区间进行相同的操作了嘛,灵光一闪,你立刻就想到用递归来实现快速排序。递归,想必这也是极好的(pink老师这么说,我也学过来。)
2.1 排序前的准备
准备一个待排序的数组,调用快速排序的函数,打印排序结果的for循环。
int main()
{
//待排序数组
int arr[15] = { 44,3,38,5,47,15,36,26,27,2,46,4,19,50,48 };
//调用函数
quick_sort(arr, 15);
//打印排序好的数组
int i = 0;
for (i = 0; i < 15; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
2.2 快速排序主体函数部分
1)准备一个变量来装我们的基准元素。
2)用两个变量来表示上图中的左三角,右三角,用来判断一次排序的结束。
3)再来一个变量,用来表示是找比基准数小的元素,还是找比基准数大或者相等的元素呢。
4)循环的条件就是左三角要在右三角的左边嘛。依据上图对区间的数进行判断填入空的下标中即可。有两种情况嘛,所以需要一个变量,通过值的改变来表示是找小的,还是找大的。只要左三角和右三角重合不满足循环条件,将基准元素拿过来放到空的下标就行了。
2.3 完整代码
void quick_sort(int* arr, int x)
{
//递归结束条件
if (x < 2)
{
return;
}
//左三角和右三角
int left = 0;
int right = x - 1;
//记录基准元素的大小
int flag = arr[left];
//找比基准元素大的数还是小的数,2代表找小的,1代表找大的
int sort_way = 2;
int i;
while (left < right)
{
//找小的
if (sort_way == 2)
{
//判断大小
if (arr[right] >= flag)
{
//没找到,移动右边的三角,继续找
right--;
continue;
}
//走到这代表找到啦,就把找到的填入空的下标里面
arr[left] = arr[right];
//原来的左三角是那个空的下标嘛,让他加一下即要从左边开始找大的元素了
left++;
//改成找大的
sort_way = 1;
continue;
}
//找大的
if (sort_way == 1)
{
//做判断
if (arr[left] <= flag)
{
//没找到,移动左边的三角,继续找
left++;
continue;
}
//走到这代表找到啦,就把找到的填入空的下标里面
arr[right] = arr[left];
//原来的右三角是那个空的下标嘛,让他减一下即要从右边开始找大的元素了
right--;
//改成找小的
sort_way = 2;
continue;
}
}
//一次排序结束,将基准元素拿过来,分区间,写left还是right无所谓嘛,上面的图已经说明了退出
//循环时left和right是一样的
arr[left] = flag;
//对左边的区间重复上述操作
quick_sort(arr, left);
//对右边的区间重复上述操作
quick_sort(&arr[left+1], x - left - 1);
}
int main()
{
//待排序数组
int arr[15] = { 44,3,38,5,47,15,36,26,27,2,46,4,19,50,48 };
//调用函数
quick_sort(arr, 15);
//打印排序好的数组
int i = 0;
for (i = 0; i < 15; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
3.快速排序的优化
3.1 合理选择基准元素
想必细心的小伙伴一定发现了如果基准元素选不好,比如选了待排序列中最小的数,那么我们进行快速排序时,会找不到比他小的数,判断的次数增加了,而且还是白忙活。增加了代码的时间复杂度。于是我们可以用中学学的分层抽样(当待排数据比较多),选取一部分数据后,选择其中的中位数作为基准元素。这样就不会有头重脚轻的感觉啦。
3.2 配合插入排序
研究表明,嘿嘿谁研究的俺也不知道,当待排数的个数小于10时,用插入排序的效率会高于快速排序。所以我们在递归的前面加个判断,如果区间数目的个数小于10,咱就用插入排序。如果大于10呢就用快速排序让它的区间个数小于10了后,再来插入排序。
if (left > 10)
quick_sort(arr, left);
else
insert_sort(arr, left);
if ((x - left - 1) > 10)
quick_sort(&arr[left + 1], x - left - 1);
else
insert_sort(arr, x - left - 1);
插入排序的代码看我前面的文章讲过了哈。
4. 其他的分区间方法
黄色的代表基准元素,紫色的代表比基准元素大的数,绿色的代表比基准数小的数,橙色代表排好的数哈。
每次找到一个比基准数小的数,就将它与第一个比基准数大的数交换 。扫描完毕后再将基准数与最后一个比基准数小的数交换从而达到分区间的目的。第一次刚刚好左边的区间长度为1,就是排好了的部分。嘿嘿,是不是要比上面的复杂多了。
好啦,今天的快速排序就到这里啦,下期见!