zoukankan      html  css  js  c++  java
  • 分治算法(二)

        大家都知道选择排序和冒泡排序,这两个排序都是双重for循环,时间复杂度为O(n^2),显然效率都是比较低的,而运用分治思想的归并排序和快速排序会更高效一些。

    1、归并排序

    1)原理:假设初始序列含有n个记录,则可以看成是n个有序子序列,每个子序列的长度为1,然后两两归并,得到[n/2]([x]表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法成为2路归并排序。(摘自《大话数据结构》)

        可见其运用了典型的分治思想,①分解②求解③合并。刚开始学习这个算法时,我感觉分开排序和一起排序没什么区别,但在实现算法时,会发现n个一起排序的话要用双重for循环,而分开排再合并会高效的多,下面看一下实现的代码。

    2)参考代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int a[10]={2,3,1,5,34,21,12,4,10,6};
    int b[10];
    void Merge(int a[],int s,int m,int e,int tmp[]);
    void Mergesort(int a[],int s,int e,int tmp[]);
    int main()
    {
        int size=sizeof(a)/sizeof(int);
        Mergesort(a,0,size-1,b);
        for(int i=0;i<size;i++){
            printf("%d ",a[i]);
        }
        return 0;
    }
    void Mergesort(int a[],int s,int e,int tmp[]){
        if(s<e){
            int m=s+(e-s)/2;
            Mergesort(a,s,m,tmp);
            Mergesort(a,m+1,e,tmp);
            Merge(a,s,m,e,tmp);
        }
    }
    void Merge(int a[],int s,int m,int e,int tmp[]){
        int p1=s;
        int p2=m+1;
        int p=0;
        while(p1<=m&&p2<=e){
            if(a[p1]<a[p2]){
                tmp[p++]=a[p1++];
            }
            else{
                tmp[p++]=a[p2++];
            }
        }
        while(p1<=m){
            tmp[p++]=a[p1++];
        }
        while(p2<=e){
            tmp[p++]=a[p2++];
        }
        //注意e和s已经变了,刚开始写成了i<size
        for(int i=0;i<e-s+1;i++){
            a[s+i]=tmp[i];
        }
    
    }

    根据原理得,这十个数会先一个和一个比,然后合成两个,然后两个和两个比,一直到合成十个排序就完成了。

    3)时间复杂度推导:

    自:http://www.icourse163.org/learn/PKU-1001894005#/learn/content?type=detail&id=1002874892&sm=1

    中国大学MOOC程序设计与算法,郭炜

     2快速排序

    1)基本思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

        其基本思想也是根据分治算法来的,分解,求解,合并。像归并排序一样光看原理,感觉看不出来该种排序的高效,其实我觉得算法的巧妙更多的是在程序每个细节的代码实现中。

    2)参考代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int a[10]={29,2,3,12,9,76,34,16,7,45};
    void QuickShort(int a[],int s,int e);
    int main()
    {
        QuickShort(a,0,9);
        for(int i=0;i<10;i++){
            printf("%d ",a[i]);
        }
        return 0;
    }
    void QuickShort(int a[],int s,int e){//s指向要排序列的第一个元素,e指向最后一个
    if(s>=e){//临界条件:当两个指针指向同一个数时,说明排完了
        return;
    }
    int k=a[s];//让k等于s指向的值
    int i=s;
    int j=e;
    while(i!=j){//下面比较的while循环不要忘了让j>i即得到前一半和后一半的下标,也是跳出下面循环的边界
        while(j>i&&a[j]>k){//先从j指向的数开始和k比较,小于k就交换值
            j--;
        }
        swap(a[i],a[j]);
        while(j>i&&a[i]<=k){//交换后k就变成了a[j]的值
            i++;
        }
        swap(a[i],a[j]);
    }
        QuickShort(a,s,i);
        QuickShort(a,i+1,e);
    }

       

    祝你早日攒够失望,然后开始新的生活。
  • 相关阅读:
    理解SQL SERVER中非聚集索引的覆盖,连接,交叉和过滤
    TSQL查询进阶流程控制语句
    效率最高的Excel数据导入(c#调用SSIS Package将数据库数据导入到Excel文件中【附源代码下载】)
    SQL Service自定义数据类型
    理解SQL SERVER中的逻辑读,预读和物理读
    TSQL查询进阶深入理解子查询
    SQL查询入门(下篇)
    使用SQL进行递归查询
    利用 sys.sysprocesses 检查 Sql Server的阻塞和死锁
    灵活运用 SQL SERVER FOR XML PATH
  • 原文地址:https://www.cnblogs.com/LuRenJiang/p/7289701.html
Copyright © 2011-2022 走看看