日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

歸并排序

 waston 2019-02-12
歸并排序介紹

將兩個(gè)的有序數(shù)列合并成一個(gè)有序數(shù)列,我們稱之為"歸并"。歸并排序(Merge Sort)就是利用歸并思想對(duì)數(shù)列進(jìn)行排序。根據(jù)具體的實(shí)現(xiàn),歸并排序包括"從上往下"和"從下往上"2種方式。


1. 從下往上的歸并排序:將待排序的數(shù)列分成若干個(gè)長度為1的子數(shù)列,然后將這些數(shù)列兩兩合并;得到若干個(gè)長度為2的有序數(shù)列,再將這些數(shù)列兩兩合并;得到若干個(gè)長度為4的有序數(shù)列,再將它們兩兩合并;直接合并成一個(gè)數(shù)列為止。這樣就得到了我們想要的排序結(jié)果。(參考下面的圖片)

2. 從上往下的歸并排序:它與"從下往上"在排序上是反方向的。它基本包括3步:
① 分解 -- 將當(dāng)前區(qū)間一分為二,即求分裂點(diǎn) mid = (low + high)/2;
② 求解 -- 遞歸地對(duì)兩個(gè)子區(qū)間a[low...mid] 和 a[mid+1...high]進(jìn)行歸并排序。遞歸的終結(jié)條件是子區(qū)間長度為1。
③ 合并 -- 將已排序的兩個(gè)子區(qū)間a[low...mid]和 a[mid+1...high]歸并為一個(gè)有序的區(qū)間a[low...high]。

 

下面的圖片很清晰的反映了"從下往上"和"從上往下"的歸并排序的區(qū)別。

 

歸并排序圖文說明

歸并排序(從上往下)代碼

/*
 * 將一個(gè)數(shù)組中的兩個(gè)相鄰有序區(qū)間合并成一個(gè)
 *
 * 參數(shù)說明:
 *     a -- 包含兩個(gè)有序區(qū)間的數(shù)組
 *     start -- 第1個(gè)有序區(qū)間的起始地址。
 *     mid   -- 第1個(gè)有序區(qū)間的結(jié)束地址。也是第2個(gè)有序區(qū)間的起始地址。
 *     end   -- 第2個(gè)有序區(qū)間的結(jié)束地址。
 */
void merge(int a[], int start, int mid, int end)
{
    int *tmp = (int *)malloc((end-start+1)*sizeof(int));    // tmp是匯總2個(gè)有序區(qū)的臨時(shí)區(qū)域
    int i = start;            // 第1個(gè)有序區(qū)的索引
    int j = mid + 1;        // 第2個(gè)有序區(qū)的索引
    int k = 0;                // 臨時(shí)區(qū)域的索引

    while(i <= mid && j <= end)
    {
        if (a[i] <= a[j])
            tmp[k++] = a[i++];
        else
            tmp[k++] = a[j++];
    }

    while(i <= mid)
        tmp[k++] = a[i++];

    while(j <= end)
        tmp[k++] = a[j++];

    // 將排序后的元素,全部都整合到數(shù)組a中。
    for (i = 0; i < k; i++)
        a[start + i] = tmp[i];

    free(tmp);
}

/*
 * 歸并排序(從上往下)
 *
 * 參數(shù)說明:
 *     a -- 待排序的數(shù)組
 *     start -- 數(shù)組的起始地址
 *     endi -- 數(shù)組的結(jié)束地址
 */
void merge_sort_up2down(int a[], int start, int end)
{
    if(a==NULL || start >= end)
        return ;

    int mid = (end + start)/2;
    merge_sort_up2down(a, start, mid); // 遞歸排序a[start...mid]
    merge_sort_up2down(a, mid+1, end); // 遞歸排序a[mid+1...end]

    // a[start...mid] 和 a[mid...end]是兩個(gè)有序空間,
    // 將它們排序成一個(gè)有序空間a[start...end]
    merge(a, start, mid, end);
}


從上往下的歸并排序采用了遞歸的方式實(shí)現(xiàn)。它的原理非常簡(jiǎn)單,如下圖:

通過"從上往下的歸并排序"來對(duì)數(shù)組{80,30,60,40,20,10,50,70}進(jìn)行排序時(shí):
1. 將數(shù)組{80,30,60,40,20,10,50,70}看作由兩個(gè)有序的子數(shù)組{80,30,60,40}和{20,10,50,70}組成。對(duì)兩個(gè)有序子樹組進(jìn)行排序即可。
2. 將子數(shù)組{80,30,60,40}看作由兩個(gè)有序的子數(shù)組{80,30}和{60,40}組成。
    將子數(shù)組{20,10,50,70}看作由兩個(gè)有序的子數(shù)組{20,10}和{50,70}組成。
3. 將子數(shù)組{80,30}看作由兩個(gè)有序的子數(shù)組{80}和{30}組成。
    將子數(shù)組{60,40}看作由兩個(gè)有序的子數(shù)組{60}和{40}組成。
    將子數(shù)組{20,10}看作由兩個(gè)有序的子數(shù)組{20}和{10}組成。
    將子數(shù)組{50,70}看作由兩個(gè)有序的子數(shù)組{50}和{70}組成。

 

歸并排序(從下往上)代碼

/*
 * 對(duì)數(shù)組a做若干次合并:數(shù)組a的總長度為len,將它分為若干個(gè)長度為gap的子數(shù)組;
 *             將"每2個(gè)相鄰的子數(shù)組" 進(jìn)行合并排序。
 *
 * 參數(shù)說明:
 *     a -- 待排序的數(shù)組
 *     len -- 數(shù)組的長度
 *     gap -- 子數(shù)組的長度
 */
void merge_groups(int a[], int len, int gap)
{
    int i;
    int twolen = 2 * gap;    // 兩個(gè)相鄰的子數(shù)組的長度

    // 將"每2個(gè)相鄰的子數(shù)組" 進(jìn)行合并排序。
    for(i = 0; i+2*gap-1 < len; i+=(2*gap))
    {
        merge(a, i, i+gap-1, i+2*gap-1);
    }

    // 若 i+gap-1 < len-1,則剩余一個(gè)子數(shù)組沒有配對(duì)。
    // 將該子數(shù)組合并到已排序的數(shù)組中。
    if ( i+gap-1 < len-1)
    {
        merge(a, i, i + gap - 1, len - 1);
    }
}

/*
 * 歸并排序(從下往上)
 *
 * 參數(shù)說明:
 *     a -- 待排序的數(shù)組
 *     len -- 數(shù)組的長度
 */
void merge_sort_down2up(int a[], int len)
{
    int n;

    if (a==NULL || len<=0)
        return ;

    for(n = 1; n < len; n*=2)
        merge_groups(a, len, n);
}

從下往上的歸并排序的思想正好與"從下往上的歸并排序"相反。如下圖:

通過"從下往上的歸并排序"來對(duì)數(shù)組{80,30,60,40,20,10,50,70}進(jìn)行排序時(shí):
1. 將數(shù)組{80,30,60,40,20,10,50,70}看作由8個(gè)有序的子數(shù)組{80},{30},{60},{40},{20},{10},{50}和{70}組成。
2. 將這8個(gè)有序的子數(shù)列兩兩合并。得到4個(gè)有序的子樹列{30,80},{40,60},{10,20}和{50,70}。
3. 將這4個(gè)有序的子數(shù)列兩兩合并。得到2個(gè)有序的子樹列{30,40,60,80}和{10,20,50,70}。
4. 將這2個(gè)有序的子數(shù)列兩兩合并。得到1個(gè)有序的子樹列{10,20,30,40,50,60,70,80}。

 

歸并排序的時(shí)間復(fù)雜度和穩(wěn)定性

歸并排序時(shí)間復(fù)雜度
歸并排序的時(shí)間復(fù)雜度是O(N*lgN)。
假設(shè)被排序的數(shù)列中有N個(gè)數(shù)。遍歷一趟的時(shí)間復(fù)雜度是O(N),需要遍歷多少次呢?
歸并排序的形式就是一棵二叉樹,它需要遍歷的次數(shù)就是二叉樹的深度,而根據(jù)完全二叉樹的可以得出它的時(shí)間復(fù)雜度是O(N*lgN)。

歸并排序穩(wěn)定性
歸并排序是穩(wěn)定的算法,它滿足穩(wěn)定算法的定義。
算法穩(wěn)定性 -- 假設(shè)在數(shù)列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。則這個(gè)排序算法是穩(wěn)定的!

 

歸并排序?qū)崿F(xiàn)

下面給出歸并排序的三種實(shí)現(xiàn):C、C++和Java。這三種實(shí)現(xiàn)的原理和輸出結(jié)果都是一樣的,每一種實(shí)現(xiàn)中都包括了"從上往下的歸并排序"和"從下往上的歸并排序"這2種形式。
歸并排序C實(shí)現(xiàn)
實(shí)現(xiàn)代碼(merge_sort.c)

  1 /**
  2  * 歸并排序:C 語言
  3  *
  4  * @author skywang
  5  * @date 2014/03/12
  6  */
  7 
  8 #include 
  9 #include 
 10 
 11 // 數(shù)組長度
 12 #define LENGTH(array) ( (sizeof(array)) / (sizeof(array[0])) )
 13 
 14 /*
 15  * 將一個(gè)數(shù)組中的兩個(gè)相鄰有序區(qū)間合并成一個(gè)
 16  *
 17  * 參數(shù)說明:
 18  *     a -- 包含兩個(gè)有序區(qū)間的數(shù)組
 19  *     start -- 第1個(gè)有序區(qū)間的起始地址。
 20  *     mid   -- 第1個(gè)有序區(qū)間的結(jié)束地址。也是第2個(gè)有序區(qū)間的起始地址。
 21  *     end   -- 第2個(gè)有序區(qū)間的結(jié)束地址。
 22  */
 23 void merge(int a[], int start, int mid, int end)
 24 {
 25     int *tmp = (int *)malloc((end-start+1)*sizeof(int));    // tmp是匯總2個(gè)有序區(qū)的臨時(shí)區(qū)域
 26     int i = start;            // 第1個(gè)有序區(qū)的索引
 27     int j = mid + 1;        // 第2個(gè)有序區(qū)的索引
 28     int k = 0;                // 臨時(shí)區(qū)域的索引
 29 
 30     while(i <= mid && j <= end)
 31     {
 32         if (a[i] <= a[j])
 33             tmp[k++] = a[i++];
 34         else
 35             tmp[k++] = a[j++];
 36     }
 37 
 38     while(i <= mid)
 39         tmp[k++] = a[i++];
 40 
 41     while(j <= end)
 42         tmp[k++] = a[j++];
 43 
 44     // 將排序后的元素,全部都整合到數(shù)組a中。
 45     for (i = 0; i < k; i++)
 46         a[start + i] = tmp[i];
 47 
 48     free(tmp);
 49 }
 50 
 51 /*
 52  * 歸并排序(從上往下)
 53  *
 54  * 參數(shù)說明:
 55  *     a -- 待排序的數(shù)組
 56  *     start -- 數(shù)組的起始地址
 57  *     endi -- 數(shù)組的結(jié)束地址
 58  */
 59 void merge_sort_up2down(int a[], int start, int end)
 60 {
 61     if(a==NULL || start >= end)
 62         return ;
 63 
 64     int mid = (end + start)/2;
 65     merge_sort_up2down(a, start, mid); // 遞歸排序a[start...mid]
 66     merge_sort_up2down(a, mid+1, end); // 遞歸排序a[mid+1...end]
 67 
 68     // a[start...mid] 和 a[mid...end]是兩個(gè)有序空間,
 69     // 將它們排序成一個(gè)有序空間a[start...end]
 70     merge(a, start, mid, end);
 71 }
 72 
 73 
 74 /*
 75  * 對(duì)數(shù)組a做若干次合并:數(shù)組a的總長度為len,將它分為若干個(gè)長度為gap的子數(shù)組;
 76  *             將"每2個(gè)相鄰的子數(shù)組" 進(jìn)行合并排序。
 77  *
 78  * 參數(shù)說明:
 79  *     a -- 待排序的數(shù)組
 80  *     len -- 數(shù)組的長度
 81  *     gap -- 子數(shù)組的長度
 82  */
 83 void merge_groups(int a[], int len, int gap)
 84 {
 85     int i;
 86     int twolen = 2 * gap;    // 兩個(gè)相鄰的子數(shù)組的長度
 87 
 88     // 將"每2個(gè)相鄰的子數(shù)組" 進(jìn)行合并排序。
 89     for(i = 0; i+2*gap-1 < len; i+=(2*gap))
 90     {
 91         merge(a, i, i+gap-1, i+2*gap-1);
 92     }
 93 
 94     // 若 i+gap-1 < len-1,則剩余一個(gè)子數(shù)組沒有配對(duì)。
 95     // 將該子數(shù)組合并到已排序的數(shù)組中。
 96     if ( i+gap-1 < len-1)
 97     {
 98         merge(a, i, i + gap - 1, len - 1);
 99     }
100 }
101 
102 /*
103  * 歸并排序(從下往上)
104  *
105  * 參數(shù)說明:
106  *     a -- 待排序的數(shù)組
107  *     len -- 數(shù)組的長度
108  */
109 void merge_sort_down2up(int a[], int len)
110 {
111     int n;
112 
113     if (a==NULL || len<=0)
114         return ;
115 
116     for(n = 1; n < len; n*=2)
117         merge_groups(a, len, n);
118 }
119 
120 void main()
121 {
122     int i;
123     int a[] = {80,30,60,40,20,10,50,70};
124     int ilen = LENGTH(a);
125 
126     printf("before sort:");
127     for (i=0; i)
128         printf("%d ", a[i]);
129     printf("\n");
130 
131     merge_sort_up2down(a, 0, ilen-1);        // 歸并排序(從上往下)
132     //merge_sort_down2up(a, ilen);            // 歸并排序(從下往上)
133 
134     printf("after  sort:");
135     for (i=0; i)
136         printf("%d ", a[i]);
137     printf("\n");
138 }

歸并排序C++實(shí)現(xiàn)
實(shí)現(xiàn)代碼(MergeSort.cpp)

  1 /**
  2  * 歸并排序:C++
  3  *
  4  * @author skywang
  5  * @date 2014/03/12
  6  */
  7 
  8 #include 
  9 using namespace std;
 10 
 11 /*
 12  * 將一個(gè)數(shù)組中的兩個(gè)相鄰有序區(qū)間合并成一個(gè)
 13  *
 14  * 參數(shù)說明:
 15  *     a -- 包含兩個(gè)有序區(qū)間的數(shù)組
 16  *     start -- 第1個(gè)有序區(qū)間的起始地址。
 17  *     mid   -- 第1個(gè)有序區(qū)間的結(jié)束地址。也是第2個(gè)有序區(qū)間的起始地址。
 18  *     end   -- 第2個(gè)有序區(qū)間的結(jié)束地址。
 19  */
 20 void merge(int* a, int start, int mid, int end)
 21 {
 22     int *tmp = new int[end-start+1];    // tmp是匯總2個(gè)有序區(qū)的臨時(shí)區(qū)域
 23     int i = start;            // 第1個(gè)有序區(qū)的索引
 24     int j = mid + 1;        // 第2個(gè)有序區(qū)的索引
 25     int k = 0;                // 臨時(shí)區(qū)域的索引
 26 
 27     while(i <= mid && j <= end)
 28     {
 29         if (a[i] <= a[j])
 30             tmp[k++] = a[i++];
 31         else
 32             tmp[k++] = a[j++];
 33     }
 34 
 35     while(i <= mid)
 36         tmp[k++] = a[i++];
 37 
 38     while(j <= end)
 39         tmp[k++] = a[j++];
 40 
 41     // 將排序后的元素,全部都整合到數(shù)組a中。
 42     for (i = 0; i < k; i++)
 43         a[start + i] = tmp[i];
 44 
 45     delete[] tmp;
 46 }
 47 
 48 /*
 49  * 歸并排序(從上往下)
 50  *
 51  * 參數(shù)說明:
 52  *     a -- 待排序的數(shù)組
 53  *     start -- 數(shù)組的起始地址
 54  *     endi -- 數(shù)組的結(jié)束地址
 55  */
 56 void mergeSortUp2Down(int* a, int start, int end)
 57 {
 58     if(a==NULL || start >= end)
 59         return ;
 60 
 61     int mid = (end + start)/2;
 62     mergeSortUp2Down(a, start, mid); // 遞歸排序a[start...mid]
 63     mergeSortUp2Down(a, mid+1, end); // 遞歸排序a[mid+1...end]
 64 
 65     // a[start...mid] 和 a[mid...end]是兩個(gè)有序空間,
 66     // 將它們排序成一個(gè)有序空間a[start...end]
 67     merge(a, start, mid, end);
 68 }
 69 
 70 
 71 /*
 72  * 對(duì)數(shù)組a做若干次合并:數(shù)組a的總長度為len,將它分為若干個(gè)長度為gap的子數(shù)組;
 73  *             將"每2個(gè)相鄰的子數(shù)組" 進(jìn)行合并排序。
 74  *
 75  * 參數(shù)說明:
 76  *     a -- 待排序的數(shù)組
 77  *     len -- 數(shù)組的長度
 78  *     gap -- 子數(shù)組的長度
 79  */
 80 void mergeGroups(int* a, int len, int gap)
 81 {
 82     int i;
 83     int twolen = 2 * gap;    // 兩個(gè)相鄰的子數(shù)組的長度
 84 
 85     // 將"每2個(gè)相鄰的子數(shù)組" 進(jìn)行合并排序。
 86     for(i = 0; i+2*gap-1 < len; i+=(2*gap))
 87     {
 88         merge(a, i, i+gap-1, i+2*gap-1);
 89     }
 90 
 91     // 若 i+gap-1 < len-1,則剩余一個(gè)子數(shù)組沒有配對(duì)。
 92     // 將該子數(shù)組合并到已排序的數(shù)組中。
 93     if ( i+gap-1 < len-1)
 94     {
 95         merge(a, i, i + gap - 1, len - 1);
 96     }
 97 }
 98 
 99 /*
100  * 歸并排序(從下往上)
101  *
102  * 參數(shù)說明:
103  *     a -- 待排序的數(shù)組
104  *     len -- 數(shù)組的長度
105  */
106 void mergeSortDown2Up(int* a, int len)
107 {
108     int n;
109 
110     if (a==NULL || len<=0)
111         return ;
112 
113     for(n = 1; n < len; n*=2)
114         mergeGroups(a, len, n);
115 }
116 
117 int main()
118 {
119     int i;
120     int a[] = {80,30,60,40,20,10,50,70};
121     int ilen = (sizeof(a)) / (sizeof(a[0]));
122 
123     cout << "before sort:";
124     for (i=0; i)
125         cout << a[i] << " ";
126     cout << endl;
127 
128     mergeSortUp2Down(a, 0, ilen-1);        // 歸并排序(從上往下)
129     //mergeSortDown2Up(a, ilen);            // 歸并排序(從下往上)
130 
131     cout << "after  sort:";
132     for (i=0; i)
133         cout << a[i] << " ";
134     cout << endl;
135 
136     return 0;
137 }

上面3種實(shí)現(xiàn)的原理和輸出結(jié)果都是一樣的。下面是它們的輸出結(jié)果:

before sort:80 30 60 40 20 10 50 70 
after  sort:10 20 30 40 50 60 70 80 

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多