算法之逆序对

算法之逆序对

逆序对问题

? 假设A[1..n]是一个有n个不同数的数组。若iA[j],则对偶(i, j)称为A的一个逆序对(inversion)。

  1. 列出数组{2, 3, 8, 6, 1}的5个逆序对
  2. 由集合{1, 2, ..., n}中的元素构成的什么数组具有最多的逆序对?它有多少逆序对?
  3. 插入排序的运行时间与输入数组中的逆序对的数量有什么关系?
  4. 给出一个求在n个元素的任何排列中逆序对数量的算法,最坏时间复杂度为: \(\Theta\)(nlgn)
  1. 根据定义易得,逆序对为:(2, 1)、(3, 1)、(8, 6)、(8, 1)、(6, 1)
  2. 当数组元素恰好为n个元素从大到小排列时,拥有最多的逆序对。此时逆序对为: (n - 1) + (n -2) + (n - 3) + ... + 1 = n(n - 1) / 2
  3. 根据插入排序的实现过程,不难得出每次从未排序数组选择一个值arr[j]插入已排序数组的时候,所需要的移动次数,即为以arr[j]为右侧值的逆序对的个数。这个特性也可以设计出一个时间复杂度为: \(\Theta\)(\(n^2\))的算法。当然这种指数级别复杂度的算法我们直接PASS
  4. 不难想到\(\Theta\)(nlgn)算法复杂度的归并排序。其实归并排序在分治的时候不会改变逆序对的个数。只有在合并的时候,才会因为逆序对的出现导致右侧提前被合入原数组。其实修改点主要在两个方面:
  • 声明一个全局变量用来存储总的次数
  • 在右侧提前被合入原数组的时候对总次数进行累加(只需要改归并排序的merge方法, 归并排序请参照http://www.cnblogs.com/Kidezyq/p/8379267.html

具体源代码如下:

private static int count;

private static void merge(int[] arr, int startIndex, int midIndex, int endIndex) {
    /**
     * 合并策略:
     * 1. 新建两个数组,分别存取左半部分排好序的数组和右半部分排好序的数组
     * 2. 分别从左右两个数组最开始下标开始遍历,选取较小的依次放入原数组对应位置
     * 3. 最终如果左右数组中有一个已经遍历完成,另一个数组所剩的元素直接放入元素组后面部分即可
     */
    // STEP1
    int[] leftArr = new int[midIndex - startIndex];
    int[] rightArr = new int[endIndex - midIndex];
    System.arraycopy(arr, startIndex, leftArr, 0, leftArr.length);
    System.arraycopy(arr, midIndex, rightArr, 0, rightArr.length);

    // STEP2
    int k = startIndex; // 存储原数组中的下标
    int i = 0;          // 存储左边数组的下标
    int j = 0;          // 存储右边数组的下标
    while (i < leftArr.length && j < rightArr.length) {
        // 将较小的元素复制到元素组对应下标k,并且移动较小元素所在数组下标
        if (leftArr[i] < rightArr[j]) {
            arr[k++] = leftArr[i++];
        } else {
            // 此时说明 arr[i]到arr[leftArr.length - 1]的值都比arr[j]大,即此时以arr[j]结尾的逆序对个数为:
            count += leftArr.length - i;
            arr[k++] = rightArr[j++];
        }
    }

    // STEP3
    if (i < leftArr.length) {
        System.arraycopy(leftArr, i, arr, k, leftArr.length - i);
    } else if (j <= rightArr.length) {
        System.arraycopy(rightArr, j, arr, k, rightArr.length - j);
    }
}

原文地址:https://www.cnblogs.com/Kidezyq/p/8379332.html

时间: 2024-10-10 12:16:50

算法之逆序对的相关文章

算法之逆序对问题求解

题目:   给出一列数,a1, a2,....到 an,求它的逆序对数.逆序对就是 下标 i 比 j小,但是值 ai 却比 a j大.n 可以高大 10万. 思路: (1)穷举,肯定不现实的,因为n 可以高达10万,肯定超时: (2)考录分治法,受到归并排序的启发,能不能不断划分数组到最小,然后合并时统计 逆序对数.划分和递归都和归并排序没什么区别,问题在合并时如何统计. 合并左右两个数组时,左边 的数组的下标肯定要比右边数组的下标小,那么如果右边数组有比左边数组小的值,比如 [ 5,6] 和

笔试算法题(32):归并算法求逆序对 &amp; 将数组元素转换为数组中剩下的其他元素的乘积

出题:多人按照从低到高排成一个前后队列,如果前面的人比后面的高就认为是一个错误对: 例如:[176,178,180,170,171]中的错误对 为 <176,170>, <176,171>, <178,170>, <178,171>, < 180,170>, <180,171>. 现在要求从一个整数序列中找出所有这样的错误对: 分析: 逆序对(Inversion Pair):在N个可判断大小的数中,逆序对的数量为[0,n(n-1)/2

第二章 算法基础 思考题2-4(逆序对)

1 package chap02; 2 3 import static org.junit.Assert.*; 4 5 import java.util.Arrays; 6 7 import org.junit.Test; 8 9 public class ques2_4 { 10 /** 11 * 逆序对,将一个序列中的所有逆序对打印输出 12 * 13 * @author xiaojintao 14 * 15 */ 16 static void printReverseOrder(int[]

Day2:T4求逆序对(树状数组+归并排序)

T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*(); B[J]>=B[I] 也就是求逆序对: 求逆序对的方法主要有两种: 归并排序: 树状数组: 这里两种方法都学习一下: 1.之前对于树状数组的印象就只有单点修改和区间求和 一直觉得lowbit是一个神奇的东西(至今没有搞懂原理) 上网搜了一下用树状数组求逆序对的方法,发现有一个大神写的很棒....看

HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少是多少? 因为每次相邻的交换操作最多只能减少一个逆序对,所以最多可以减少k个逆序对,所以我们只要求出原来的序列有多少个逆序对然后减去k再跟0取较大的就可以了. 因为数据范围是10的五次方,所以暴力求肯定会TLE,所以要用n*logn算法求逆序对,n*logn算法有几种可以求逆序对的: 线段树,树状数

利用merge sort寻找逆序对

算法导论第二章 练习题,使用合并排序算法寻找逆序对 基本思想: 在merge过程中,交换位置与一组逆序对是一一对应的. 在左右两个子数组内部是排好序的,所以逆序对的出现仅仅存在于“左数组中的数组大有右数组中的数字”的情况. 所以在每次的merge过程中就可以进行逆序对的计数. java代码实现: 1 public class InversionCount { 2 public static void main(String[] args){ 3 int[] arr = new int[]{2,3

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

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

hdu 5273 Dylans loves sequence(区间逆序对数-莫队算法)

n<=1000,q<=100000,求区间内逆序对数,从[l,r]显然可以log(n)的时间内移动到[l-1,r],[l+1,r],[l,r-1],[l,r+1],那么就可以用莫队进行离线 复杂度大概是O(n*sqrt(n)*log2(n)),不过可以暴力枚举起点,然后向后统计,然后O(1)回答,不过n再大就无法解决了,这个即使是n<=1e5也可以很快得到答案,开-o优化,1e6也可以很快得到答案 #include<bits/stdc++.h> using namespace

【算法32】计算数组中的逆序对

问题描述 设 A[1...n] 是一个数组,如果对于 i < j 有 A[i] > A[j], 则 A[i] 和 A[j] 构成一对逆序.给定一个数组,计算数组中逆序对的个数.例如数组 a[] = {1, 4, 3, 2}, 则 {4, 3} {4, 2} {3, 2}是逆序对,返回 3. 解法一:暴力求解 两个 for 循环枚举所有的数对,如果是逆序对,则 count++,最终返回 count 即可.时间复杂度 O(n^2),代码如下: 1 #include <iostream>