目录
一、排序思想和时空复杂度
归并排序也是基于分治思想的,又称二路归并排序。
他的核心思想是:将两个或者两个以上的有序表合并成一个新的有序表。
假定待排序表含有n个元素,则可将每一个元素都视为一个有序表,每个子表的长度为1;
然后两两归并,得到n/2再取上界个长度为2或者1的有序表;
继续两两归并,直到合并成一个长度为n的有序表为止。
空间复杂度:
merge操作中,我们会借助一个辅助数组。这个数组的大小是n,
所以空间复杂度为O(N)级别。
时间复杂度:
首先,每一趟归并排序的时间复杂度是O(N),
从第一趟归并排序开始,我们会将两个元素比较大小,再排好序,一共进行n/2次,这是O(N)级别的;
第二趟归并排序,会将两个元素和两个元素再比较,排序,一共进行n/4次,也是O(N)级别的;
综上,每一趟排序的时间复杂度是O(N)。
那么一共会进行多少次归并排序呢?
如果是8个元素,那么会进行3次,分别是【2,2,2,2】,【4,4】,【8】。
如果是9个元素,那么会进行4次,分别是:【2,2,2,2,1】,【4,4,1】,【8,1】,【9】
所以,n个元素会进行log2N取上界次。
也就是子归并排序的次数为O(LOG2N)级别的。
那么总的时间复杂度为:O(NLOG2N)。
稳定性:
稳定。因为归并操作不会改变相同关键字记录的相对次序。
二、排序思路
整体是分治思想。
先分:将每个组中的元素由n变为1;
再治:依次将组两两合并,直到最后组的元素为n。
三、代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <iostream>
using namespace std;
int a[1000] = {0,8,7,4,9,3,5,10,6};
int temp[1000];
//mid指的是右边元素第一个角标,l,r为左边界和右边界
void merge(int l, int r, int mid)
{
//左子序列首元素角标
int i = l;
//右子序列首元素角标
int j = mid;
//临时的排序数组
int p = l;
//开始归并
while (i < mid && j <= r)
{
//打擂,如果左子序列更小,
//则先添加左子序列至临时数组,然后左子序列下标往后移一位
//同时临时数组角标后移一位
if (a[i] < a[j])
{
temp[p] = a[i];
p++;
i++;
}
//如果右子序列更小,同上
else
{
temp[p] = a[j];
p++;
j++;
}
}
//一定会剩一个序列没排完,则直接赋过去即可。
//因为传入的左子序列和右子序列分别有序。
while (i < mid)
{
temp[p] = a[i];
p++;
i++;
}
while (j <= r)
{
temp[p] = a[j];
p++;
j++;
}
//最后把临时数组里的值赋给原数组即可。
p = l;
i = l;
while (p <= r)
{
a[i] = temp[p];
p++;
i++;
}
}
void mergeSort(int l, int r)
{
//递归思想
if (l < r)
{
//先将序列平均分成左右两段
int mid = (l + r) / 2;
//排左边的子序列
mergeSort(l, mid);
//排右边的子序列
mergeSort(mid + 1, r);
//合并子序列
//mid+1指的是右边子序列的第一个角标
merge(l, r, mid + 1);
}
}
int main()
{
mergeSort(1, 8);
for (int i = 1; i <= 8; i++)
{
cout << a[i] << " ";
}
return 0;
}