归并排序(入门经典8.2.1),快速排序,二分查找

归并排序

第一种高效排序方法是归并排序。按照分治三步法,对归并排序算法介绍如下:
划分问题:把序列分成元素个数尽量相等的两半
递归求解:把两半元素分别排序
合并问题:把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;
}

猜你喜欢

转载自blog.csdn.net/weixin_42373330/article/details/82725318