归并排序与数组小和问题求解

排序

归并排序

思想

归并排序算法有两个基本的操作,

一个是,也就是把原数组划分成两个子数组的过程。

一个是,它将两个有序数组合并成一个更大的有序数组。

它将数组平均分成两部分: center = (left + right)/2,当数组分得足够小时—数组中只有一个元素时,只有一个元素的数组自然而然地就可以视为是有序的,此时就可以进行合并操作了。

因此,上面讲的合并两个有序的子数组,是从 只有一个元素 的两个子数组开始合并的。

当合并两个数组的时候,如果遇到待比较的元素是相同的值时,那么先拷贝右边的,后拷贝左边的。其实先后无所谓,主要是为了方便统一,在类似的题目:找数组小和的题目。考虑右边有多少比自己大的,那么需要先把右边的拷贝进去,因为拷贝左边进数组也不能确定右边数组有多少比自己大的元素。

所以在比较的时候,只有左边严格的小于右边,才会进行拷贝。

代码实现

package com.vitamin.sort;

import org.junit.Test;

import java.util.Arrays;

/**
 * 归并排序
 */
public class MergeSort {


    @Test
    public void test() {
        MergeSort mergeSort = new MergeSort();
        mergeSort.sort();
        mergeSort.print();
    }


    int arr[] = new int[]{5, 6, 7, 10, 2, 30, 8, 9, 10, 2};

    public void print() {
        Arrays.stream(arr).forEach(System.out::println);
    }

    public void sort() {
        if (arr == null || arr.length <= 1) {
            return;
        }
        sortArr(0, arr.length - 1);
    }

    private void sortArr(int begin, int end) {
        if (end == begin) {
            return;
        }
        // 分治,分别排序
        int middle = begin + ((end - begin) >> 1);
        sortArr(begin, middle);
        sortArr(middle + 1, end);
        // 进行归并操作
        merge(begin, middle, end);

    }

    /**
     * 归并操作
     * 传递数值:  0,1,2
     * @param begin
     * @param middle
     * @param end
     */
    private void merge(int begin, int middle, int end) {

        if (end == begin) {
            return;
        }
        //创建辅助数组
        int helpArr[] = new int[end - begin + 1];
        int i = 0;
        int leftStart = begin;
        int rightStart = middle + 1;
        while (leftStart <= middle && rightStart <= end) {
            helpArr[i++] = arr[leftStart] < arr[rightStart] ? arr[leftStart++] : arr[rightStart++];
        }
        while (leftStart <= middle) {
            helpArr[i++] = arr[leftStart++];
        }
        while (rightStart <= end) {
            helpArr[i++] = arr[rightStart++];
        }
        // 原数组实现排序
        for (int j = 0; j < helpArr.length; j++) {
            arr[j + begin] = helpArr[j];
        }

    }

}

数组小和

注意:小和的求解一定要在数组排序之前进行。

private int sortArr(int begin, int end) {
    if (end == begin) {
        return 0;
    }
    // 分治,分别排序
    int middle = begin + ((end - begin) >> 1);
    return sortArr(begin, middle) +
            sortArr(middle + 1, end) +
            // 进行归并操作
            merge(begin, middle, end);

}

/**
 * @param begin
 * @param middle
 * @param end
 */
private int merge(int begin, int middle, int end) {

    // 只有一个元素的时候是不会产生小和的
    if (end == begin) {
        return 0;
    }
    //创建辅助数组
    int helpArr[] = new int[end - begin + 1];
    int i = 0;
    int leftStart = begin;
    int rightStart = middle + 1;
    // 小和
    int sum = 0;
    // 1 1 1 2 5    1 1 5 5 6
    // 1 1
    while (leftStart <= middle && rightStart <= end) {
        sum += arr[leftStart] < arr[rightStart] ? arr[leftStart] * (end - rightStart + 1) : 0;
        helpArr[i++] = arr[leftStart] < arr[rightStart] ? arr[leftStart++] : arr[rightStart++];

    }
    while (leftStart <= middle) {
        helpArr[i++] = arr[leftStart++];
    }
    while (rightStart <= end) {
        helpArr[i++] = arr[rightStart++];
    }
    // 原数组实现排序
    for (int j = 0; j < helpArr.length; j++) {
        arr[j + begin] = helpArr[j];
    }
    return sum;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值