先滑到底看代码 然后对照着流程看(代码是题解里的)
我们通过一个具体的例子来详细解释整个归并排序和逆序对统计的过程,包括递归的流程。我们使用数组 [5, 4, 2, 6, 3, 1],并假设要计算这个数组中的逆序对。
初始数组
我们开始的数组是:
[5, 4, 2, 6, 3, 1]
递归过程
归并排序的递归过程是通过将数组不断分割成两半来实现的。我们将整个数组 [5, 4, 2, 6, 3, 1] 分割如下:
第一次分割:
[5, 4, 2, 6, 3, 1]
├── [5, 4, 2] (l=0, r=2)
└── [6, 3, 1] (l=3, r=5)
第二次分割(左边的部分 [5, 4, 2]):
[5, 4, 2]
├── [5, 4] (l=0, r=1)
└── [2] (l=2, r=2)
第三次分割(左边的部分 [5, 4]):
[5, 4]
├── [5] (l=0, r=0)
└── [4] (l=1, r=1)
合并过程
在分割到每个子数组只包含一个元素后,我们开始合并这些子数组,同时统计逆序对。
合并 [5] 和 [4]
归并函数被调用,l=0, mid=0, r=1。
lptr 指向 5,rptr 指向 4。
比较 5 和 4,发现 5 > 4,所以 4 放入临时数组,同时增加逆序对计数,因为 5 在 4 的左边,所以形成逆序对。
最终得到临时数组 [4, 5],更新原数组为 [4, 5, 2, 6, 3, 1]。
合并 [4, 5] 和 [2]
继续合并,l=0, mid=1, r=2。
lptr 指向 4,rptr 指向 2。
4 > 2,所以 2 放入临时数组,同时逆序对计数增加 2(左边还有 4 和 5)。
接下来 4 和 5 继续放入临时数组。
得到 [2, 4, 5],更新原数组为 [2, 4, 5, 6, 3, 1]。
合并 [6, 3, 1]
接下来处理右边的部分 [6, 3, 1]:
分割:
[6, 3, 1]
├── [6, 3] (l=3, r=4)
└── [1] (l=5, r=5)
合并 [6] 和 [3]:
比较 6 和 3,6 > 3,逆序对计数增加 1(左边还有 6)。
最终得到 [3, 6],更新原数组为 [2, 4, 5, 3, 6, 1]。
合并 [3, 6] 和 [1]:
比较 3 和 1,逆序对计数增加 2(左边还有 3 和 6)。
得到 [1, 3, 6],更新原数组为 [2, 4, 5, 1, 3, 6]。
合并最终结果
最后,我们合并 [2, 4, 5] 和 [1, 3, 6]:
lptr 指向 2,rptr 指向 1,1 放入临时数组,逆序对计数增加 3(左边还有 2, 4, 5)。
接下来 2 和 1,逆序对计数增加 2(左边还有 4, 5)。
接下来 4 和 1,逆序对计数增加 1(左边还有 5)。
最后,得到 [1, 2, 3, 4, 5, 6],更新原数组。
统计逆序对数量
最终,我们统计到的逆序对数量是:
从 [5, 4] 中的 1
从 [4, 2] 中的 2
从 [6, 3] 中的 1
从 [6, 1] 中的 2
从 [3, 1] 中的 2
从 [5, 2] 中的 2
从 [4, 2] 中的 2
所以总的逆序对数量是 11。
总结
通过以上步骤,我们可以清楚地看到:
归并排序在分割时将数组不断划分为更小的子数组。
合并过程中,我们比较两个子数组的元素,并在发现逆序对时进行计数。
最终,我们获得了逆序对的总数,代码实现了这一逻辑。
C++ 代码
#include <iostream> // 引入输入输出库
#include <algorithm> // 引入算法库(但在此代码中未使用)
using namespace std;
const int N = 100010; // 定义数组的最大长度
int array[N]; // 定义一个整型数组来存储输入的数字
int nums; // 存储输入的数字个数
unsigned long result = 0; // 统计逆序对的数量,使用 unsigned long 以避免溢出
// 归并排序的函数,同时计算逆序对的数量
void merge_sort(int array[], int l, int r)
{
if (l >= r) return; // 如果左指针大于或等于右指针,表示区间无效,直接返回
int mid = (l + r) / 2; // 计算中间位置
merge_sort(array, l, mid); // 对左半部分进行递归排序
merge_sort(array, mid + 1, r); // 对右半部分进行递归排序
// 创建临时数组用于合并
int temp[r - l + 1];
int lptr = l; // 左指针初始化为左边界
int rptr = mid + 1; // 右指针初始化为右半部分的开始位置
int tempptr = 0; // 临时数组的指针,用于存储合并结果
// 合并两个已排序的子数组,并统计逆序对
while (lptr <= mid && rptr <= r)
{
if (array[lptr] <= array[rptr]) // 如果左指针指向的元素小于等于右指针指向的元素
{
temp[tempptr++] = array[lptr++]; // 将左指针的元素放入临时数组,左指针向右移动
}
else // 如果左指针指向的元素大于右指针指向的元素
{
temp[tempptr++] = array[rptr++]; // 将右指针的元素放入临时数组,右指针向右移动
result += (mid - lptr + 1); // 统计逆序对数量,左边剩下的元素都是逆序对
}
}
// 将剩余的左半部分元素放入临时数组
while (lptr <= mid)
{
temp[tempptr++] = array[lptr++]; // 将左指针的剩余元素放入临时数组
}
// 将剩余的右半部分元素放入临时数组
while (rptr <= r)
{
temp[tempptr++] = array[rptr++]; // 将右指针的剩余元素放入临时数组
}
// 将临时数组的内容复制回原数组
for (int i = l, j = 0; i <= r; i++, j++)
{
array[i] = temp[j]; // 将合并后的元素复制回原数组
}
}
int main() {
scanf("%d", &nums); // 输入数字个数
for (int i = 0; i < nums; i++)
{
scanf("%d", &array[i]); // 输入每个数字
}
merge_sort(array, 0, nums - 1); // 调用归并排序函数,排序并计算逆序对
cout << result; // 输出逆序对的数量
return 0; // 程序结束
}