题目描述
多说无益~直接冲代码吧!
思路 && 代码
1. 暴力 O(n2n^2 n 2 )
乍一看这题目,很难不直接用暴力法冲一冲(也就双层循环的事) 但是不出意料地超时啦~想一想,O(n2n^2 n 2 )会超时,那么我们就试着思考怎么降低时间复杂度到O(nlogn)吧 !(还是不行的话,就再去思考O(n)的方法,循序渐进~) 于是,分治 的思路就跃然于纸上了~
class Solution { public int reversePairs ( int [ ] nums) { int res = 0 ; int len = nums. length; for ( int i = len - 1 ; i > 0 ; i-- ) { for ( int j = i - 1 ; j >= 0 ; j-- ) { if ( nums[ j] > nums[ i] ) { res++ ; } } } return res; }
}
2. 归并排序法(分治O(nlogn))
总体框架应该是和归并排序 一样的 从头思考,如果采用分治的方法进行二分,需要有怎样的考虑? 当前递归层的结果,首先应该是左半边内部的结果 + 右半边内部的结果 然后,应该再对左半边与右半边的联系 进行处理 为什么要排序? 首先,我们通过二分递归的形式,占用了O(logn)的复杂度 然后,我们需要使用剩下的O(n)复杂度,完成剩下的步骤 而排序,占用了O(n)复杂度,同时实现了剩下的步骤 左半边排序、右半边排序后,并不会影响左右半边直接的联系 ,也就是无后效性 合并操作 : 维护 lowIndex,用于记录插入左半边值后,新增的逆序对。 插入左半边值后,增加的逆序对 = 已经插入的右边值个数 注意:左半边当前值、右半边当前值相同 的情况,选择插入左半边 当前值,这是为了保证正确性。(可以写个例子考虑一下就知道原因了~)
class Solution { public int reversePairs ( int [ ] nums) { return mergeSort ( nums, 0 , nums. length - 1 ) ; } public int mergeSort ( int [ ] nums, int left, int right) { if ( left >= right) { return 0 ; } int mid = ( left + right) / 2 ; int res = mergeSort ( nums, left, mid) + mergeSort ( nums, mid + 1 , right) ; int i = left, j = mid + 1 ; int [ ] arr = new int [ right - left + 1 ] ; int lowIndex = 0 ; for ( int k = 0 ; k < arr. length; k++ ) { if ( i > mid) { arr[ k] = nums[ j++ ] ; } else if ( j > right || nums[ i] <= nums[ j] ) { res += lowIndex; arr[ k] = nums[ i++ ] ; } else if ( nums[ i] > nums[ j] ) { lowIndex++ ; arr[ k] = nums[ j++ ] ; } } for ( i = left; i <= right; i++ ) { nums[ i] = arr[ i - left] ; } return res; }
}
二刷
class Solution { public int reversePairs ( int [ ] nums) { return mergeSort ( nums, 0 , nums. length - 1 ) ; } int mergeSort ( int [ ] nums, int left, int right) { if ( left >= right) { return 0 ; } int mid = ( left + right) / 2 ; int res = mergeSort ( nums, left, mid) + mergeSort ( nums, mid + 1 , right) ; int [ ] arr = new int [ right - left + 1 ] ; int first = left, second = mid + 1 ; for ( int i = 0 ; i < arr. length; i++ ) { if ( first > mid) { arr[ i] = nums[ second++ ] ; } else if ( second > right || nums[ first] <= nums[ second] ) { arr[ i] = nums[ first++ ] ; res += second - mid - 1 ; } else if ( nums[ second] < nums[ first] ) { arr[ i] = nums[ second++ ] ; } } for ( int i = 0 ; i < arr. length; i++ ) { nums[ left + i] = arr[ i] ; } return res; }
}