归并排序

归并排序:

和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

  • 把长度为n的输入序列分成两个长度为n/2的子序列;

  • 对这两个子序列分别采用归并排序;

  • 将两个排序好的子序列合并成一个最终的排序序列。

递归方法:

package com.zy.base.class003;

public class Code01_MergeSort {
    public static void main(String[] args) {
        int[] arr = {10, 2, 5, 6, 1, 3, 9};
        new Code01_MergeSort().mergeSort(arr);
        System.out.println();
    }
    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        procss(arr, 0, arr.length - 1);

    }

    private static void procss(int[] arr, int start, int end) {
        if (start == end) {
            return;
        }
        int mid = start + (end - start) / 2;
        procss(arr, start, mid);
        procss(arr, mid + 1, end);
        merge(arr, start, mid, end);
    }

    private static void merge(int[] arr, int start, int mid, int end) {
        int[] helper = new int[end - start + 1];
        int left = start;
        int right = mid + 1;
        int helperIndex = 0;
        while (left <= mid && right <= end) {
            helper[helperIndex++] = arr[left] <= arr[right] ? arr[left++] : arr[right++];
        }

        while (left <= mid) {
            helper[helperIndex++] = arr[left++];
        }

        while (right <= end) {
            helper[helperIndex++] = arr[right++];
        }

        for (int i = 0; i < helper.length; i++) {
            arr[start + i] = helper[i];
        }
    }
}

非递归方法:

package com.zy.base.class003;

public class Code01_MergeSort1 {
    public static void main(String[] args) {
        int[] arr = {10, 2, 5, 6, 1, 3, 9};
        new Code01_MergeSort1().mergeSort(arr);
        System.out.println();
    }

    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        procss(arr, 0, arr.length - 1);
    }

    private static void procss(int[] arr, int start, int end) {
        if (start == end) {
            return;
        }
        int len = arr.length;
        int mergeSize = 1;
        while (mergeSize < len) {
            int left = 0;
            while (left < len) {
                int m = left + mergeSize - 1;
                if (m >= len) {
                    break;
                }
                int right = Math.min(m + mergeSize, len - 1);
                merge(arr, left, m, right);
                left = right + 1;
            }
            if (mergeSize > len / 2) {
                break;
            }
            mergeSize <<= 1;
        }



    }

    private static void merge(int[] arr, int start, int mid, int end) {
        int[] helper = new int[end - start + 1];
        int left = start;
        int right = mid + 1;
        int helperIndex = 0;
        while (left <= mid && right <= end) {
            helper[helperIndex++] = arr[left] <= arr[right] ? arr[left++] : arr[right++];
        }

        while (left <= mid) {
            helper[helperIndex++] = arr[left++];
        }

        while (right <= end) {
            helper[helperIndex++] = arr[right++];
        }

        for (int i = 0; i < helper.length; i++) {
            arr[start + i] = helper[i];
        }
    }
}

常见面试题:

在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和。求数组小和。

例如:[1, 3, 4, 2, 5]

1左边比1小的数:

3左边比3小的数:1

4左边比4小的数:1、3

2左边比2小的数:1

5左边比5小的数:1、3、4、2

所以数组的小和为1+1+3+1+1+3+4+2=16

上代码:

package com.zy.base.class003;

public class Code02_SmallSum {
    public static void main(String[] args) {
        int[] arr = {1, 3, 4, 2, 5};
        int i = Code02_SmallSum.smallSum(arr);
        System.out.println(i);
    }
    public static int smallSum(int[] arr) {
        if (arr == null || arr.length < 2) {
            return 0;
        }
        return process(arr, 0, arr.length - 1);
    }

    private static int process(int[] arr, int left, int right) {
        if (left == right) {
            return 0;
        }
        int mid = left + (right - left) / 2;
        return process(arr, left, mid) + process(arr, mid + 1, right) + merge(arr, left, mid, right);
    }

    private static int merge(int[] arr, int start, int mid, int end) {
        int[] helper = new int[end - start + 1];
        int res = 0;
        int left = start;
        int right = mid + 1;
        int helperIndex = 0;
        while (left <= mid && right <= end) {
            res += arr[left] < arr[right] ? (end - right + 1) * arr[left] : 0;
            helper[helperIndex++] = arr[left] < arr[right] ? arr[left++] : arr[right++];
        }
        while (left <= mid) {
            helper[helperIndex++] = arr[left++];
        }
        while (right <= end) {
            helper[helperIndex++] = arr[right++];
        }

        for (int i = 0; i < helper.length; i++) {
            arr[start + i] = helper[i];
        }
        return res;
    }
}

求逆序对

剑指 Offer 51. 数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

 

示例 1:

输入: [7,5,6,4]
输出: 5
 

限制:

0 <= 数组长度 <= 50000

package com.zy.offer;

/**
 * 剑指 Offer 51. 数组中的逆序对
 * 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
 *
 *
 *
 * 示例 1:
 *
 * 输入: [7,5,6,4]
 * 输出: 5
 *
 *
 * 限制:
 *
 * 0 <= 数组长度 <= 50000
 */
public class Offer51 {
    public int reversePairs(int[] nums) {
        if (nums == null || nums.length < 2) {
            return 0;
        }
        return process(nums, 0, nums.length - 1);

    }

    private int process(int[] nums, int start, int end) {
        if (start == end) {
            return 0;
        }
        int mid = start + (end - start) / 2;

        return process(nums, start, mid) + process(nums, mid + 1, end) + merge(nums, start, mid, end);

    }

    private int merge(int[] nums, int start, int mid, int end) {
        int[] help = new int[end - start + 1];
        int left = start;
        int right = mid + 1;
        int index = 0;
        int res = 0;
        while (left <= mid && right <= end) {
            res += nums[left] > nums[right] ? (mid - left + 1) : 0;
            help[index++] = nums[left] > nums[right] ? nums[right++] : nums[left++];
        }
        while (left <= mid) {
            help[index++] = nums[left++];
        }

        while (right <= end) {
            help[index++] = nums[right++];
        }

        for (int i = 0; i < help.length; i++) {
            nums[start + i] = help[i];
        }
        return res;
    }
}

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值