代码是全的,全部复制粘贴就可以使用。用vs的小伙伴记得把scanf的_s安全检查去掉哦。这是我的第一篇博客,如果有不足之处希望大家指正在留言区指正。
.
偶也是一个新手,在自学数据结构时了解到了这个排序算法,自己就产生了很浓厚的兴趣然后就研究了一下,不过这个排序算法据说是面试常考的一个题目,以下是我个人的一些心得。冒泡排序和快排一样,都是属于交换类的排序, 快话不多说,先举个栗子。
比如我们对16,12,14,13,15这5个数进行快排(从小到大)。
low为数组下限位置,high为数组上限位置,important为待排序字符在排序完成的数列中的位置(即满足左边的数都小于他,右边的数都大于他的位置)。
.
先定义low=1和high=数组的长度,令important=low,先移动high(条件满足情况下),将important处的数和high处的数进行比较,因为16>15,所以进行15和16交换,交换完成后开始移动low(条件满足情况下),因为没有数比16大,low会不断+1所以最后high和low会停留在5这个位置上,5就是我们要找的位置,所以important最后=5,16在5位置时6的左边的数都小于它,右边的数都大于它。
排序前 | 16 | 12 | 14 | 13 | 15 |
---|---|---|---|---|---|
排序后 | 15 | 12 | 14 | 13 | 16 |
初始 | low | high | |||
排序后 | low high |
.
现在16的位置已经定了,我们开始对16的左半部分进行排序。我们先对15进行排序,使得15左边的数都小于他,右边的数都大于他,这个时候low=1和high=4,important=low, 先移动high(条件满足情况下 ),同上,15和13进行交换,然后移动low,因为没有数比15大,所以最后low=high=4,15在4位置时左边的数都小于他,右边的数都大于他。
排序前 | 15 | 12 | 14 | 13 | 16 |
---|---|---|---|---|---|
排序后 | 13 | 12 | 14 | 15 | 16 |
初始 | low | high | |||
排序后 | low high |
.
现在16,15的位置都已经定了,我们让low=1,high=3,important=12,判
断12<14,所以high-1,因为13>12,所以进行交换,13和12交换。13在2位置时,左边的数都小于他,右边的数都大于他。
排序前 | 13 | 12 | 14 | 15 | 16 |
---|---|---|---|---|---|
排序后 | 12 | 13 | 14 | 15 | 16 |
初始 | low | high | |||
排序后 | low high |
.
同上,但是不做任何交换。因为12已经满足左边的数小于他,右边的数大于他。
排序前 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|
排序后 | 12 | 13 | 14 | 15 | 16 |
初始 | low | high | |||
排序后 | low high |
.
1.将每一个数都放置在一个正确的位置上,使得该数的左边的所有数都小于他,右边的所有数都大于他,当每个数都满足这个条件时,数列排序完成。
2. 因为对每一步的处理有相似性,所以可以用递归和分治方法实现。
3. 将一个数组通过产生important拆分成2个小数组段,同时对这两个数组进行排序,提高效率
typedef struct data
{
int number[10];
int length;
}Data;
int found_important(Data* p, int low, int high)//寻找中点
{
int important;
int temp;
important = p->number[low];
while (low < high)
{
while (low<high && important>p->number[high])//找右边位置(控制排序方向)
high--;
temp= p->number[high];//交换数据,确保imporant的右边都小于他
p->number[high] = p->number[low];
p->number[low] = temp;
while (low<high && important<=p->number[low]) //找左边位置(控制排序方向)
low++;
temp = p->number[high];//交换数据,确保imporant的左边都大于他
p->number[high] = p->number[low];
p->number[low] = temp;
}
return low;//当low==high时,退出循环
void quickSort(Data *p,int low,int high)
{
int important;
if (low < high)
{
important = found_important(p, low, high);//记录中点位置
//分治思想
quickSort(p, low, important-1);//确定左边数列顺序
quickSort(p, important+1, high);//确定右边数列顺序
}
}
int main()//快速排序(从大到小)
{
Data array;
Data* p=&array;
int i;//用于控制循环
printf("请输入要排序的数组个数\n");
scanf_s("%d", &p->length);
printf("请输入待排序的数组\n");
for (i = 1; i < p->length+1; i++)
{
scanf_s("%d",&p->number[i]);
}
quickSort(p, 1, p->length);
printf("数据排序后是:\n");
for (i=1;i<p->length+1;i++)
{
printf("%5d",p->number[i]);
}
return 0;
}
到这里就差不多结束了
我们现在来看一下程序的复杂度吧!
假设一个待排序的数组有n个元素
一次寻找important的循环需要遍历数组一次,所以定位important的函数found_important的时间复杂度是O(n);
最好情况:
每一次都是对数组的对分,那么通过多个线程(提高速度的关键)对同时对子问题进行运算,那么就要运行log(以2为底) n次程序,所以最终的复杂度就是O(n×log(以2为底)n)。
最坏情况:
(上述栗子就是最坏情况)
每次的impotent都是子数组的最大或最小元素,使得数组无法被拆分运算,那么总共要运行n次,所以最终的时间复杂度为(n×n)。
平均上述情况,该算法的复杂度最终为O(n*log(以2为底)n)。
最好情况:
即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1)。
最坏情况:
栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n))。
因为存在对数,所以当数据量很大时,快排的优势就特别明显了。
因此,该排序方法被认为是目前最好的一种内部排序方法。
参考资料:
1.快速排序百度百科
2.《大话数据结构》 -成杰著