首页 > 代码库 > 分治算法(二)

分治算法(二)

    大家都知道选择排序和冒泡排序,这两个排序都是双重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);
}

 

 

   

分治算法(二)