剑指offer系列之三十四:数组中的逆序对

题目描述

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

要找到数组中的逆序对,一种思路是每遍历到一个元素的时候就和后面的元素进行一一比较,这种算法的时间复杂度是O(n2),所以不是很理想。我们可以借鉴归并排序的思想,把数组划分为子数组,然后对每个子数组中的逆序对数进行统计,统计完成后再并到一个新的数组中进行统计,这样就化整为零,实现了高效统计。总计起来思路就是:首先把数组划分为子数组,然后统计子数组中的逆序对数,然后继续统计相邻子数组的逆序对数,在统计相邻子数组中的逆序对数的时候,需要使用两个指针,一个指向第一个数组的尾部,一个指向第二个数组的尾部,如果第一个指针指向的元素大于后面的,说明相邻之间存在逆序对,并把较大的那个数拷贝到一个临时数组中,然后指针往前移动,直到所有的子数组以及相邻的子数组的逆序对数统计完毕。

下面是这种思路的实现代码(已被牛客AC):

package com.rhwayfun.offer;

public class InversePairsCount {

    public int InversePairs(int[] array) {
        if (array == null || array.length <= 0)
            return 0;
        int[] copy = new int[array.length];
        for (int i = 0; i < array.length; i++)
            copy[i] = array[i];
        int count = mergeCount(array, copy, 0, array.length - 1);
        return count;
    }

    private int mergeCount(int[] array, int[] copy, int start, int end) {
        if(start == end){
            copy[start] = array[start];
            return 0;
        }
        int len = (end - start) / 2;
        int leftCount = mergeCount(copy, array, start, start + len);
        int rightCount = mergeCount(copy, array, start + len + 1, end);
        //i指向第一个子数组的最后一个下标
        int i = start +len;
        //j指向第二个子数组的最后一个下标
        int j = end;
        int indexCopy = end;
        int count = 0;
        while(i >= start && j >= start + len + 1){
            if(array[i] > array[j]){
                copy[indexCopy--] = array[i--];
                count += j - start - len;
            }else{
                copy[indexCopy--] = array[j--];
            }
        }
        for(; i >= start; i--)
            copy[indexCopy--] = array[i];
        for(; j >= start + len + 1; j--)
            copy[indexCopy--] = array[j];
        return leftCount + rightCount + count;
    }

    public static void main(String[] args) {
        int[] array = new int[]{7,5,6,4};
        int count = new InversePairsCount().InversePairs(array);
        System.out.println(count);
    }
}
时间: 2024-09-01 07:00:58

剑指offer系列之三十四:数组中的逆序对的相关文章

剑指offer系列之三十:整数中1出现的次数

题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数. 有一种比较简洁的思路:由于是一个整数区间,所以可以对每一个数进行判断,那么如何判断某个数中1出现了多少次呢?注意到如果一个数一直被10除,如果被10整除后的余数是1,那么一定出现了数字1.举个例子,数字11

剑指offer系列之三十六:数字在排序数组中出现的次数

题目描述 统计一个数字在排序数组中出现的次数. 因为是排序数组,自然联想到二分查找算法,这样我们在二分的时候可能会获取多个相同的数字.就是说,中间那个位置的值可能刚好是统计的那个值,假设为k.那么k还有可能在前面或者后面出现,在这个基础上继续二分当然也是可以的,如果能够在使用二分查找算法的时候统计出第一个k和最后一个k出现的位置,那么k出现的次数自然就确定了.第一个k出现的位置,可以使用二分查找算法,如果中间位置的值刚好是k,那么继续比较中间位置前面位置的值是不是也是k,如果不是那么该中间位置就

剑指offer系列之三十九:数组中只出现一次的数字

题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 先考虑只有只有一个数字出现一次的情况,因为其他数字只出现了两次,所以对这两个数字进行异或运算的时候,其结果是0,那么那个只出现一次的数字进行异或运算的时候,其结果必然不是0,所以可以利用这点找出那个只出现一次的数字.现在考虑有两个数字出现一次的情况,仍然借鉴上面的思路,因为只出现一次的那个数字的异或结果不是0,遍历整个数组进行异或运算的结果也肯定不是0,现在可以对该数右边第一个是1的位的位置作

剑指offer系列之六十四:矩阵中的路径

题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子. 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子. 这实际上是回

剑指offer系列之三十八:判断是否是平衡二叉树

题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 所谓平衡二叉树就对某个结点来讲,其左子树的深度与右子树深度的绝对值不超过1.由于需要对每个节点进行判断,所以可以采用递归的思路进行解决.具体思路是:先求出根节点的左右子树的深度,并对两者进行判断,如果没有满足左右子树的深度的绝对值不超过1的条件,那么就不是平衡二叉树.下一步,自然就是分别对根节点的左右子树进行递归判断了,这样一直到叶子结点.整棵二叉树的所有节点都判断完毕,所以这个问题就解决了.下面是这种思路的实现代码(已被牛客AC): p

剑指offer系列之五十四:按之字形顺序打印二叉树

题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 此题明显是层序遍历的思路.由于需要按照之字形打印每一层,相当于在打印每一行之前需要判断上一行打印的顺序.比如,如果上一行打印的顺序使从左到右,那么下一行的打印顺序应该是从右到左.实现的这点可以采用奇数行从左到右打印,偶数行从右到左进行打印.还可直接设置 一个布尔变量,每打印一行就改变一次该变量的值.其他的就是层序遍历的思路了.下面是我实现的代

剑指offer系列之三十五:两个链表的第一个公共节点

题目描述 输入两个链表,找出它们的第一个公共结点. 由于是单链表,所以可以发现从第一个公共节点开始,后面的结点都是相同的,一种思路是从两个链表的尾部开始遍历,直到发现最后一个相同的结点为止,那么这最后一个相同的结点是单链表的角度看就是两个链表的第一个公共节点了.还有一种思路是不需要从尾部开始遍历,毕竟从尾部遍历单链表的话还得使用反转链表的方法进行操作,首先计算出两个链表的长度,让更长的那个单链表先移动两个链表长度差值个位置,然后两个链表同时移动,从更短的那个链表的第一个位置开始遍历,两个链表都往

剑指offer系列之四十四:翻转单词顺序

题目描述 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思.例如,"student. a am I".后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是"I am a student.".Cat对一一的翻转这些单词顺序可不在行,你能帮助他么? 可以发现,要对一个句子进行翻转,可以先对整个句子进行翻转,之后再对每个单词进行翻转.而不论是返

剑指offer系列之十四:反转链表

题目描述 输入一个链表,反转链表后,输出链表的所有元素. 思路如下:在遍历链表上的每个节点的时候,就修改其指针,当遍历到最后一个结点的时候,整个链表就反转完成了.所以需要创建三个变量:一个是当前遍历的结点,一个是遍历结点的前一个结点,还有一个是当前遍历结点的下一个结点.基于这种思路可以写出如下的实现代码(已被牛客AC): package com.rhwayfun.offer; public class ReverseLinkedList { public static class ListNod