数组中的逆序对

一、需求

  • 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
  • 输入一个数组,求出这个数组中的逆序对的总数。

二、暴力法

2.1  思路分析

  1. 使用双重循环,外循环遍历数组每个元素,内循环遍历外循环当前元素之后的元素,符合条件的进行计数; 

 2.2  代码实现

class Solution {
    public int reversePairs(int[] nums) {
        int count = 0;
        for(int i = 0; i < nums.length; i++) {
            for(int j = i+1; j < nums.length; j++) {
                if(nums[i] > nums[j]) count++;
            }
        }
        return count;
    }
}

2.3  复杂度分析

  • 时间复杂度为O(N^2);
  • 空间复杂度为O(1);

三、动态规划法

3.1  思路分析

  1. 看到要求方案总数,故想到动态规划,定义状态dp[i]表示以nums[i]为结尾的逆序总数;
  2. 初始化状态dp[0] = 0;
  3. 关于状态转移方程,需要dp[i-1]加上[nums[0],nums[i-1]]中大于nums[i]的个数,假设用count表示,那么dp[i] = dp[i-1] + count;
  4. 该方法同暴力法一样,具有较高的时间复杂度,在测试中超时。

3.2  代码实现

class Solution {
    public int reversePairs(int[] nums) {
        if(nums.length == 0) return 0;
        int[] dp = new int[nums.length];
        dp[0] = 0;
        for(int i = 1; i < nums.length; i++) {
            int count = reverseCount(nums,i);
            dp[i] = dp[i-1] + count;
        }
        return dp[nums.length-1];
    }
    //返回count
    public int reverseCount(int[] nums,int i) {
        int res = 0;
        for(int j = 0; j < i; j++) {
            res += nums[j] > nums[i] ? 1 : 0;
        }
        return res;
    }
}

3.3  复杂度分析

  • 时间复杂度为O(N^2);
  • 空间复杂度为O(N),当然因为dp[i]只与dp[i-1]及常数count有关,因此可用变量tmp代替迭代过程,使得空间复杂度为O(1);

四、归并排序+分治法

4.1  思路分析

  1. 假设现在数组划分成的左(leftArr)、右(rightArr)数组均有序,通过leetcode官方题解可知,若遍历右数组中的元素(索引 j )小于遍历左数组的元素(索引 i )时,那么从索引 i ~ 索引 j 构成的逆序个数就是左数组中剩余的元素( i 之前的元素被合并到一个新的数组中);
  2. 我们首先在之前写的归并排序代码的基础上实现,然后在利用官方的形式实现一遍,虽然形式不同,但原理相同;

4.2  代码实现1

class Solution {

	/**
	 * 该方法更具一般性,不用传入中间值mid
	 * @param arr
	 * @param left
	 * @param right
	 */
	public static int merge(int[] arr, int left, int right) {
		//获取中间值
		int mid = left + (right - left) / 2;
		// 初始化左、右子数组长度
		int leftSize = mid - left + 1;
		int rightSize = right - mid;
		// 新建左、右子数组
		int[] leftArr = new int[leftSize];
		int[] rightArr = new int[rightSize];
		// 初始化左、右子数组
		for (int i = left; i <= mid; i++) {
			leftArr[i - left] = arr[i];
		}
		for (int j = mid + 1; j <= right; j++) {
			rightArr[j - mid - 1] = arr[j];
		}
		// 新建三指针
		int i = 0;
		int j = 0;
		int k = left;
		int count = 0;
		// 开始合并有序数组
		while (i < leftSize && j < rightSize) {
			if (leftArr[i] <= rightArr[j]) {
				arr[k] = leftArr[i];
				i++;
				k++;
			} else {
				arr[k] = rightArr[j];
				j++;
				k++;
				count += mid - i - left + 1;
			}
		}
		while (i < leftSize) {
			arr[k] = leftArr[i];
			i++;
			k++;
		}
		while (j < rightSize) {
			arr[k] = rightArr[j];
			j++;
			k++;
		}
		return count;
	}
	/**
	 * 该方法用来实现对无序数组的排序
	 * 假定左子数组区间[left,mid],右子数组区间[mid+1,right]
	 * @param arr
	 * @param left
	 * @param right
	 */
	public static int mergeSort(int[] arr,int left,int right) {
		if(left == right) return 0;
		int mid = left + (right - left)/2;
		int leftCount = mergeSort(arr,left,mid);
		int rightCount = mergeSort(arr,mid+1,right);
		int crossCount = merge(arr,left,right);
		return leftCount + rightCount + crossCount;
	}
	
	public static void main(String[] args) {
		int[] arr = {6,8,9,10,1,5,2,7};
		//int[] arr = {7,5,6,4};
		int count = mergeSort(arr,0,7);
		System.out.println(count);
	}
}

4.3  代码实现2

class Solution {
    public int reversePairs(int[] nums) {
        if(nums.length < 2) {
            return 0;
        }
        //拷贝原数组
        int[] copy = new int[nums.length];
        for(int i = 0; i < copy.length; i++) {
            copy[i] = nums[i];
        }
        //用于归并排序
        int[] temp = new int[nums.length];
        return mergeSortPairs(copy,0,copy.length-1,temp);//[Left,Right]
    }
    /**
     * 该方法用来返回逆序总数
     */
    public int mergeSortPairs(int[] copy,int left,int right,int[] temp) {
        if(left == right) {
            return 0;
        }
        int mid = left + (right - left)/2;
        int leftPairs = mergeSortPairs(copy,left,mid,temp);
        int rightPairs = mergeSortPairs(copy,mid+1,right,temp);
        //若当前已经是个有序数组了,就直接返回
        if(copy[mid] <= copy[mid+1]) {
            return leftPairs + rightPairs;
        }
        int crossPairs = mergeSortCount(copy,left,right,temp);
        return leftPairs + rightPairs + crossPairs; 
    }
    /**
     * 该方法返回当前数组对应的逆序数
     */
    public int mergeSortCount(int[] copy,int left,int right,int[] temp) {
        for(int i = left; i <= right; i++) {
            temp[i] = copy[i];
        }
        int mid = left + (right - left)/2;
        int i = left;
        int j = mid + 1;
        int count = 0;
        for(int k = left; k <= right; k++) {
            if(i == mid + 1) {
                copy[k] = temp[j];
                j++;
            } else if(j == right + 1) {
                copy[k] = temp[i];
                i++;
            } else if(temp[i] <= temp[j]) {
                copy[k] = temp[i];
                i++;
            } else {
                copy[k] = temp[j];
                j++;
                count += mid - i + 1;
            }
        }
        return count;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值