分治算法的思想

分治算法(divide and conquer)的核心思想其实就是四个字,分而治之 ,也就是将原问题划分成n个规模较小,并且结构与原问题相似的子问题,递归地解决这些 子问题,然后再合并其结果,就得到原问题的解。 这个定义看起来有点类似递归的定义。

关于分治和递归的区别,分治算法是一种处理问题的思想,递归是一种编程技巧。分治算法一般都比较适合用递归来实现。

分治算法的递归实现中,每一层递归都会涉及这样三个操作:

分治算法能解决的问题,一般需要满足下面这几个条件:

在归并排序的同时, 求原数组有多少个逆序对

public class LeetCode {

    public static void main(String[] args) {
        Merge merge = new Merge();
        int[] a = new int[]{1,5,6,2,3,4};
        merge.mergeStart(a);
        // 排序好的数组
        System.out.println(Arrays.toString(a));
        // 逆序对数
        System.out.println(merge.num);
    }
}

class Merge {
    /**
     * 存储原始数组有多少个逆序对
     */
    public int num = 0;
    
    public void mergeStart(int[] a) {
        merge(a, 0, a.length - 1);
    }

    private void merge(int[] a, int l, int r) {
        // l >= r代表当前已经无法分割
        if (l >= r) return;

        int mid = (l + r) / 2;

        merge(a, l, mid);
        merge(a, mid + 1, r);

        int left = l;
        int right = mid + 1;
        // 临时数组用来存储排序好的元素
        int[] tmp = new int[r - l + 1];
        // 临时数组下标
        int k = 0;
        // 当左指针和右指针后面还都有元素的时候
        while (left <= mid && right <= r) {
            if (a[left] < a[right]) {
                // 左指针数比右指针数小, 把左指针数放到临时数组
                tmp[k++] = a[left++];
            } else {
                tmp[k++] = a[right++];
                // 否则就是逆序对, 这时候左边数组里, 左指针后面的所有元素都比a[right]要大, 所以都是逆序对
                num += (mid - left + 1);
            }
        }
        // 处理左边剩下的数
        while (left <= mid) {
            tmp[k++] = a[left++];
        }
        // 处理右边剩下的数
        while (right <= r) {
            tmp[k++] = a[right++];
        }
        // 把排序好的数组赋值给原数组
        for (int i = 0; i < tmp.length; i++) {
            a[l + i] = tmp[i];
        }
    }
}