【算法导论学习-011】数组中的逆序对个数(Counting inversions in an array)

来源:互联网 发布:潮汕的淘宝村 编辑:程序博客网 时间:2024/05/11 02:56
1、问题来源
《算法导论》P41思考题2-4:Let A[1..n] be an array of n distinct numbers. (i, j) is called an inversion of A if i < j and A[i] > A[j]. 
2、解决方案
参考1:http://www.geeksforgeeks.org/counting-inversions/
讨论区:http://stackoverflow.com/questions/337664/counting-inversions-in-an-array/338252#338252
方案1:不排序的双重循环(时间复杂度O(n^2))
 public static int getInversions(int[] array) {        int counter=0;        for (int i = 0; i < array.length; i++) {            for (int j = i+1; j < array.length; j++) {                if (array[i]>array[j]) {                    counter++;                }            }        }        return counter;           }
方案2:归并排序过程中计算逆序对(《算法导论》提示的方法,复杂度O(nlgn))
以下过程图解请参照《算法导论》P35页,注意对比本博客中【算法导论学习-002】归并排序(MergeSort)的异同。
<span style="font-size:18px;">/**  * 创建时间:2014年8月10日 下午4:12:19  * 项目名称:Test  * @author Cao Yanfeng  * @since JDK 1.6.0_21  * 类说明:  */public class CountInversionsTest {     /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        int[] array={5,2,4,7,1,3,2,6};//      int result=getInversions(array);        int result=mergeSort(array, 0, array.length-1);        System.out.println(result);            }    public static int mergeSort(int[] arr, int start, int end) {        if (start < end) {            int middle = (start + end) >>> 1;         </span><span style="font-size:18px;color:#cc0000;"><strong>   int l=mergeSort(arr, start, middle);            int r=mergeSort(arr, middle + 1, end);            int m=mymerge(arr, start, middle, end);            return l+r+m;        }else {            return 0;</strong></span><span style="font-size:18px;">        }    }     /*     * 归并排序的思路:首先将arr分成两部分,用leftArray和rightArray暂存,然后从头开始比较leftArray和rightArray,     * 谁小谁放入arr,相应指针移动     * 小技巧:leftArray和rightArray都增多最后一位,存放Integer.MAX_VALUE,用于不让它移动到最后     */    public static int mymerge(int[] arr, int start, int middle, int end) {        /*拆分数组为leftArray和rightArray*/        int leftArrayLength = middle - start + 1;        int rightArrayLength = end - middle;        int[] leftArray = new int[leftArrayLength + 1];//多一位        int[] rightArray = new int[rightArrayLength + 1];//多一位        for (int i = 0; i < leftArrayLength; i++) {            leftArray[i] = arr[start + i];        }        for (int i = 0; i < rightArrayLength; i++) {            rightArray[i] = arr[middle + 1 + i];        }        /*多的最后一位记得要赋最大值*/        rightArray[rightArrayLength] = Integer.MAX_VALUE;        leftArray[leftArrayLength] = Integer.MAX_VALUE;        /*算法的关键部分,左右指针移动*/        int j = 0;        int k = 0;       </span><strong style="font-size: 16px;"><span style="color:#993300;"> int number=0;</span></strong><span style="font-size:18px;">        for (int i = start; i <= end; i++) {            if (leftArray[j] </span><span style="color:#cc0000;font-size: 16px;"><strong><=</strong></span><span style="font-size:18px;"> rightArray[k]) {                arr[i] = leftArray[j];                j++;            } else {                arr[i] = rightArray[k];                k++;               </span><span style="color:#990000;font-size: 16px;"><strong> number+=leftArrayLength-j;</strong></span><span style="font-size:18px;">            }        }        return number;     } }</span>
方案3:制作数组排序后的副本,利用原数组每个元素在副本中进行二分搜索(复杂度O(nlgn)​
    例如: int[] array={5,2,4,7,1,3,2,6};
    制作排序后的副本​  int[] temp={1,2,2,3,4,5,6}
    array[0]=5,在temp中二分搜索,得到下表index=5,比它小的有inversions=​index-0=5个
    temp​去除5,int[] temp={1,2,2,3,4,6}​
    array[1]=2,同理inversions=​index-0=1个​
    同理,对于之后的inversions分别是 3,4,1​,0,,0,总计5+1+3+4+1=14个
*******************************************************************************************************************************************************
最后说明,对于一系列数来说,他们的所有排列的逆序对的期望是:n(n-1)/4。
讨论区:http://stackoverflow.com/questions/7804681/the-expected-number-of-inversions-from-introduction-to-algorithms-by-cormen


0 0