题目描述
多说无益~直接冲代码吧!
思路 && 代码
1. 暴力 O(
n
2
n^2
n 2 )
乍一看这题目,很难不直接用暴力法冲一冲(也就双层循环的事) 但是不出意料地超时啦~想一想,O(
n
2
n^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;
}
}