插入排序与归并排序比较+分治策略算法

这篇博客探讨了两种常见的排序算法——插入排序和归并排序,详细介绍了它们的实现原理,并通过Java代码进行展示。文章还对比了两者在不同数据量下的运行时间,强调了数据量越大,归并排序的优势越明显。此外,博主还深入讨论了分治策略,包括求最大数组和问题以及整数划分的递归练习,展示了分治算法在解决实际问题中的应用。
摘要由CSDN通过智能技术生成

算法-学习笔记

1 排序

1.1 插入排序

  • 插入排序代码(Java):
/**
  * <b> 插入排序
  * <br/> 从无序到有序,将无序组的数据 依次插入倒有序组
  * @param array 输入数据
  * @return int
  */
public static void insertionSort(int[] array){
    for (int i = 1; i < array.length; i++) {
        int key = array[i], j=i-1;
        while(j >= 0 && array[j] > key){
            array[j+1] = array[j];
            j--;
        }
        array[j+1]=key;
    }
}
  • 测试代码(需导入junit包)
public static int[] produceArray(int num){ //工具方法,用于生产num个随机数初始化的数组
        Random random = new Random();
        int[] a= new int[num];
        for (int i = 0; i < num; i++) {
            a[i] = random.nextInt(100);
        }
        return a;
}
@Test
public void insertionSort(){
    int []a = Sort.produceArray(10000);
    System.out.println(Arrays.toString(a));

    long beginTime = System.nanoTime();      //计时,单位纳秒
    com.algorithm.sort.Sort.insertionSort(a);
    long overTime = System.nanoTime();
    
    System.out.println(Arrays.toString(a));
    System.out.println("耗时:"+(overTime-beginTime)/1000000.0+"ms"); //耗时结果
}

1.2 归并排序

  • 归并排序递归实现
/**
 * <b> 归并排序-递归实现
 * <br/>采用分治法(分而治之)
 * @param array  待排序数组
 * @param left 需排序的 左下标
 * @param right 需排序的 右下标
 * @return int[] 排序后的数组
 */
public static void mergeSort(int[] array, int left, int right){
    if(left<right){
        //          int middle = (int) Math.round((left+right)/2.0);
        int middle = (left+right)/2;
        mergeSort(array,left,middle);
        mergeSort(array,middle+1,right);
        merge(array,left,middle,right);
    }
}
/**
 * <b> 两堆有序数列合并
 * @param array 原数组
 * @param left  左下标
 * @param middle 中下标
 * @param right 右下标
 *
 */
public static void merge(int[] array,int left, int middle, int right){
    int n1 = middle-left+1;     //左边数组个数
    int n2 = right-middle-1+1;  //右边数组个数,±1可省略,这里为了便于观察,添加上了

    int[] arrayLeft = new int[n1+1];  //多添加一个,用于作为堆底
    int[] arrayRight = new int[n2+1];
    
    for (int i = 0; i < n1; i++) {    //初始化两个有序数组
        arrayLeft[i] = array[left+i];
    }
    for (int i = 0; i < n2; i++) {
        arrayRight[i] = array[middle+i +1];
    }
    
    arrayLeft[n1] = Integer.MAX_VALUE; //临界量赋值,用作堆底,也可称其为哨兵
    arrayRight[n2] = Integer.MAX_VALUE;

    int pl=0,pr=0;      //左右数组初始指针
    for (int i = left; i <= right ; i++) { //从已排好序的两数组中,抽取一个较小值 放到原数组中
        if(arrayLeft[pl] < arrayRight[pr]){
            array[i] =arrayLeft[pl];
            pl++;
        }else{
            array[i] = arrayRight[pr];
            pr++;
        }
    }
}
  • 测试代码(需junit包)
@Test
public void mergeSort(){
    int size = 100000;  //数据量大小
    int []a = Sort.produceArray(size);  //与上一测试代码相同的工具方法
    System.out.println(Arrays.toString(a));

    long beginTime = System.nanoTime();
    com.algorithm.sort.Sort.mergeSort(a,0,size-1);
    long overTime = System.nanoTime();
    
    System.out.println(Arrays.toString(a));
    System.out.println("耗时:"+(overTime-beginTime)/1000000.0+"ms");
}

1.3 算法时间对比

  • 插入排序与归并排序所用时间

在这里插入图片描述
在这里插入图片描述

  • 数据量越大两者的效率差距越大

2 分治策略

2.1 求最大数组和

给定一个有正有负的一个整数序列,求其一个子数组,满足数组之和为最大。

在这里插入图片描述

  • 代码
/**
 * <p>分治策略
 * <br/>
 * 解决方法分可为三个步骤:<br/>
 * 1.分解(divide):将问题划分为一些子问题,问题形式与原问题一样,只是规模更小<br/>
 * 2.解决(conquer):递归求解出子问题,如果子问题足够小,则停止递归,直接求解(这类问题主要就是找这个突破口)<br/>
 * 3.合并(combine):将子问题的解组合成原问题的解。
 * @author gu-ppy
 * @Package com.algorithm.divide
 * @Description: TODO
 * @date 2020/7/2 16:45
 */
public class DivideAndConquer {
    //集成
    public static int[] findMaxArraySum(int[]data){
        System.out.println("");
       return findMaxSum(data,0,data.length-1);
    }

    /**
    * <b> 查找最大子数组和 问题
    * <br/> 总体划分为三个子问题
    * @param data 原数据
    * @param left 找到的最大数组和的左下标
    * @param right 找到的最大数组和的右下标
    * @return int[] 共3位数据 1:左下标 2:右下标  3:最大数组和
    */
    public static int[] findMaxSum(int[] data, int left, int right){
        if(left==right){
            return new int[]{left, right, data[left]};
        }else{
            int mid = (right+left)/2;
            int [] leftArray,crossArray,rightArray;
            leftArray = findMaxSum(data,left,mid);             //递归查找在中点左边部分的最大数组和
            rightArray = findMaxSum(data, mid+1, right);       //递归查找在中点右边部分的最大数组和
            crossArray = findMaxSumCrossing(data,left,mid,right);  //查找穿过中点的最大数组和

            if (leftArray[2]>rightArray[2] && leftArray[2]>crossArray[2])
                return leftArray;
            else if (rightArray[2]>leftArray[2] && rightArray[2]>crossArray[2])
                return rightArray;
            else return crossArray;
        }
    }
    /**
     * <b> 查找最大数组和的子模块<br/>
     * What:查找穿过中点的最大子数组和<br/>
     * Why:由于其之和为最大,那么左边与右边同之和为相应部分的最大
     *
     * @param data 元数据
     * @param left 左下标
     * @param mid 中点下标
     * @param right 右下标
     * @return int[] 共3位数据 1:左下标 2:右下标  3:最大数组和
     */
    public static int[] findMaxSumCrossing(int[] data,int left, int mid, int right){
        int leftSum=Integer.MIN_VALUE,rightSum=Integer.MIN_VALUE,sum=0; //中间变量,和 计算
        int maxLeft = 0,maxRight = 0; //用于接收最大数组的左右两边界下标
        for (int i=mid;i>=left;i--){   //遍历左侧数组
            sum+=data[i];
            if(sum>leftSum){
                leftSum=sum;
                maxLeft = i;
            }
        }
        sum=0;
        for (int j=mid+1;j<=right;j++){  //遍历右侧数组
            sum+=data[j];
            if(sum>rightSum){
                rightSum=sum;
                maxRight = j;
            }
        }
        return new int[]{maxLeft,maxRight,rightSum+leftSum};
    }
}
  • 测试代码(junit包)
@Test
public void findMaxArraySum(){
    int[] data ={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7}; //16个数据
    int[]result = com.algorithm.divideDivideAndConquer.findMaxArraySum(data);
    System.out.println(Arrays.toString(result));
}

2.2 递归练习

整数划分:

  • n=m1+m2+…+mi; (其中mi为正整数,并且1 <= mi <= n),则{m1,m2,…,mi}为n的一个划分。
  • 如果{m1,m2,…,mi}中的最大值不超过m,即max(m1,m2,…,mi)<=m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m);
  • 编写程序,输入整数n,m,返回n的所有m的划分个数。
public static int integerDivide(int n, int m){
    if (n<1 || m < 1)  //不能为负数
        return 0;
    if (n==1||m==1)    //为1时只有一种
        return 1;
    if (n<=m)          //m大于n的部分无意义,而n=m就为一种划分
        return 1+integerDivide(n,n-1);
    return integerDivide(n-m,m)+integerDivide(n,m-1);  //两部分,一部分是包含m,一部分不包含m
}
  • 测试代码
@Test
public void integerDivide(){
    System.out.println(DivideAndConquer.integerDivide(10,10 ));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值