归并排序:一种排序方式,时间复杂度为O(nlog(n));
归:递归
并:合并
如下题:
有举重运动员,每一名举重运动员比赛前需要上报三个预举的重量,从小到大排序;而举办方需要根据上报的所有重量进行从小到达排序从而确定入场顺序。
例:150 156 160 152 155 161 156 162 165 155 157 162
一个颜色代表一个运动员上报的重量;
需要获得结果: 150 152 155 155 156 156 157 160 161 162 162 165
第一种方法:选择排序法
每循环一次,找到其中的最小值将其放在最前面;
简单分析即可知:时间复杂度为:O(n^2);------所需时间太长,尤其在n较大时;
第二种方法:归并排序法
可以观察上述数字,每一种颜色的数字都是原本就排好序的;那么我们可以这样做:
如150 156 160 152 155 161
第一步:150与152比较 150小,存放在数组中,指向左侧数组的指针向后移动
第二步:156与152比较 152小,指向右侧数组的指针向后移动
第三步:156与155比较 155小,右侧指针右移
第四步:156与161比较 156小,左侧指针右移
第五步:160与161比较 160小,指针无法右移,结束;
但是此时,右侧还有数161未加入数组中,因此在两个数组之间进行排序之后再将未加入的数加入数组中;
那么常规来说,如果是完完全全没有规律的一组数呢?
是否也可以使用归并排序来解决?
很明显,上一题中只有合并,而没有“归”;
如:1 5 3 4 2 6
如果我们将每一个数字都看成一个整体,是否每个整体又都是有序的了?
那么此时,我们
分开,一直到分成单个,然后再结合上题,两个两个进行比较排序并合并;
//拆分代码
void split(int l,int r) {
if (l >= r)return;
int mid = (l + r) / 2;
split(l, mid);
split(mid + 1, r);
//合并
merge(l, mid, r);
}
//比较并排序代码
void merge(int l,int mid,int r) {
int i = l;
int j = mid + 1;
int k = l;
while (i <= mid && j <= r) {
if (a[i] < a[j]) {
b[k++] = a[i++];
}
else b[k++] = a[j++];
}
//将未能比较的数加入数组中
while (i <= mid) {
b[k++] = a[i++];
}
while (j <= r) {
b[k++] = a[j++];
}
for (int i = l; i <= r; i++) { //需要额外加入一个数组来存储转换,如果直接在原数组上进行操作,极其容易造成错误;
a[i] = b[i];
}
}
总代码:
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define dp 1100
int a[dp], b[dp];//此行与上一行在此程序中与 int a[1100],b[1100]; 等价
int n;
//比较并排序代码
void merge(int l, int mid, int r) {
int i = l;
int j = mid + 1;
int k = l;
while (i <= mid && j <= r) {
if (a[i] < a[j]) {
b[k++] = a[i++];
}
else b[k++] = a[j++];
}
//将未能比较的数加入数组中
while (i <= mid) {
b[k++] = a[i++];
}
while (j <= r) {
b[k++] = a[j++];
}
for (int i = l; i <= r; i++) { //需要额外加入一个数组来存储转换,如果直接在原数组上进行操作,极其容易造成错误;
a[i] = b[i];
}
}
void split(int l,int r) {
if (l >= r)return;
int mid = (l + r) / 2;
split(l, mid);
split(mid + 1, r);
//合并
merge(l, mid, r);
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
split(0, n - 1);
for (int i = 0; i < n; i++) {
cout << a[i] << " ";
}
return 0;
}