啊哈算法理解快速排序
文末进行关键代码解读
#include <stdio.h>
#include <stdlib.h>
int n,a[101];
void quicksort(int left,int right)
{
int i,j,t,temp;
if(left>right) return;
temp=a[left];//temp中存的就是基准数
i=left;
j=right;
while(i!=j)
//这块循环的终止条件就是保证i==j
//那么i左侧所有数据全部小于等于a[left];
//i右侧所有数据全部大于a[left]
{
//顺序很重要,从右边开始找
//为什么一定要先从右边往左边寻找呢?
//往下看 有详解
while(a[j]>=temp&&i<j)
j--;
while(a[i]<=temp&&i<j)
i++;
//如果i==j 说明j的左侧所有数据全部小于等于a[left]
//那么i,j所停的位置就是a[left]要填入的位置
if(i<j)
{
t=a[i];
a[i]=a[j];
a[i]=t;
}
}
//基数归位
a[left]=a[i];
a[i]=temp;
quicksort(left,i-1);//继续处理左边的
quicksort(i+1,right);//继续处理右边的
}
int main()
{
int i,j,t;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
quicksort(1,n);//调用快速排序
//输出排序后结果
for(i=1;i<=n;i++)
printf("%d ",a[i]);
getchar();getchar();
return 0;
}
为什么left位置是基准点 要从右向左寻找呢?
这么想 如果说先从左边开始寻找的话(我先拿具体例子说明)
!!那么i所停留的位置一定是大于a[left]值数据的位置或者是j位置!!
①停留在j位置:(5 4 3 2 1数据) 走到了j位置 那么i等于j跳出循环 之后a[left]=a[i]=1 a[i]=temp=5 显然不正确
② 如果说停留的位置是大于a[left]值数据的位置(例如6 1 2 7 9或者6 1 7 2 9)
如果是6 1 2 7 9的话,那么之后j向左移锁停留的位置一定也是7位置(由于i==j跳出循环),最终基数归位a[left]=a[i]=7 a[i]=temp=6 显然不正确
如果是6 1 7 2 9的话,我们发现最终结果是正确的
这是反例说明 其实从根上去想其中的逻辑的话,如果基准点是最左边,从左边遍开始寻找的话,它可以保证左边的数字都是小于基准值的,但是可能会造成i,j相遇时的位置元素是大于基准值的情况(结合上边实例思考),这时跳出循环,进行交换,是不是把大于基准值的那个值与基准值进行交换了呢??所以错误
而从右边开始寻找的话,它可以保证右边的数字都是大于基准值的,但是i,j相遇时的位置元素必定小于或等于基准值,这样再交换就是正确的了。
当然 如果你基准点在最右侧 就要从左边开始寻找了
————————————————————————
时间复杂度分析:
最好情况:每次划分对一个记录定位后 该记录的左侧子序列与右侧子序列的长度相同。在具有n个记录的序列中,一次划分需要对整个带划分序列扫描一遍,则所需时间为O(n)。
那么T(n)=2T(n/2)+n = 2(2T(n/4)+n/2))+n=4T(n/4)+2n
=8T(n/8)+3n
…
=nT(1)+nlgn=O(nlgn)
最坏情况下,待排序记录序列是正序或逆序,每次划分只得到一个比上一次划分少一个记录的子序列(另一个子序列为空)。这样子,必须经过n-1次递归调用才能把所有记录定位,第i次划分需要经过n-i次比较才能找到第i个记录的位置,时间复杂度为
i=1->i=n-1 每次(n-i)次求和=2/1 * n * (n-1)=O(n²)
平均情况:
空间复杂度:O(logn)