ccxk
2024-09-09
点 赞
0
热 度
30
评 论
0

逆序对

文章摘要

智阅GPT

逆序对是指在一个数列中,任意两个数满足前面的数大于后面的数,这两个数就构成了一个逆序对。计算逆序对的目的是衡量一个数列“乱序”的程度。

逆序对的定义:

对于一个序列 ​ a_1, a_2, ..., a_n ,如果存在 ​ i < j ,并且 ​ a_i > a_j ,那么 ​ (a_i, a_j) 就称为一个逆序对。

例如:
对于数组 ​[3, 2, 1],共有 3 对逆序对,分别是:

  • ​ (3, 2)
  • ​ (3, 1)
  • ​ (2, 1)

逆序对可以用于排序相关的算法,例如在判断数组是否接近有序、通过逆序对数衡量乱序程度等。

如何高效计算逆序对?

简单的暴力算法是通过两层循环直接枚举每一对数,复杂度为 ​ O(n^2) 。但对于大规模数据,这种方法效率较低。为了提高效率,可以利用“归并排序”的思想来计算逆序对,时间复杂度可以降到 ​ O(n \log n)

归并排序计算逆序对的思想:

在归并排序中,我们将数组划分为两部分,分别对两部分计算逆序对。当我们合并两个有序数组时,通过比较左右两部分的元素,我们可以同时计算出两部分中的逆序对。

步骤:

  1. 使用归并排序将数组分成两部分。
  2. 分别递归计算左边和右边部分的逆序对。
  3. 合并时计算跨越左右两部分的逆序对。

代码实现:

这里是使用归并排序的方式来计算逆序对的C++代码:

#include <iostream>
using namespace std;

const int N = 100010; // 定义数组最大大小
int temp[N]; // 临时数组用于归并
int arr[N];  // 原数组

// 归并排序,并在排序过程中计算逆序对的个数
long long merge_sort(int arr[], int l, int r) {
    if (l >= r) return 0; // 递归出口:当数组只有一个元素时,逆序对为0

    int mid = (l + r) / 2;
    long long cnt = 0;
  
    // 递归计算左右两部分的逆序对数
    cnt += merge_sort(arr, l, mid);
    cnt += merge_sort(arr, mid + 1, r);

    // 合并两个有序数组,并计算逆序对
    int i = l, j = mid + 1, k = 0;
  
    while (i <= mid && j <= r) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
            // 逆序对数:因为 arr[i] > arr[j],所以 [i, mid] 之间的所有元素都大于 arr[j]
            cnt += (mid - i + 1);
        }
    }

    // 将剩余元素放入临时数组
    while (i <= mid) temp[k++] = arr[i++];
    while (j <= r) temp[k++] = arr[j++];

    // 将排序后的数组复制回原数组
    for (i = l, k = 0; i <= r; ++i, ++k) {
        arr[i] = temp[k];
    }
  
    return cnt;
}

int main() {
    int n;
    cout << "输入数组长度: ";
    cin >> n;
  
    cout << "输入数组元素: ";
    for (int i = 0; i < n; ++i) {
        cin >> arr[i];
    }
  
    // 调用归并排序计算逆序对
    long long result = merge_sort(arr, 0, n - 1);
    cout << "逆序对个数: " << result << endl;

    return 0;
}

代码详解:

  1. merge_sort(arr, l, r) 函数是递归归并排序的主要函数,它返回的是在区间 [l, r] 中的逆序对数量。
  2. 递归处理左右两部分后,在合并时,如果发现左边部分的某个元素大于右边部分的元素,说明形成了逆序对,且这些逆序对的数量等于左边部分剩余未处理元素的个数。
  3. 通过比较并合并两个子数组,我们能够高效地计算出跨越左右两部分的逆序对。

时间复杂度:

归并排序的时间复杂度是 ​ O(n \log n) ,因为它将数组划分为两部分并递归处理,每次合并的时间复杂度为 ​ O(n) 。在此过程中,计算逆序对的额外复杂度与归并操作相同,因此总复杂度是 ​ O(n \log n)

总结:

通过归并排序计算逆序对是一种非常经典且高效的算法。利用“分而治之”的思想,在排序的过程中同时计算逆序对,时间复杂度为 ​ O(n \log n) ,适合处理大规模的数组。


用键盘敲击出的不只是字符,更是一段段生活的剪影、一个个心底的梦想。希望我的文字能像一束光,在您阅读的瞬间,照亮某个角落,带来一丝温暖与共鸣。

ccxk

站长

不具版权性
不具时效性

文章内容不具时效性。若文章内容有错误之处,请您批评指正。


目录

欢迎来到ccxk的站点,为您导航全站动态

26 文章数
5 分类数
6 评论数
15标签数
最近评论
ccxk

ccxk


hi

ccxk

ccxk


Hi

Camelaaa_

Camelaaa_


热门文章

访问统计