• [补充]归并排序(非递归)以及归并排序的更高效算法——自然归并排序


    递归版归并排序


      我们在 CLRS 中已经学会了归并排序的递归写法:

      merge函数:

    def merge(left, right):
        # prerequisite: both left and right is sorted list
        ret = []
        i = 0
        j = 0
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                ret.append(left[i])
                i += 1
            else:
                ret.append(right[j])
                j += 1
    
        if i == len(left):
            for it in right[j:]:
                ret.append(it)
        else:
            for it in left[i:]:
                ret.append(it)
        print("after sort, left is {}, right is {}, ret is {}".format(left, right, ret))
    
        return ret
    View Code

      mergeSort函数:

    def mergeSort(arr):
        print("current arr is:{}".format(arr))
        if len(arr) <= 1:
            return arr
        mid = len(arr) // 2
        left  = mergeSort(arr[:mid])
        right = mergeSort(arr[mid:])
        return merge(left, right)
    View Code

      但是,递归算法的常数因子很影响时间,转化成非递归版算法通常都是更优的解法,所以我们来实现一下非递归版

    非递归版归并排序


      实现的原理和递归版刚好相反,递归解法是将有序串一分为二直到每个串只有一个元素,然后再排序合并。而非递归版是默认有 n 个长度为 1 子串,然后将相邻的两个串两两排序并合并,直到合并成一个长度为 n 的子串。比如刚开始有 n 个子串,下一步是相邻的两个串两两排序并合并,构成 n/2 个长度为 2 的子串,然后再排序合并,形成 n/4 个长度为 4 的子串....直到生成一个长度为 n 的子串。

    void mergeSort2(int n){
        int s=2,i;
        while(s<=n){
            i=0;
            while(i+s<=n){
                merge(i,i+s-1,i+s/2-1);
                i+=s;
            }
            //处理末尾残余部分
            merge(i,n-1,i+s/2-1);
            s*=2;
        }
        //最后再从头到尾处理一遍
        merge(0,n-1,s/2-1);
    }
    View Code

    自然归并排序


      通常,子串在排序之前就已经有序,我们需要记录下已经有序的子串的数量以及其各自的头尾位置,在排序时无需再对这些子串进行排序,直接合并即可。

      实现记录有序子串函数是 pass() 函数,其中的 rec 用于记录有序子串的头尾位置,pass 函数返回有序串的个数。

    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    
    std::vector<int> arr; //排序数组
    std::vector<int> c; //记录排序子串的索引
    int len; //当前串的长度
    
    //扫描记录每个自然串的起始下标与自然串总个数+1
    int pass () {
        int max = arr[0];
        int num = 0;
        c[num++] = 0;
        for (int i = 1; i < len; i++){
            if (max <= arr[i]) {
                max = arr[i];
            }
            else {
                c[num++] = i;
                max = arr[i];
            }
        }
    
        c[num++] = len; //让c多个尾巴且num+1,方便mergeSort
        //for (int i = 0; i <= num; i++) printf("%d ",c[i]);
        //printf("
    ");
        return num;
    }
    
    void merge (const int& s, const int& end1, const int& end2) {
        //s,end1, end2 分别对应第一个串的开头、第一个串的
        //结尾、第二个串的结尾
        int tmpArr[len];
        int s1 = s, s2 = end1 + 1;
        //printf ("s1 = %d, s2 = %d
    ", s1, s2);
        for (int i = s; i <= end2; i++) {
            if (s1 > end1) tmpArr[i] = arr[s2++];
            else if (s2 > end2) tmpArr[i] = arr[s1++];
            else if (arr[s1] < arr[s2]) tmpArr[i] = arr[s1++];
            else tmpArr[i] = arr[s2++];
        }
        for (int i = s; i <= end2; i++)
            arr[i] = tmpArr[i];
    }
    
    void mergeSort () {
        int cnt = pass();
        //for (int i = 0; i < 2 + cnt; i++) printf("%d", c[i]);
        //printf("cnt = %d
    ", cnt);
        while (cnt != 2) {
            for (int i = 0; i < cnt; i = i + 2) {
                merge(c[i], c[i+1]-1, c[i+2]-1);
            }
            cnt = pass();
            printf("cnt = %d
    ", cnt);
        }
    }
    
    int main (void) {
        while(scanf("%d", &len) != EOF && len != 0) {
            arr.clear();
            arr.resize(len);
            c.clear();
            c.resize(len+1);
            for (int i = 0; i < len; i++) {
                scanf("%d", &arr[i]);
            }
            mergeSort();
            for (int i = 0; i < len; i++)
                printf("%d ", arr[i]);
            printf("
    ");
        }
        return 0;
    }
    View Code

       python版本:

    class Merge():
        def __init__(self):
            self.tmp_arr = [0,0,0,0,0,0,0,0,0,0,0,0]
    
        def merge(self, arr, start, mid, end):
            for i in range(start, end+1):
                self.tmp_arr[i] = arr[i]
            j = mid + 1
            k = start
            for i in range(start, end+1):
                if k > mid:
                    arr[i] = self.tmp_arr[j]
                    j += 1
                elif j > end:
                    arr[i] = self.tmp_arr[k]
                    k += 1
                elif self.tmp_arr[j] < self.tmp_arr[k]:
                    arr[i] = self.tmp_arr[j]
                    j += 1
                else:
                    arr[i] = self.tmp_arr[k]
                    k += 1
        def sort(self, arr, start, end):
            if end <= start:
                return 
            mid = start + (end - start) / 2
            self.sort(arr, start, mid)
            self.sort(arr, mid + 1, end)
            self.merge(arr, start, mid ,end)
            
    mobj = Merge()
    arr = [5,3,4,7,1,9,0,4,2,6,8]
    mobj.sort(arr, 0, len(arr)-1)
    for i in range(len(arr)):
        print arr[i]
    View Code

    参考


      归并排序(递归实现+非递归实现+自然合并排序)

      

      

    ————全心全意投入,拒绝画地为牢
  • 相关阅读:
    EXCEL中统计个数问题
    Boot Windows XP from a USB flash drive
    转:用VHD文件将Windows 7安装到虚拟磁盘
    CPU性能排名
    活动目录维护
    IE7占用CPU资源非常高
    不得不看:Windows Data Protection
    硬盘模式为UDMA 2.
    转载:NC6400安装Win7系统驱动列表及注意事项
    HP笔记本电池
  • 原文地址:https://www.cnblogs.com/Bw98blogs/p/8533354.html
Copyright © 2020-2023  润新知