用于计算数组中的倒数的 C++ 程序 - 集 1(使用合并排序)
数组的反转计数表示 - 数组距离排序多远(或接近)。如果数组已经排序,则反转计数为 0,但如果数组以相反的顺序排序,则反转计数为最大值。
形式上来说,如果 a[i] > a[j] 并且 i < j 两个元素 a[i] 和 a[j] 形成一个反转
例子:
Input: arr[] = {8, 4, 2, 1}
Output: 6
Explanation: Given array has six inversions:
(8, 4), (4, 2), (8, 2), (8, 1), (4, 1), (2, 1).
Input: arr[] = {3, 1, 2}
Output: 2
Explanation: Given array has two inversions:
(3, 1), (3, 2)
方法1(简单):
方法:遍历数组,对于每个索引,找到数组右侧的较小元素的数量。这可以使用嵌套循环来完成。将数组中所有索引的计数相加并打印总和。
算法:
- 从头到尾遍历数组
- 对于每个元素,使用另一个循环找到小于当前数字的元素的计数,直到该索引。
- 总结每个索引的反转计数。
- 打印反转计数。
执行:
C++
// C++ program to Count Inversions
// in an array
#include
using namespace std;
int getInvCount(int arr[], int n)
{
int inv_count = 0;
for (int i = 0; i < n - 1; i++)
for (int j = i + 1; j < n; j++)
if (arr[i] > arr[j])
inv_count++;
return inv_count;
}
// Driver Code
int main()
{
int arr[] = {1, 20, 6, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
cout << " Number of inversions are " <<
getInvCount(arr, n);
return 0;
}
// This code is contributed by Akanksha Rai
C++
// C++ program to Count Inversions in
// an array using Merge Sort
#include
using namespace std;
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[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 is index for left subarray
i = left;
// j is index for right subarray
j = mid;
// k is index for resultant merged
// subarray
k = left;
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 code
int main()
{
int arr[] = {1, 20, 6, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
int ans = mergeSort(arr, n);
cout << " Number of inversions are " << ans;
return 0;
}
// This is code is contributed by rathbhupendra
输出:
Number of inversions are 5
复杂性分析:
- 时间复杂度: O(n^2),从头到尾遍历数组需要两个嵌套循环,所以时间复杂度是O(n^2)
- 空间复杂度: O(1),不需要额外的空间。
方法2(增强合并排序):
方法:
假设数组左半边和右半边的反转次数(设为inv1和inv2); Inv1 + Inv2 中没有考虑哪些类型的反转?答案是——在合并步骤中需要计算的反转。因此,要获得需要添加的反转总数是左子数组、右子数组和merge()中的反转数。
如何获得合并()中的反转次数?
在合并过程中,让 i 用于索引左子数组, j 用于索引右子数组。在 merge() 的任何步骤中,如果 a[i] 大于 a[j],则存在 (mid – i) 反转。因为左右子数组是排序的,所以左子数组中的所有剩余元素 (a[i+1], a[i+2] ... a[mid]) 将大于 a[j]
完整的图片:
算法:
- 这个想法类似于归并排序,在每个步骤中将数组分成相等或几乎相等的两半,直到达到基本情况。
- 创建一个函数merge,当数组的两半合并时计算反转次数,创建两个索引i和j,i是前半部分的索引,j是后半部分的索引。如果 a[i] 大于 a[j],则存在 (mid – i) 反转。因为左右子数组是排序的,所以左子数组中的所有剩余元素 (a[i+1], a[i+2] ... a[mid]) 将大于 a[j]。
- 创建一个递归函数,将数组分成两半,并通过将前半部分的反转次数、后半部分的反转次数和合并两者的反转次数相加来找到答案。
- 递归的基本情况是给定的一半中只有一个元素。
- 打印答案
执行:
C++
// C++ program to Count Inversions in
// an array using Merge Sort
#include
using namespace std;
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[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 is index for left subarray
i = left;
// j is index for right subarray
j = mid;
// k is index for resultant merged
// subarray
k = left;
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 code
int main()
{
int arr[] = {1, 20, 6, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
int ans = mergeSort(arr, n);
cout << " Number of inversions are " << ans;
return 0;
}
// This is code is contributed by rathbhupendra
输出:
Number of inversions are 5
复杂性分析:
- 时间复杂度: O(n log n),使用的算法是分治法,所以每一层都需要遍历一次全数组,并且有log n层,所以时间复杂度是O(n log n)。
- 空间复杂度: O(n),临时数组。
请注意,上面的代码修改(或排序)输入数组。如果我们只想计算反转,我们需要创建原始数组的副本并在副本上调用 mergeSort() 以保留原始数组的顺序。