您的位置:首页 > 其它

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

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

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

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

归并排序

归并排序是典型的分治法

分治法的三个步骤: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)

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

划分问题:数组在排序后使得,左侧的值全部都小于右侧值

递归解决:左右分别排序

合并:不需要!

//网上的快速排序很多,这里举一例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)。所以告诉我们,算法学得晚其实并不要紧,毕竟,种一棵树最好的时间就是十年前和现在。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: