算法 排序 【归并排序】

  • Post author:
  • Post category:其他



目录


一、排序思想和时空复杂度


空间复杂度:


时间复杂度:


稳定性:


二、排序思路


三、代码


一、排序思想和时空复杂度

归并排序也是基于分治思想的,又称二路归并排序。

他的核心思想是:

将两个或者两个以上的有序表合并成一个新的有序表。

假定待排序表含有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;
}