算法-逆序对(归并排序)

我觉得每次博客时,开头的话语是最难写的,这次就不写了,但是要鼓励自己好好学习!

题意:

在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个
逆序对。给你一个数组,求出这个数组中逆序对的总数。
概括:如果a[i] > a[j] 且 i < j, a[i] 和 a[j] 构成一个逆序对。

样例:

序列 [2, 4, 1, 3, 5] 中,有 3 个逆序对 (2, 1), (4, 1), (4, 3),则返回 3 

这题理解起来不是很难的,但是麻烦的地方就是时间复杂度。如果用常规的做法,肯定会超时,所以只能用非常规的方法了,但是这里贴出常规的代码,就当是给自己一个警示!

1.常规的方法(超时)

常规的方法非常简单,这里就不再解释了

 1     public static long reversePairs(int[] A) {
 2         int sum = 0;
 3         for(int i = 0; i < A.length - 1; i++){
 4             for(int j = i + 1; j < A.length; j++){
 5                 if(A[i] > A[j]){
 6                     sum++;
 7                 }
 8             }
 9         }
10         return sum;
11     }

2.非常规的方法--归并排序

归并排序有一个特点就是:它是将一个数组分为两部分,然后将两部分分别进行排序,在合并之前,两个部分之间的数字index大小不会变化,例如:1 4 3 2分成两部分后分别是{1,4}和{3,2},排序之后是{1,4}和{2,3},在排序之前,{1,4}在{2,3}的前面,而在排序之后(合并之前),{1,4}也在{2,3}的前面,所以经过排序(合并之前),两个部分之间的顺序不会变

归并排序最终分解是,将两个数字分成了两个部分,基于归并排序的上面那个特点,两个数字的位置不会因为大小而改变,因为这时候还没有合并,只是两个部分。求逆序对的话,就看看这两个符不符合。就这样,先两两配对,后面在44配对,由于之间的22配对,已经将44配对每一个部分的内部逆序对计算出来,所以只需要计算44配对之间的逆序对。

假设,我们用num来记录逆序对的个数,当我们在合并的两个部分时,会分别的比较两个部分的值,先将较小的值放在一个新的数组里面去。假设两个部分分别是用i和j来控制(其中i <= mid && j <= height),数组为nums[],如果nums[i] <= nums[j],这个直接放入新的数组里面,因为它不符合逆序对的规则(逆序对的规则是:n < m && nums[n] > nums[m]);但是当nums[i] > nums[j]时,需要注意的是,这时候就符合逆序对的规则了,但是这里不是简单的加1。想一下,当前nums[i] > nums[j],那么num[i + 1~ mid]之间的数字是不是也大于nums[j],所以当前符合要求的个数是: mid - i + 1个数字(但是这里可能有人疑问,为什么要把nums[i + 1 ~ mid]的情况加进去,想一下,如果这里的nums[j]只与nums[i]配对的话,经过本次循环,j++,后面的nums[i  + 1~ mid]不会在nums[j],因此 j 已经变了,所以这里必须将nums[i + 1 ~ mid]的情况考虑进去).

理解了上面的解释,看下面的代码才会很好的理解:

 1     private static int result = 0;
 2
 3     public static long reversePairs(int[] A) {
 4         mergeSort(A, 0, A.length - 1);
 5         return result;
 6     }
 7
 8     private static void mergeSort(int nums[], int low, int height) {
 9         if (low < height) {
10             int mid = (low + height) / 2;
11             mergeSort(nums, low, mid);
12             mergeSort(nums, mid + 1, height);
13
14             merge(nums, low, mid, height);
15         }
16     }
17
18     private static void merge(int[] nums, int low, int mid, int height) {
19         int temp[] = new int[height - low + 1];
20         int i = low;
21         int j = mid + 1;
22         int k = 0;
23         while (i <= mid && j <= height) {
24             if (nums[i] < nums[j]) {
25                 temp[k++] = nums[i++];
26             } else {
27                 temp[k++] = nums [j++];
28                 result += (mid-i+1);
29             }
30         }
31         while (i <= mid) {
32             temp[k++] = nums[i++];
33         }
34         while (j <= height) {
35             temp[k++] = nums[j++];
36         }
37         for (int n = 0; n < temp.length; n++) {
38             nums[n + low] = temp[n];
39         }
40     }
时间: 2024-10-21 07:52:51

算法-逆序对(归并排序)的相关文章

洛谷P1908 求逆序对 [归并排序]

题目描述 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游 戏,现在他们喜欢玩统计.最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ai>aj且i<j的有序对.知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目. 输入输出格式 输入格式: 第一行,一个数n,表示序列中有n个数. 第二行n个数,表示给定的序列. 输出格式: 给定序列中逆序对的数目. 输入输出样例

[剑指offer]51-数组中的逆序对(归并排序)

题目链接 https://www.nowcoder.com/questionTerminal/96bd6684e04a44eb80e6a68efc0ec6c5 题意 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000000007.题目保证输入的数组中没有的相同的数字. 解题思路 在归并排序的过程中计逆序对.时间复杂度O(nlogn),空间复杂度O(n). 将

逆序对&amp;归并排序:新斯诺克

题目描述  Description 斯诺克又称英式台球,是一种流行的台球运动.在球桌上,台面四角以及两长边中心位置各有一个球洞,使用的球分别为1 个白球,15 个红球和6 个彩球(黄.绿.棕.蓝.粉红.黑)共22个球. 击球顺序为一个红球.一个彩球直到红球全部落袋,然后以黄.绿.棕.蓝.粉红.黑的顺序逐个击球,最后以得分高者为胜.斯诺克的魅力还在于可以打防守球,可以制造一些障碍球使对方无法击打目标球而被扣分.正是因为这样,斯诺克是一项充满神奇的运动. 现在考虑这样一种新斯诺克,设母球(母球即是白

逆序对

什么是逆序对?? 我们在这里给出一个定义:如果i<j&&a[i]>a[j]的一对数称为逆序对. 为什么??!! 背过!!(都说了是定义了!)(其实我也不知道为什么  orz   (*^__^*) ……) 但是知道逆序对有什么用处呢? 具体用处我也不是很清楚,但是目前我们可以用逆序对的个数求将一列数排成有序的一列数交换的次数 怎样求逆序对呢?? 我们在这里给出两种算法:归并排序求逆序对:树状数组求逆序对. §归并排序求逆序对 归并排序的核心思想是二分. 我们将一个序列a1,a2,

二分&#183;归并排序之逆序对 算法讲解和题目

题目: #1141 : 二分·归并排序之逆序对 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 在上一回.上上回以及上上上回里我们知道Nettle在玩<艦これ>.经过了一番苦战之后,Nettle又获得了的很多很多的船.这一天Nettle在检查自己的舰队列表:我们可以看到,船默认排序是以等级为参数.但实际上一个船的火力值和等级的关系并不大,所以会存在A船比B船等级高,但是A船火力却低于B船这样的情况.比如上图中77级的飞龙改二火力就小于55级的夕立改二.现在Nett

[PHP] 算法-数组归并排序并计算逆序对的个数的PHP实现

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000000007 1.数组归并排序 2.归并排序比较左右两个堆数组中的元素大小时,进行计数,倒着比较,因为左堆倒第一如果比右堆倒第一大,那么就比右堆的所有都大 mergeSort if left<right mid=[(p+r)/2] mergeSort(arr,left,mid,temp) mergeSort(

算法学习——利用归并排序求逆序对的数量

首先明白逆序对的定义,逆序对就是数组中两个元素前大后小,我们就称这两个元素为一组逆序对. 接着看题目:  我们利用分治的思想,将区间一分为二,然后得到了逆序对的存在情况共三种: 1.两个元素都在左侧区间. 2.两个元素都在右侧区间. 3.两个元素一个在左,一个在右. 那么很明显我们分治的去解决这个问题,就得到解法, 1.将区间划分成左右两个区间 2.递归左右两个区间 3.统计逆序对数量(1)+(2) 4.计算逆序对数量(3) 5.返回相应的结果. 那么问题来了,逆序对(1)(2)的情况都很容易理

归并排序求逆序对

归并排序求逆序对 by mps [1]什么是逆序对? 对于一个数列需要按从小到大排序,如果有ai,aj且满足ai>aj和i<j则ai,aj为一组逆序对 [2]如何求逆序对? 我们发现,我们可以暴力枚举i,j,然后逐一判断并累加答案即可,时间复杂度O(N2)        但是对于数据量大一点的题目,只有不断地TLE了→_→ [3]归并排序求逆序对 逆序对的定义(见[1])是一组本应该有序的序列中的逆序对,那么我们就想到了排序,但由于是要22匹配,我们又想到了归并排序 归并排序大致内容如下: 将

编程算法 - 数组中的逆序对 代码(C)

数组中的逆序对 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 在数组中的两个数字如果前面一个数字大于后面的数字, 则这两个数字组成一个逆序对. 输入一个数组, 求出这个数组中的逆序对的总数. 使用归并排序的方法, 辅助空间一个排序的数组, 依次比较前面较大的数字, 算出整体的逆序对数, 不用逐个比较. 时间复杂度: O(nlogn) 代码: /* * main.cpp * * Created on: 2014.6.12 * Author: