java实现逆序数对

依据分治法,如果我们将数组分解成两个子序列,分别求出两个子序列的逆序数,再求出两个子序列之间元素的逆序数,就可以得出整个数组的逆序数了。可以做以下考虑:

            分解:将问题分成前后两个规模为n/2的数组

            解决:分别求解各自的逆序对数。如果子问题规模为2或1,可直接求解。

            合并:此时虽然知道两个子序列各自的逆序对数,但两个子序列之间的逆序对数无法轻易获知,如果进行两两比较的话,合并操作的时间复杂度就是n2 ,分治法没有意义。

            再考虑上述“合并”的问题,如果此时两个子序列都是有序的话,则通过修改合并排序的MERG过程就可以得出子序列之间的逆序数:在MERG对两个子序列的第一个元素之间进行选则时,如果前一个序列的首元素被选中,则逆序数不变——该元素不会和后一个序列中的剩下元素构成逆序对,如果第二个序列的首元素被选中,则逆序数增加“第一个序列剩下的元素数”——该元素和前一序列中剩下的每个元素构成逆序对,MERG后这些逆序对消除。按着这个思路分治算法重新设计如下:

            分解:将问题分成前后两个规模为n/2的数组

            解决:分别进行递归合并排序,并记录累加排序所消除的的逆序对数。如果子问题规模为2或1,可直接求解。

            合并:通过合并排序的MERG进行合并,在MERG过程中按上述方法累加逆序数。

PS:在最初用分治法考虑问题(4)时,排序的作用在一开并不那么明显,但通过对“合并”的分析,要求对子问题的求解需要产生“排序”的副作用。这种”副作用“在分治法中是值得注意的。

 

 

import java.util.Arrays; 
  
public class Test { 
    public staticint count = 0; 
  
    public staticvoid main(String[] args) {  
        int arr[] = { 6, 5, 4, 3, 2, 1 }; 
        int[] result = sort_and_count(arr); 
        System.out.println("逆序数:"+count); 
        for(int i=0;i<result.length;i++){ 
            System.out.print(result[i]+" "); 
        }  
    }  
  
    private staticint[] sort_and_count(int[] arr) { 
        if (arr.length == 1) { 
            return arr; 
        }  
        int length = arr.length; 
        int alength = length / 2;  
        int A[] = Arrays.copyOfRange(arr,0, alength);  
        int B[] = Arrays.copyOfRange(arr, alength, length); 
        A = sort_and_count(A);  
        B = sort_and_count(B);  
        arr = merge_and_count(A, B);  
        return arr;  
    }  
    //此函数有两个功能:  
    //(1)归并排序中的归并  
    //(2)计算逆序数  
    private staticint[] merge_and_count(int[] a,int[] b) {  
        int i = 0; 
        int j = 0; 
        int result[] = newint[a.length+b.length];  
        int current = 0;  
        while (i < a.length && j < b.length) { 
            if(a[i]<b[j]){        
                result[current++] = a[i];  
                i++;  
            }  
            if(a[i]>b[j]){ 
                result[current++] = b[j];  
                count += (a.length - i);  
                j++;  
            }  
        }  
        if(i==a.length){     
            for(;j<b.length;j++){  
                result[current++] = b[j];   
            }  
        }  
        if(j==b.length){  
            for(;i<a.length;i++){ 
                result[current++] = a[i];   
            }  
        }  
        return result;  
    }  
}  


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值