常用排序算法之快速排序
2016-04-24 23:25
246 查看
1.何为“快排”?
快速排序,即就是很快速,很便捷的一种排序嘛-!O(∩_∩)O~
但是,快速排序也并非一定是快速的。如果按照最糟糕的情况来看,它的时间复杂度几乎和冒泡排序是相同的0(n2)(是什么情况呢?)。但是,平均时间复杂度是0(NlogN)。
2.基本思想
快速排序是基于一种“二分”的思想。
它将在一组无序数中,寻找一个基准数,该数将原数组一分为二,左边的数组的数均小于该基准数,而右边的数组的数均大于该基准数。而该左,右两边的数组再依次递归进行上述操作,直至递归结束。
快速排序之所以被称为是冒泡排序的进化版,是因为冒泡排序每次比较的都是相邻的两个数,而快速排序只有在最坏的情况下才是这样。一般情况下,快排中的两数交换的距离要大得多!
3.算法模拟
原始数组
第一轮遍历
(1) 根据快排的思想,找到一个基准数,我们上面说过它可以是任意数,那么,我们姑且将数组中的第一个数作为基准数(其实不用姑且,因为地球银都这样),即将3作为基准数。
(2) 我们就开始伟大的征程了!先从右边开始j,寻找错误顺序的那个数(即比基准数小的数),然后,从左开始i,同样,寻找错误顺序的那个数(比基准数大的数)
(3) 根据上表,我们找到了a[2]=4,a[4]=1这两个站错位置的数。接着交换两个数的值(类似于冒泡排序)
(4) 还没有完,我们接下来继续找只与基准数站错位置的数。黄天不负有心银,我们找到了5这个数。此时,我们要注意的是,5的左右两边我们之前已经将其他站错位置的数已经“排好序”,这说明,两军已经成功会师i==j!再将5这个数与基准数交换位置即可。
此时,第一轮遍历完毕!a[3]的顺序已经确定。
第二轮遍历
(1) 第一次遍历,我们将原始数组一分为二,left:a[0-2],right:a[4]
(2) 因为数据少,比较容易思考(好吧,是寡人太懒),所以right的数组中的值已经排好序了。
因此,我们来看left的数组。同理,将5作为基准数!
(3) 然后,先从右边开始(想想为什么每次都是从右开始)遍历,我们找到站错位置的a[2]=1,接着,开始从左开始遍历,啊哦~没找到。注意,a[2]=1的左右两边都已经被遍历过了(两军会师i==j)。那么,将1与基准数进行交换。
此时,第二轮遍历完毕。a[2]位置已经确定!
第三轮遍历
(1) 将上一轮的数组,以a[2]进行二分,很遗憾!只剩下左半边。left a[0-1]
(2) ok,先从右开始遍历,我们找到没有站错位的数,然后,就没有然后了..。因为,基准数的右边全都站位正确。a[0]=1位置已经确定!
第四轮遍历与第五轮遍历
a[1]=2,a[4]=4
这个不用说了,直接return 返回即可。
4.核心代码
(1) 寻找基准数a[1] a[0]作为临时变量,存储每次遍历后新数组的a[1]值
(3) 从左往右开始遍历i++,找到站错位置的数a[i](比基准数a[0]大)
(4) 将会师点的数(i==j)a[i]或a[j]与基准数a[0]进行交换
5.完整代码
6.运算结果
快速排序,即就是很快速,很便捷的一种排序嘛-!O(∩_∩)O~
但是,快速排序也并非一定是快速的。如果按照最糟糕的情况来看,它的时间复杂度几乎和冒泡排序是相同的0(n2)(是什么情况呢?)。但是,平均时间复杂度是0(NlogN)。
2.基本思想
快速排序是基于一种“二分”的思想。
它将在一组无序数中,寻找一个基准数,该数将原数组一分为二,左边的数组的数均小于该基准数,而右边的数组的数均大于该基准数。而该左,右两边的数组再依次递归进行上述操作,直至递归结束。
快速排序之所以被称为是冒泡排序的进化版,是因为冒泡排序每次比较的都是相邻的两个数,而快速排序只有在最坏的情况下才是这样。一般情况下,快排中的两数交换的距离要大得多!
3.算法模拟
原始数组
0 | 1 | 2 | 3 | 4 |
3 | 2 | 4 | 5 | 1 |
(1) 根据快排的思想,找到一个基准数,我们上面说过它可以是任意数,那么,我们姑且将数组中的第一个数作为基准数(其实不用姑且,因为地球银都这样),即将3作为基准数。
(2) 我们就开始伟大的征程了!先从右边开始j,寻找错误顺序的那个数(即比基准数小的数),然后,从左开始i,同样,寻找错误顺序的那个数(比基准数大的数)
0 | 1 | 2 | 3 | 4 |
3 | 2 | 4 | 5 | 1 |
0 | 1 | 2 | 3 | 4 |
3 | 2 | 1 | 5 | 4 |
此时,第一轮遍历完毕!a[3]的顺序已经确定。
0 | 1 | 2 | 3 | 4 |
5 | 2 | 1 | 3 | 4 |
(1) 第一次遍历,我们将原始数组一分为二,left:a[0-2],right:a[4]
0 | 1 | 2 |
5 | 2 | 1 |
4 |
4 |
因此,我们来看left的数组。同理,将5作为基准数!
0 | 1 | 2 |
5 | 2 | 1 |
此时,第二轮遍历完毕。a[2]位置已经确定!
0 | 1 | 2 |
1 | 2 | 5 |
(1) 将上一轮的数组,以a[2]进行二分,很遗憾!只剩下左半边。left a[0-1]
0 | 1 |
1 | 2 |
第四轮遍历与第五轮遍历
a[1]=2,a[4]=4
这个不用说了,直接return 返回即可。
4.核心代码
(1) 寻找基准数a[1] a[0]作为临时变量,存储每次遍历后新数组的a[1]值
a[0] = a[left];//<span style="font-family: Arial, Helvetica, sans-serif;">3 3 1 2 5 4</span>(2)从右往左开始遍历j--,找到站错位置的数a[j](比基准数a[0]小)
//从右往左遍历 ,寻找不满足条件的数 3 3 1 2 5 4 while(i<j){ if(a[0]>a[j]){ break; } j--; }
(3) 从左往右开始遍历i++,找到站错位置的数a[i](比基准数a[0]大)
//从左往右遍历 while(i<j){ if(a[0]<a[i]){ break; } i++; }
(4) 将会师点的数(i==j)a[i]或a[j]与基准数a[0]进行交换
a[left] = a[i]; a[i] = a[0];(5)传送门调用
quickSort(left,i-1,a);//左半边调用 quickSort(i+1,right,a);//右半边调用
5.完整代码
#include <stdio.h>
#define N 100
void quickSort(int left,int right,int *a){
if(left>right){//即超过会师点
return;
}
int i = left;<span style="font-family: Arial, Helvetica, sans-serif;">//i,j存储当前的左右下标</span>
int j = right;
a[0] = a[left];
while(i<j){//每一轮循环
//从右往左遍历 ,寻找不满足条件的数 3 3 1 2 5 4 while(i<j){ if(a[0]>a[j]){ break; } j--; }
//从左往右遍历 while(i<j){ if(a[0]<a[i]){ break; } i++; }
if(i<j){//类似冒泡排序,交换站错位置的两个数
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
a[left] = a[i];//交换基准点与会师点的数
a[i] = a[0];
quickSort(left,i-1,a);
quickSort(i+1,right,a);
}
int main(void){
int n=5;
//scanf("%d",&n);
int a
={-1,3,1,2,5,4}; //a[0]=-1 相当于一个中转,存放基准数的临时变量
quickSort(1,n,a);
int i;
for(i=1;i<=n;i++){
printf("%-3d",a[i]);
}
return 0;
}
6.运算结果
相关文章推荐
- Java 浅拷贝和深拷贝那些事
- 20145330Java程序设计第三次实验
- iOS环信集成<1>
- 使用微软的TFS团队开发
- RNN学习笔记:Understanding Deep Architectures using a Recursive Convolutional Network
- Android 简单自定义view--倒计时
- 史上最全最强SpringMVC详细示例实战教程
- 20145319 第八周学习总结
- 剑指offer之编程(二)
- linux expect 自动登录交换机保存配置
- 关于取地址运算符&以及指针的问题
- 解析JSON
- ViewPager的滑动方法setOnPageChangeListener已被addOnPageChangeListener取代
- java算法题总结1
- UIBarButtonItem的setTitleTextAttributes方法不起作用
- 20145330第八周《Java学习笔记》
- 20145307第三次JAVA学习实验报告
- SQLZOO(select basics)writeup
- Activity典型情况下的生命周期分析
- spring PDF文档 及zip包下载地址