zoukankan      html  css  js  c++  java
  • lintcode:逆序对

    题目

    在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。给你一个数组,求出这个数组中逆序对的总数。
    概括:如果a[i] > a[j] 且 i < j, a[i] 和 a[j] 构成一个逆序对。

    样例

    序列 [2, 4, 1, 3, 5] 中,有 3 个逆序对 (2, 1)(4, 1)(4, 3),则返回 3 。

    解题

    直接暴力找,时间复杂度O(n^2)

    public class Solution {
        /**
         * @param A an array
         * @return total of reverse pairs
         */
        public long reversePairs(int[] A) {
            // Write your code here
            long res = 0;
            int n = A.length;
            for(int i=0;i<n;i++){
                for(int j=i+1;j<n;j++){
                    if(A[i] >A[j]){
                        res ++;
                    }
                }
            }
            return res;
        }
    }

    归并排序的思想

    对于数组A[p,...,q]

    分成两个数组A[p,...,r],A[r+1,...,q]

    当这两个数组都是有序的时候,其逆序对数很好求

    可以两个指针i,j分布指向数组的尾部

    当A[i]>A[j]时候一定是逆序对,注意是有序的,逆序对数量:j-(r+1) +1 = j-r

    else 不是逆序对

    程序

    public class Solution {
        /**
         * @param A an array
         * @return total of reverse pairs
         */
         long res = 0;
        public long reversePairs(int[] A) {
            // Write your code here
            
            int n = A.length;
            reversP(A,0,n-1);
            return res;
        }
        public void reversP(int[] A,int low,int high){
            if(low>=high)
                return;
            int mid = low + (high - low)/2;
            reversP(A,low,mid);
            reversP(A,mid+1,high);
            merge(A,low,mid,high);
        }
        public void merge(int[] A,int low ,int mid ,int high){
            int len = high - low + 1; 
            int[] C = new int[len]; // 临时存放中间归并数组
            int i = mid;
            int j = high;
            int k = len -1;
            while(i>= low && j>=mid+1){
                if(A[i] > A[j]){
                    C[k--] = A[i];
                    i--;
                    res += j - (mid + 1) +1; // 逆序对数量
                }else{
                    C[k--] = A[j];
                    j--;
                }
            }
            while(i>=low){
                C[k--] = A[i];
                i--;
            }
            while(j>=mid+1){
                C[k--] = A[j];
                j--;
            }
    
            for(k=0;k<len;k++){
                A[k+low] = C[k];
            }
    
        }
        public void print(int[] A){
            for(int a:A){
                System.out.print(a+"	");
            }
            System.out.println();
        }
    }

    分析下输出过程

    测试样例:[2,4,1,3,5]

    5个元素划分的区间

    元素下标上界是4

    [0,4]划分[0,2]、[3,4]

    ---[0,2]划分:[0,1]、[2]

    -------[0,1]划分:[0]、[1]

    ---[3,4]划分:[3]、[4]

    输出情况

    2   4   1   3   5   最底层只有一个元素
    1   2   4   3   5   [0,2]合并
    1   2   4   3   5   
    1   2   3   4   5  [0,4]合并
     算法可行性
    一个数组分成B、C两部分,B、C两部分分布升序排序
    对于A[i] 在B中,A[j]在C中的情况,关于A[i]与A[j]的逆序对数量与B中A[i]的位置、C中A[j]的位置无关,这个很显然
    程序开始的时候找到是1个元素,后来合并成两个元素的数组,这样慢慢的合并,并计算逆序对的数量,最后就得到答案了


  • 相关阅读:
    2429: [HAOI2006]聪明的猴子
    1789: [Ahoi2008]Necklace Y型项链
    3399: [Usaco2009 Mar]Sand Castle城堡
    3713: [PA2014]Iloczyn
    1907: 树的路径覆盖
    2751: [HAOI2012]容易题(easy)
    算法模板——计算几何2(二维凸包——Andrew算法)
    算法模板——splay区间反转 2
    算法模板——Dinic网络最大流 2
    算法模板——Dinic最小费用最大流
  • 原文地址:https://www.cnblogs.com/bbbblog/p/5649519.html
Copyright © 2011-2022 走看看