归并排序
第一种高效排序方法是归并排序。按照分治三步法,对归并排序算法介绍如下:
划分问题:把序列分成元素个数尽量相等的两半
递归求解:把两半元素分别排序
合并问题:把s两个有序表合并成一个.
对于合并问题,把两个有序表合成一个,每次只需要把两个序列的最小元素加以比较,删除其中的较小元素并加入合并后的新表即可。
//归并排序(从小到大)
void merge_sort(int *A,int x,int y, int *T)
{
if(y-x>1)
{
int m=x+(y-x)/2; //划分
int p=x,q=m, i =x;
merge_sort(A,x,m,T);//递归求解
merge_sort(A,m,y,T);//递归求解
while(p <m|| q <y)
{
if(q >=y|| (p<m&& A[p] <=A[q])) T[i++]=A[p++];//从左半数组复制到临时空间
else T[i++]=A[q++];//从有半数组复制到临时空间
}
for(i =x; i <y; i++) A[i] =T[i];// 从辅助空间复制回A数组
}
}
代码中两个条件是关键。首先,只要有一个序列非空,就要继续合并(while(p<m||q<y)),因此在比较不能直接比较A[p]和A[q],因为可能其中一个序列为空,从而A[p]或者A[q]代表的是一个实际不存在的元素。正确的方式是:
如果第二个序列为空(此时第一个序列一定非空),复制A[p]
否则(第二个序列非空),当且仅当第一个序列也非空,且A[p]<=A[q]时,才复制A[p].
上面的代码巧妙地运用短路运算符“||”把两个条件连接在一起:如果条件1满足,就不会计算条件2;如果条件1不满足,就一定会计算条件2.这样的技巧很实用,细心体会。另外若不太习惯T[i++]=A[p++]这种“复制后移动下标”的方式,是时候弄懂,弄熟了。
快速排序
既然敢叫“快速排序”,必然有其过人之处。事实上,它确实是最快的通用内部排序算法。它有Hoare于1962年提出,相对归并排序来说不仅速度更快,并且不需辅助空间(还记得那个T数组吗)。按照分治三步法,将快速排序算法作如下介绍:
划分问题:把数组的各个元素重排后分成左右两个部分,使得左边的任意元素都小于或等于右边的任意元素。
递归求解:把左右两部分分别排序
合并问题:不用合并,因为此时数组已经完全有序。
二分查找
二分查找,简单的讲,根据字面意思就是一半一半的查找。但要熟练掌握二分查找,其实还是有点困难的
这里推荐一篇精彩的二分查找博客:https://www.cnblogs.com/wuyuegb2312/archive/2013/05/26/3090369.html
//二分查找(迭代实现)
int bsearch(int *A, int x, int y, int v)
{
int m;
while(x<y)
{
m=x+(y-x)/2;
if(A[m]==v) return m;
else if(A[m]>v) y=m;
else x=m+1;
}
return -1;
}
//二分查找求下界
int lower_bound(int *A, int x, int y, int v)
{
int m;
while(x<y)
{
m=x+(y-x)/2;
if(A[m]>=v) y=m;
else x=m+1;
}
return x;
}