排序算法就是那么回事儿<一>

想来已经大四了,如果真的有什么对大一的自己能说的话,那么就是,其实编程没有什么窍门,无非就是多想,多练。玩玩别被看似复杂的算法给迷住了双眼,其实其核心思想总是只有那么几点。只要多加练习,就能领略其中之美。

另有一点是,有了合适的入门读物,就可以少很多曲折和反复。请注意:合适。这个难度不应该太高,陡峭地让你爬不上去,也不能仅仅是罗列代码。只要能够用自己的语言将这个算法概括出来,那就说明你已经初步掌握了。

算法有多种实现版本,可能你所看到的和书本上的不同,但是这并不妨碍你理解算法的含义

归并排序

归并排序是典型的分治法


分治法的三个步骤:1.划分问题 2.递归求解 3.合并问题

对归并排序而言
1. 划分问题 :分成长度相等的左右两半
2. 递归求解:把左右两个序列分别进行排序
3. 将结果合为一体 :把两个有序表合为一体

问题的难点在于第三点。

每次将两个有序表中的最小值删除,并且将其放入合并后的新表即可

让我们首先定义一个这个函数的形式

void merge_sort(int* A,int x,int y,int* T);

x,y为操作的范围
注意,这个范围为左闭右开,即[x,y),也就是说实际范围是[x,y-1]

void merge_sort(int* A,int x,int y,int* T){
    if(y-x<=1)return;
    int m=(x+y)/2;
    merge_sort(A,x,m,T);
    merge_sort(A,m,y,T);
    int p=x,q=m,i=x;//分别是左序列、有序列、新序列的下标
    while(p<m||q<y){
        if(q>=y||A[p]<=A[q]) T[i++]=A[p++];
        else T[i++]=A[q++]
    }
    for(int i=x;i<y;i++)//从辅助数组中转移回来
        A[i]=T[i];

}

看看核心部分while循环中的部分,有没有一种似曾相识的感觉,从两个序列中得到一个最小的值,这让我们想到了什么?是不是非常像Dijkstra算法(DAG最短路径和算法),Dijkstra算法每次都是从未知的元素中选取d值最小的一点。

另外while循环中有些有技巧的地方,如果q>=y,||运算符就会短路了后面的[]运算(a[y]肯定是非法的)。


归并排序并不美好,它的时间复杂度是O(nlogn),而且需要一个额外的数组T来临时保存结果

快速排序

快速排序的最差时间为O(n^2),平均情况O(nlogn)

和上面一样,我们首先说出其步骤应该是:

  1. 划分问题:数组在排序后使得,左侧的值全部都小于右侧值
  2. 递归解决:左右分别排序
  3. 合并:不需要!
//网上的快速排序很多,这里举一例Hoare版本,这个版本比较好理解
int partition(int* a,int l,int r){
    int key=a[0];
    int left=l,right=r;
    while(left<right){
        while(key<=a[right]&&right>left)right--;
         a[left]=a[right];
        while(key>=a[left]&&right>left) left++;
         a[right]=a[left];
    }
    a[left]=key;
    return left;
}
void quick_sort(int* a,int left,int right){
    int p=partition(a,left,right);
    quick_sort(a,left,p-1);
    quick_sort(a,p+1,right);
}

这里插一句题外话,其实快排的发明者就是Tony Hoare,此人1934年出生,到了50岁的时候才对算法发生了兴趣( Tony Hoare’s interest in computing was awakened in the early fifties)。所以告诉我们,算法学得晚其实并不要紧,毕竟,种一棵树最好的时间就是十年前和现在。

版权声明:本文为博主原创文章,转载请标明出处。

posted @ 2015-09-12 17:33  Fridge  阅读(165)  评论(0编辑  收藏  举报