归并排序的优化--自然归并排序

不知道怎么将这个算法思想表达的更好,也不知道自己的理解的是否对

黑体的注释是普通的自然归并,从相邻长度为1的子数组段进行合并也就是一开始将每两个相邻元素进行归并,然后再相邻四个元素左右两组都有序的合并成4个有序的........自下向上不断往上归并直到有序  

 

/*
 * 自然合并排序是合并排序算法的一种改进. 自然合并排序:
 * 对于初始给定的数组,通常存在多个长度大于1的已自然排好序的子数组段.
 * 例如,若数组a中元素为{4,8,3,7,1,5,6,2},则自然排好序的子数组段有{4,8},{3,7},{1,5,6},{2}.
 * 构成更大的排好序的子数组段({3,4,7,8},{1,2,5,6}).继续合并相邻排好序的子数组段,直至整个数组已排好序.
 *
 *
 * 用一个数组t[n]来存储数组排好序的子数组段   下标表示第i段  值表示在原数组值下降的位置(在升序的情况下)
 * 例如 arr{4,8,3,7,1,5,6,2}  t[0] = 0  t[1] = 2 t[2]=4 t[3]=7 那么一共有4段子数组
 *
 * 用t[n]代替之前的数组长度,从步长为1开始合并 表示相邻为步长的t[n]子数组段   t[n]来表示要合并的子数组下标
 */


public class 自然合并排序 {
    static int part; //表示有多少段排好序的子数组
    static int[] t;
    public static void main(String[] args) {
        int[] arr = {4,8,3,7,1,5,6,2};
        t = new int[arr.length];
        t[0] = 0;//第一个赋值为0 第一个数字肯定是有序的
        getPart(arr);
        mergeSore(arr,arr.length);
        
        for(int i=0;i<arr.length;i++) {
            System.out.print(arr[i]+" ");
        }
    }

    private static void getPart(int[] arr) {
        int k = 1;
        for(int i=0;i<arr.length-1;i++) {
            if(arr[i]>arr[i+1])
                t[k++] = i+1;  //记录第k+1段有序子数组开始的下标  
            //那么t[k]-1就为第k段的有序子数组的末尾下标
        }
        //相当于 一根绳子 切了三刀  分成了四条  的意思   第一个t[0]不算 只是表示起点
        part = k; // 因为前面是k++ 所以k表示数组分成了几段有序子数组段
        
        
    }

    private static void mergeSore(int[] arr,int n) {
        
        int s = 1;//表示步长
        
        //while(s<n) 
        while(s<part) {
            
            mergePass(arr,s,n);
            s+=s;
            mergePass(arr,s,n);
            s+=s;
        }
        
    }

    private static void mergePass(int[] arr, int s, int n) {
        int i=0;//控制t数组的下标   从0开始
        
        //t数组已经赋值的长度以内进行循环合并数组  
        
        //while(i<=n-s*2)
        while(i<=part-s*2) {
             //合并t数组下标相差s的两个子数组段
            int r = ((i+2*s)<part)?t[i+2*s]:n;//判断第二段子数组段的边界取什么值 因为i+2*s 可能超过part了    重点注意
            
            
            //注意 t[i+s]-1  才表示左子数组段的末尾下标
            //注意r-1才表示右子数组段的末尾
            merge(arr,t[i],t[i+s]-1,r-1);
            //merge(arr,i,i+s-1,i+2*s-1);
            
            i=i+2*s;
        }
    
        
        //if(i+s<n)
        if(i+s<part)
            merge(arr,t[i],t[i+s]-1,n-1);

        //merge(arr,i,i+s-1,n-1);


        
        //这里我只用原数组 所以不需要变动
        /*else
            if(i<part)
            {
                for(int j=t[i];j<=n-1;j++)
                    arr[j]
            }*/
        
    }

    private static void merge(int[] arr, int i, int j, int r) {
        int[] b = new int[arr.length];
        int left = i;
        int right = j+1;
        int k = 0;
        while(left<=j&&right<=r) {
            if(arr[left]<=arr[right])  //谁小就放进辅助数组 且指针++
                b[k++] = arr[left++];
            else
                b[k++] = arr[right++];
        }
        
        if(left>j) {
            for(int l = right;l<=r;l++) {
                b[k++] = arr[l];
            }
        }
        else {
            for(int l = left;l<=j;l++) {
                b[k++] = arr[l];
            }
        }
        
        //赋值回原数组
        for(int l=0;l<k;l++) {
            arr[i++] = b[l];
        }
        
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值