目录
前言
排序是重要又较为简单的算法之一,个人认为学习算法建议先学习排序,排序有十种重要的排序方法,有些好,有些坏。
十大排序算法包括哪些
十大排序算法包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序、基数排序。
今天我来讲讲归并排序。
归并排序原理
归并排序的核心在于将一个大的数组不断分成两半,递归地对两半进行排序,最后将排序好的两半合并在一起。具体步骤如下:
- 分解:将待排序的序列划分为两个子序列,直到子序列的长度为1(此时认为子序列是有序的)。
- 递归排序并合并:递归地对子序列进行归并排序,并将已排序的子序列合并成一个大的有序序列,直到合并为1个完整的序列。
归并排序基本信息
英文名 | merge sort |
时间复杂度 | O(n log n) |
空间复杂度 | O(n) |
是否稳定 | 是 |
代码实现
代码思路
归并排序的代码思路可以分为递归实现和非递归实现两种方式。
递归实现
归并排序的递归实现过程如下:
- 分解:将数组从中间分成两个子数组,递归地对这两个子数组进行排序。
- 合并:将排好序的两个子数组合并成一个有序数组。
- 终止条件:当子数组只有一个元素时,认为该子数组已经有序,递归结束。
代码实现
/*头文件部分*/
#include <iostream>
#include <vector>
/*归并排序部分*/
void merge(std::vector<int>& vec, int left, int mid, int right) {
std::vector<int> temp(right - left + 1);
int i = left, j = mid + 1, k = 0;
while(i <= mid && j <= right)
temp[k++] = vec[i] <= vec[j] ? vec[i++] : vec[j++];
while(i <= mid)
temp[k++] = vec[i++];
while(j <= right)
temp[k++] = vec[j++];
for(k = 0, i = left; i <= right; ++i, ++k)
vec[i] = temp[k];
}
void mergeSort(std::vector<int>& vec, int left, int right) {
if(left < right) {
int mid = left + (right - left) / 2;
mergeSort(vec, left, mid);
mergeSort(vec, mid + 1, right);
merge(vec, left, mid, right);
}
}
/*主函数部分*/
int main() {
int n;
std::cin >> n;
std::vector<int> vec;
for (int i = 0; i < n; ++i) {
int num;
std::cin >> num;
vec.push_back(num);
}
mergeSort(vec, 0, vec.size() - 1);
int i = 1;
for(int num : vec) {
std::cout << "第" << i << "个数:" << num << std::endl;
i++;
}
return 0;
}
实例
【模板】排序
题目描述
将读入的 N 个数从小到大排序后输出。
输入格式
第一行为一个正整数 N。
第二行包含 N 个空格隔开的正整数 ai,为你需要进行排序的数。
输出格式
将给定的 N 个数从小到大输出,数之间空格隔开,行末换行且无空格。
样例1
样例输入1
5
4 2 4 5 1
样例输出1
1 2 4 4 5
本题来自洛谷题目
P1177 【模板】排序https://www.luogu.com.cn/problem/P1177https://www.luogu.com.cn/problem/P1177
代码
#include <iostream>
#include <vector>
void merge(std::vector<int>& vec, int left, int mid, int right) {
std::vector<int> temp(right - left + 1);
int i = left, j = mid + 1, k = 0;
while(i <= mid && j <= right)
temp[k++] = vec[i] <= vec[j] ? vec[i++] : vec[j++];
while(i <= mid)
temp[k++] = vec[i++];
while(j <= right)
temp[k++] = vec[j++];
for(k = 0, i = left; i <= right; ++i, ++k)
vec[i] = temp[k];
}
void mergeSort(std::vector<int>& vec, int left, int right) {
if(left < right) {
int mid = left + (right - left) / 2;
mergeSort(vec, left, mid);
mergeSort(vec, mid + 1, right);
merge(vec, left, mid, right);
}
}
int main() {
int n;
std::cin >> n;
std::vector<int> vec;
for (int i = 0; i < n; ++i) {
int num;
std::cin >> num;
vec.push_back(num);
}
mergeSort(vec, 0, vec.size() - 1);
for(int num : vec)
std::cout << num << " ";
std::cout << std::endl;
return 0;
}
总结
归并排序是一种高效的排序算法,基于分治策略,通过递归分解和合并操作实现排序。其核心思想是将一个数组分成若干个子数组,分别对每个子数组进行排序,然后将已排序的子数组合并成一个完整的排序数组。
基本原理
归并排序的基本步骤包括:
- 分解:将数组从中间分成两半,分别对左半部分和右半部分进行归并排序。
- 递归:递归地对每一半进行分解,直到每个子数组只有一个元素(自然是有序的)。
- 合并:将两个有序的子数组合并成一个有序的数组。
算法描述
归并排序的具体算法描述如下:
- 分解:计算中点
mid = (l + r) / 2
。 - 递归:递归调用
mergeSort(a, l, mid)
和mergeSort(a, mid + 1, r)
。 - 合并:将两个有序数组合并成一个有序数组,存储到
a[l:r]
。 - 调用:调用
mergeSort(a, 0, n-1)
即可得到整个数组的排序结果。
时间复杂度和空间复杂度
- 时间复杂度:分解步骤的时间复杂度为 O(logn),因为数组被反复分成两半;合并步骤的时间复杂度为 O(n),因为需要遍历两个子数组来合并它们。因此,总的时间复杂度为 O(nlogn)。
- 空间复杂度:合并步骤需要一个额外的数组来存储合并后的结果,所以空间复杂度为 O(n)。
你学费了吗?
下期预告:十大排序算法之计数排序。