Java实现2路合并次序最小比较次数

题目

这里写图片描述

分析

该题目属于贪心算法,一直寻找最小的两个序列的Si以及Sj进行合并,因为总的合并次数不变的,k个序列,一共需要k-1次的合并。
因为题目已经给出默认2路归并排序需要m+n-1次比较(这个是最坏的情况),然后需要k-1次合并,所以就是把m+n-1中-1提出来,那么就剩下m+n,因为m+n表示序列长度,那么使得该值最小,就要尽可能使m+n最小,所以一直寻找最小的两个序列即可。

代码
package chapter2;

import java.util.Arrays;
/**
 * 总体的思路为贪心算法,总是选择当前最小的两个长度的数据进行合并,但是难点在于数据的存储,而不是计算次数
 * 
 * */
public class GreedyMerge {

    //合并的比较次数统计
    private static int count = 0;
    //数据最大为多少
    private final static int DATA_MAX_VALUE = 100;
    //所有的序列个数
    private final static int ARRAY_NUM = 5;
    //每个序列的大小
    private static int [] len_array = {4,5,6,8,7};
    //数据
    private static int [] [] data = null;
    //辅助数组
    private static int [] copy = null;

    private static int l , r;

    //辅助数组2,用于对基础排序放置数据被000冲掉
    private static int [] temp = null;


    /**
     * 产生数组数据data,要控制,用于初始化;缺点在于浪费存储空间,后面尽量优化,采用两个辅助数组去优化
     * 
     * */
    private static void init(){
        //辅助 数据的大小统计
        int len = 0;
        int max = 0;

        for(int i = 0 ; i < ARRAY_NUM ; i ++){
            len += len_array[i];
            if(max < len_array[i]){
                max = len_array[i];
                max = i;
            }
        }

        //初始化data数组
        data = new int[ARRAY_NUM][len];

        //产生数据
        for(int i = 0 ; i < ARRAY_NUM ; i ++)
            for(int j = 0 ; j < len_array[i] ; j ++)
                data[i][j] = (int) ( Math.random() * DATA_MAX_VALUE);

        //辅助数组的大小
        copy = new int[len];
        temp = new int[max];

    }

    /**
     * 调用系统的排序进行排序,因为这个题目重点不在于排序
     * 
     * */
    private static void simpleSort(int [] [] data){
        //引入temp的原因是因为对data[]排序的时候回将默认赋值的000全部排序到前面
        for(int i = 0 ; i < data.length ;i ++){
            temp = Arrays.copyOfRange(data[i], 0, len_array[i]);
            Arrays.sort(temp);
            for(int j = 0 ; j < temp.length ; j ++)
                data[i][j] = temp[j];
        }
    }


    /**
     * 用于测试输出所要排序的数据
     * 
     * */
    private static void showData(int [][] data){
        for (int i = 0 ; i < len_array.length ; i ++) {
            for(int j = 0 ; j < len_array[i]; j ++)
                System.out.print(data[i][j]+" ");
            System.out.println();
        }
    }

    //找出最小的两个数组下标
    private static void findMin(){
        System.out.println("-------------------------------------------------------------------------------------------------");
        for (int i : len_array) {
            System.out.print(i+"  ");
        }
        System.out.println("\n-------------------------------------------------------------------------------------------------");
        int min = Integer.MAX_VALUE;
        //别把len_array设置为长度1的数组
        for(int  i = 0 ; i < len_array.length ; i ++){
            if(len_array[i] == -1)
                continue;
            if(len_array[i] < min){
                min = len_array[i];
                l = i;
            }
        }
        int min2 = Integer.MAX_VALUE;
        for(int  i = 0 ; i < len_array.length ; i ++){
            if(len_array[i] == -1)
                continue;
            if(i == l)
                continue;
            if(len_array[i] < min2){
                min2 = len_array[i];
                r = i;
            }
        }

    }


    /**
     * 合并数据并且对比较的次数进行统计
     * 
     * */
    private static void merge(){
        findMin();
        merge(l, r);
        //已经完成合并,就返回
        if(check() == 1){
            return ;
        }
        //未完成继续合并
        merge();

    }


    /**
     * 检测是否已经合并完成
     * @return 如果返回的数字为1,则表示已经合并完成,大于等于2表示还需要继续合并
     * 
     * */
    private static int check(){
        int c = 0;
        for (int i : len_array) {
            if(i != -1)
                c++;
        }
        return c;
    }

    /**
     * 合并给出的两个下标的数组
     * */
    private static void merge(int left , int right){
        System.out.println("合并"+left+"—"+right+"-->"+left);
        count += len_array[left] + len_array[right] - 1 ;
        int i = 0,j = 0;
        int k = 0;
        while(i < len_array[left] || j < len_array[right]){
            if(i == len_array[left] ){
                copy[k++] = data[right][j++];
                continue;
            }
            if(j == len_array[right]){
                copy[k ++] = data[left][i++];
                continue;
            }

            int min = data[left][i];
            if(data[right][j] < min){
                copy[k ++] = data[right][j++];
            }else{
                copy[k ++] = data[left][i++];
            }
        }
        int _length = len_array[left] + len_array[right] ; 

        //赋值给left所在的数组
        for(int m = 0 ; m < _length ; m++){
            data[left][m] = copy[m] ;
        }

        //将left的长度为新的组合之后的长度,right改为-1就是遍历的时候跳过改组
        len_array[left] = _length ;
        len_array[right] = -1;

    }

    public static void main(String[] args) {
        init();
        //遍历每个数组,查看数据
        System.out.println("------------------------------------before-------------------------------------------------------");
        showData(data);
        //调用排序
        simpleSort(data);
        System.out.println("---------------------------------after sort---------------------------------------------------");
        showData(data);
        //开始合并
        merge();

        //已经合并好了
        int index = 0;
        for(int i = 0 ; i < len_array.length ; i ++){
            if(len_array[i] != -1)
                index = i;
        }

        System.out.println("-------------------------------------after merge----------------------------------------------");
        System.out.println("一共合并的次数为:"+count+"\nps:这个合并次数不为实际合并次数,而是最坏的情况下的合并次数,合并后的数组如下:\n");
        for (int d : data[index]) {
            System.out.print(" "+d);
        }
    }

}
运行结果:

这里写图片描述

Before表示随机生成的数据,after则是排序过后的满足题目条件的序列S1到S5然后后续的表示序列的长度(已经排序过的废弃的序列下标设置为-1,到时候遍历的时候跳过)after merge表示合并之后的数据排序,一共合并次数不为实际合并次数,最坏情况。

如有错误,请指出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值