《剑指offer》——数组中的逆序对

来源:互联网 发布:费用优化例题 编辑:程序博客网 时间:2024/05/16 17:00

T:

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

  • 暴力搜索法

直接看代码,简洁明了。
code:

public int InversePairs(int [] array) {    int nums = 0;    for (int i = 1; i < array.length; i++) {        for (int j = 0; j < i; j++) {            if (array[j] > array[i]) {                nums ++;            }        }    }    return nums;}

如果只是单纯的从暴力搜索的角度去解决该问题,貌似简单了点儿,且这种情况下的耗时为O(n2)。我自己也没想出来用什么更简单高效的方法解决,之前有想到用动态规划思想,代码敲好后,突然发现思路有严重bug。。。

看到讨论版的代码,很多人说用归并排序,对于这个排序,我也忘得差不多了,重新学习!

  • more efficiency methods

先复习一下归并排序…
具体的算法思想不说了,给个链接:http://blog.csdn.net/morewindows/article/details/6678165/

这里写图片描述

code:

    package niuke.sward2offer.inversePairs;    /**     * 归并排序,递归方式     *      * date: 2015.11.11  22:00     * @author SSS     *     */    public class Solution1 {        /**         * 将两个有序数组合并为一个新数组,耗时O(n)         *          * @param array     // 共用一个数组,即都在数组array上进行两个数组片段的重新排序         * @param aStart         * @param aEnd         * @param bStart         * @param bEnd         * @param newArray         */        public void merge(int []array, int aStart, int aEnd, int bStart, int bEnd, int []newArray) {            int i = aStart;            int j = bStart;            int count = 0;  // 新数组的下标            while (i <= aEnd && j <= bEnd) {                if (array[i] < array[j]) {                    newArray[count ++] = array[i ++];                }                else {                    newArray[count ++] = array[j ++];                }            }            while (i <= aEnd) {                newArray[count++] = array[i++];            }            while (j <= bEnd) {                newArray[count++] = array[j++];            }            /**             * 因为是要在下标为aStart和bEnd之间进行排序,所以将要有序的数列按对应下标覆盖array中原有的数据。             */            for (int k = 0; k < bEnd - aStart + 1; k++) {                array[aStart + k] = newArray[k];            }        }        /**         * 归并排序的主体部分         * @param array         * @param start         * @param end         * @param newArray         */        public void mergeSort(int []array, int start, int end, int []newArray) {            if (start < end) {                // 将下标为start和end这一部分的子数组从中间分为左、右两个部分                int mid = (start + end) / 2;                mergeSort(array, start, mid, newArray); // 认为左边有序                mergeSort(array, mid + 1, end, newArray);   // 认为右边有序                this.merge(array, start, mid, mid + 1, end, newArray);              }        }        public static void main(String []args) {            int []array = {6, 3, 5, 9, 1, 8, 2, 7, 10, 4};            int []newArray = new int[array.length];            Solution1 solution1 = new Solution1();            solution1.mergeSort(array, 0, array.length - 1, newArray);            for (int i : newArray) {                System.out.println(i);            }        }    }

那么,能不能从这种方法的排序中得到点儿启发,从而算出原始数组中的逆序对呢?

归并排序的最底层,是从一个个的单个数字开始合并的,两个单个数字合并为一个二维有序数组,两个二维有序数组合并为一个有序的四维有序数组,……

归并排序的核心在于将两个有序数组合并为一个有序数组,代码如下:

    /**         * 将两个有序数组合并为一个新数组,耗时O(n)         *          * @param array     // 共用一个数组,即都在数组array上进行两个数组片段的重新排序         * @param aStart         * @param aEnd         * @param bStart         * @param bEnd         * @param newArray         */        public void merge(int []array, int aStart, int aEnd, int bStart, int bEnd, int []newArray) {            int i = aStart;            int j = bStart;            int count = 0;  // 新数组的下标            while (i <= aEnd && j <= bEnd) {                if (array[i] < array[j]) {                    newArray[count ++] = array[i ++];                }                else {                    newArray[count ++] = array[j ++];                }            }            while (i <= aEnd) {                newArray[count++] = array[i++];            }            while (j <= bEnd) {                newArray[count++] = array[j++];            }            /**             * 因为是要在下标为aStart和bEnd之间进行排序,所以将要有序的数列按对应下标覆盖array中原有的数据。             */            for (int k = 0; k < bEnd - aStart + 1; k++) {                array[aStart + k] = newArray[k];            }        }

比如将两个有序数组: A=[a1,a2,a3,]B=[b1,b2,b3,]合并为一个有序数组C=[b1,a1,a2,a3,b2,]. 另外还一个潜在的条件,在原数组中,就是数组A中的元素都在B中元素的左边。

假设a1>b1,那么b1要先放在数组C中,既然a1>b1,且数组A也是有序数组,那么数组A中元素a1之后的元素(包括a1)都要大于b1,即都可以和b1组成逆序对,后面的依次类推。。。

所以,在归并排序的过程中求解逆序对的方法如下:设置一个全局变量,用于统计每次调用merge(…)函数时存在的逆序对。

code:

    package niuke.sward2offer.inversePairs;    /**     * 在归并排序上的改进,可求得数组中的逆序对。     *      * date:2015.11.11  22:40     * @author SSS     *     */    public class Solution2 {        private int inversePairsCount = 0;        public int InversePairs(int [] array) {            int []newArray = new int[array.length];            this.mergeSort(array, 0, array.length - 1, newArray);            return inversePairsCount;        }        /**         * 将两个有序数组合并为一个新数组,耗时O(n)         *          * @param array     // 共用一个数组,即都在数组array上进行两个数组片段的重新排序         * @param aStart         * @param aEnd         * @param bStart         * @param bEnd         * @param newArray         */        public void merge(int []array, int aStart, int aEnd, int bStart, int bEnd, int []newArray) {            int i = aStart;            int j = bStart;            int count = 0;  // 新数组的下标            while (i <= aEnd && j <= bEnd) {                if (array[i] > array[j]) {                    newArray[count ++] = array[j ++];                    inversePairsCount += aEnd - i + 1;  // 将该轮合并当中出现的逆序对统计出来                }                else {                    newArray[count ++] = array[i ++];                }            }            while (i <= aEnd) {                newArray[count++] = array[i++];            }            while (j <= bEnd) {                newArray[count++] = array[j++];            }            /**             * 因为是要在下标为aStart和bEnd之间进行排序,所以将要有序的数列按对应下标覆盖array中原有的数据。             */            for (int k = 0; k < bEnd - aStart + 1; k++) {                array[aStart + k] = newArray[k];            }        }        /**         * 归并排序的主体部分         * @param array         * @param start         * @param end         * @param newArray         */        public void mergeSort(int []array, int start, int end, int []newArray) {            if (start < end) {                // 将下标为start和end这一部分的子数组从中间分为左、右两个部分                int mid = (start + end) / 2;                mergeSort(array, start, mid, newArray); // 认为左边有序                mergeSort(array, mid + 1, end, newArray);   // 认为右边有序                this.merge(array, start, mid, mid + 1, end, newArray);              }        }        public static void main(String []args) {            int []array = {5, 3, 4, 1};            Solution2 solution2 = new Solution2();            System.out.println("nums = " + solution2.InversePairs(array));        }    }
0 0
原创粉丝点击