【问题标题】:Construct count array in better than O(n^2) time complexity以优于 O(n^2) 的时间复杂度构造计数数组
【发布时间】:2026-02-16 00:10:01
【问题描述】:

给定一个大小为n的数组A[],构造两个数组C_min[]C_max[]使得
C_min[i]表示A[0 to i]
中小于A[i]的元素数C_max[i] 表示A[i to n-1] 中大于A[i] 的元素个数

例如A[5] = {1,2,4,3,6} 那么C_min[]C_max[] 将是
C_min[5] = {0,1,2,2,4}
C_max[5] = {4,3,1,1,0}

我想不出比 O(n^2) 更好的算法,但 this post 促使我想出一些更好的方法来做到这一点,但我无法应用类似的逻辑(即帖子中提到)在这里。

在给定的帖子中,给出的问题是在数组中找到没有反转的。如果 array[i] > array[j] 并且 j>i 那么它形成一个反转。例如序列 2, 4, 1, 3, 5 有三个反转 (2, 1), (4, 1), (4, 3)。
用于解决这个问题的想法是归并排序算法。

在合并过程中,让i用于索引左子数组(L[])和j 对于右子数组(R[])。在 merge() 的任何一步,如果 L[i] 更大 比 R[j],则有 (mid – i+1) 个反转,其中 mid 是 中间索引传递给归并排序的归并函数。因为离开了 和右子数组是排序的,所以所有剩余的元素 左子数组 (L[i+1], L[i+2] ... L[mid]) 将大于 R[j]

这个逻辑的代码如下:

#include <bits/stdc++.h>

int  _mergeSort(int arr[], int temp[], int left, int right);
int merge(int arr[], int temp[], int left, int mid, int right);

/* This function sorts the input array and returns the
   number of inversions in the array */
int mergeSort(int arr[], int array_size)
{
    int *temp = (int *)malloc(sizeof(int)*array_size);
    return _mergeSort(arr, temp, 0, array_size - 1);
}

/* An auxiliary recursive function that sorts the input array and
  returns the number of inversions in the array. */
int _mergeSort(int arr[], int temp[], int left, int right)
{
  int mid, inv_count = 0;
  if (right > left)
  {
    /* Divide the array into two parts and call _mergeSortAndCountInv()
       for each of the parts */
    mid = (right + left)/2;

    /* Inversion count will be sum of inversions in left-part, right-part
      and number of inversions in merging */
    inv_count  = _mergeSort(arr, temp, left, mid);
    inv_count += _mergeSort(arr, temp, mid+1, right);

    /*Merge the two parts*/
    inv_count += merge(arr, temp, left, mid+1, right);
  }
  return inv_count;
}

/* This funt merges two sorted arrays and returns inversion count in
   the arrays.*/
int merge(int arr[], int temp[], int left, int mid, int right)
{
  int i, j, k;
  int inv_count = 0;

  i = left; /* i is index for left subarray*/
  j = mid;  /* i is index for right subarray*/
  k = left; /* i is index for resultant merged subarray*/
  while ((i <= mid - 1) && (j <= right))
  {
    if (arr[i] <= arr[j])
    {
      temp[k++] = arr[i++];
    }
    else
    {
      temp[k++] = arr[j++];

     /*this is tricky -- see above explanation/diagram for merge()*/
      inv_count = inv_count + (mid - i);
    }
  }

  /* Copy the remaining elements of left subarray
   (if there are any) to temp*/
  while (i <= mid - 1)
    temp[k++] = arr[i++];

  /* Copy the remaining elements of right subarray
   (if there are any) to temp*/
  while (j <= right)
    temp[k++] = arr[j++];

  /*Copy back the merged elements to original array*/
  for (i=left; i <= right; i++)
    arr[i] = temp[i];

  return inv_count;
}

/* Driver progra to test above functions */
int main(int argv, char** args)
{
  int arr[] = {1, 20, 6, 4, 5};
  printf(" Number of inversions are %d \n", mergeSort(arr, 5));
  getchar();
  return 0;
}

那么这个计数数组问题是否可以在类似的行上完成。

是否有可能在比 O(n^2)-time 更好的时间内构造计数数组?

【问题讨论】:

  • 为什么无缘无故投反对票??我认为有些人在否决问题而不是建议解决方案或否决理由时感到勇敢:D
  • NMDV,发布相关信息是站外链接,不被接受。最好在这里发布辱骂数据 - 你尝试了什么。
  • @AMomchilov 您所说的先前值是指什么,先前的意思是访问的最后一个数组索引或仅小于当前元素的最后一个值?
  • @chux 我尝试的是一种简单的蛮力方法,我认为不需要解释。它只有两个用于跟踪 cout 的 for 循环。在给定的链接中,问题是找到反转计数的数量,并且使用归并排序算法在 O(nlogn) 时间内找到它。
  • 对数组进行排序需要花费O(n*ln(n)),所以从排序列表开始。那时似乎微不足道。只需遍历数组

标签: c arrays algorithm sorting


【解决方案1】:

假设您保留了一个数组 S,其中 S[0..x] 是 A[0..x] 的排序版本。然后在您已经计算 C_min[0..x] 时计算 C_min[x+1] 相当于将 A[x+1] 插入 S (一个 O(log n) 操作)并将 A[x+1] 定位在S(在最坏的情况下,另一个 O(log n) 操作)。这将使计算所有 C_min O(n log n)。计算 C_max 会类似,但需要它自己的 S 版本,使得计算 C_min 和 C_max O(n log n)。

【讨论】:

  • 有道理。将尝试实施它。 :)
  • 哪种排序算法适合这种增量方法,插入排序可以轻松做到,但如何在nlogn中做到
  • @DeepankarSingh:你必须自己做一些的工作:)
  • 在第一印象中,我认为它在 O(nlogn) 中不可行,这就是我问的原因。在您的算法中,您每次都在考虑一个新元素并保持数组 S 排序,如果数组不是静态的,我们如何在 O(nlogn) 时间内保持 S 排序。
  • 即使我们在 O(logn) 时间内找到 A[x+1] 的正确位置,但在最坏的情况下,我们需要右移 S 中存在的所有元素。