#include <iostream>
#include <vector>
using namespace std;
// 归并排序
void mergeSort(vector<int>& nums, int left, int right) {
if (left >= right) return;
int mid = left + (right - left) / 2;
// 分治递归
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
// 归并合并
vector<int> temp(right - left + 1);
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) {
if (nums[i] <= nums[j])
temp[k++] = nums[i++];
else
temp[k++] = nums[j++];
}
while (i <= mid)
temp[k++] = nums[i++];
while (j <= right)
temp[k++] = nums[j++];
for (int p = 0; p < k; ++p)
nums[left + p] = temp[p];
}
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; ++i)
cin >> nums[i];
// 调用归并排序函数
mergeSort(nums, 0, n - 1);
// 输出排序后的数列
for (int i = 0; i < n; ++i)
cout << nums[i] << " ";
return 0;
}
- 定义了归并排序函数
mergeSort
,其中参数nums
是待排序的数列,left
和right
表示当前排序范围的左右边界。 - 在
mergeSort
函数中,首先进行递归的终止条件判断,如果left >= right
,表示当前排序范围只有一个元素或为空,直接返回。 - 计算当前排序范围的中间位置
mid
,使用分治法将数列分成两部分,分别对左半部分和右半部分调用mergeSort
函数进行递归排序。 - 创建临时数组
temp
,大小为当前排序范围的长度,用于存储合并操作的结果。 - 使用双指针的方式进行归并合并操作,初始化指针
i
和j
分别指向左半部分和右半部分的起始位置,指针k
初始为 0,表示临时数组的当前位置。 - 在 while 循环中,比较
nums[i]
和nums[j]
的大小,将较小的元素放入临时数组temp
中,并将相应指针向后移动。 - 如果某一部分还有剩余元素,将剩余元素依次放入临时数组
temp
中。 - 将临时数组
temp
中的元素复制回原始数列nums
的相应位置,完成合并操作。 - 返回到上一层递归。
- 在主函数中,首先读取输入的数列大小和数列元素。然后调用归并排序函数
mergeSort
对数列进行排序。 - 最后,按顺序输出排序后的数列元素。
归并排序的核心思想是分治法,通过不断将数列分成较小的子序列,然后再将这些子序列按顺序合并成一个有序的数列。在每一层的合并操作中,通过双指针的方式比较和合并两个有序的子序列。整个排序过程是递归进行的,直到每个子序列只包含一个元素或为空时结束。归并排序具有稳定性和时间复杂度为 O(nlogn) 的特点,适用于处理大规模数据的排序任务。
Q:如何合并的呢?
A:当进行归并合并操作时,我们有两个已排序的子序列,分别位于左半部分和右半部分。我们需要将这两个子序列按照从小到大的顺序合并成一个有序的序列。
- 创建一个临时数组
temp
,其大小为当前排序范围的长度。用于存储合并操作的结果。 - 初始化三个指针:
i
指向左半部分的起始位置,j
指向右半部分的起始位置,k
初始为 0,表示临时数组的当前位置。 - 在一个
while
循环中,比较nums[i]
和nums[j]
的大小,并将较小的元素放入临时数组temp
中。然后将相应的指针向后移动。 - 当其中一个指针到达了子序列的末尾时,表示该子序列的所有元素已经合并完毕。此时,我们将另一个子序列中剩余的元素依次放入临时数组
temp
的剩余位置。 - 最后,将临时数组
temp
中的元素复制回原始数列nums
的相应位置,完成合并操作。 - 返回到上一层递归。
通过不断地将子序列进行合并操作,最终实现了整个数列的排序。每次合并操作都能保证将两个有序子序列合并成一个有序序列,直到最后完成整个数列的排序。
归并排序的合并操作是稳定的,即在比较两个元素相等时,我们会优先选择左半部分的元素,这样可以保持相等元素的相对顺序不变。