58-八大排序之——⑦归并排序

归并排序:

1.算法:将两段有序的数据合并为一段有序的数据,直到所有的数据有序

2.先一个一个有序,然后两个两个有序,再四个四个有序,直到所有的数据有序

3.一次归并的时间复杂度为O(n),遍历了一遍arr数组,一共要遍历logn次数组,所以时间复杂度O(nlogn),空间复杂度O(n),稳定(没有跳跃式的交换)

//打印
void Show(int* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
//一次归并排序
static void Merge(int *arr,int len,int gap)
{
	int low1 = 0;//第一个归并段的起始下标
	int high1 = low1+gap-1;//第一个归并段的结束下标
	int low2 = high1+1;//第二个归并段的起始下标
	int high2 = low2+gap-1<len?low2+gap-1:len-1;//第二个归并段的结束下标   如果high2的下标可能越界
	int i = 0;//brr的下标
	//创建和arr长度相等的动态内存
	int *brr = (int *)malloc(sizeof(int) * len);
	assert(brr != NULL);
	//有两个归并段
	while(low2<len)//low2<len才能说明有两个归并段
	{
		//两个归并段都还有数据
		while(low1<=high1 && low2<=high2)//总是比较len1和len2,把小的保存在brr中
		{
			if(arr[low1]<=arr[low2])//low1小,把low1保存在
			{
				brr[i++] = arr[low1++];
			}
			else//low2小
			{
				brr[i++] = arr[low2++];
			}
		}
		//如果一个归并段的数据已经完成了,另一个归并段还有数据,就把数据全部依次放到brr中,这两个while只会运行一个
		while(low1<=high1)
		{
			brr[i++] = arr[low1++];
		}
		while(low2<=high2)
		{
			brr[i++] = arr[low2++];
		}
		//下两个归并段,这时的high2就是第二个归并段的low1
		low1 = high2+1;
		high1 = low1+gap-1;
		low2 = high1+1;
		high2 = low2+gap-1<len?low2+gap-1:len-1;
	}
	//只有一个归并段,直接保存,一个归并段本身就是有序的
	while(low1<len)//不能用high1,因为high1可能越界
	{
		brr[i++] = arr[low1++];
	}
	//将归并好的数据拷贝到arr中
	for(int i = 0;i<len;i++)
	{
		arr[i] = brr[i];
	}
	free(brr);
}
//时间复杂度为O(nlogn)
void MergeSort(int *arr,int len)
{
	for(int i = 1;i<len;i*=2)//O(logn)
	{
		Merge(arr,len,i);
	}
}
int main()
{
	int arr[] = { 56,12,78,34,23,100,45,67,89,90 };
	int n = sizeof(arr) / sizeof(arr[0]);

	MergeSort(arr, n);
	Show(arr, n);

	return 0;
}

非递归写法

void Print_Ar(int* br, int n)
{
	if (br == NULL || n < 1)	return;
	for (int i = 0; i < n; ++i)
	{
		cout << br[i] << " ";
	}
	cout << endl;
}
void Merge(int* src, int* dest, int left, int m, int right)
{
	int i = left;
	int j = m + 1;
	int k = left;
	while (i <= m && j <= right)//两个块都还有数据
	{
		dest[k++] = src[i] <= src[j] ? src[i++] : src[j++];
	}
	while (i <= m)//第一个块可能还省有数据
	{
		dest[k++] = src[i++];
	}
	while (j <= right)//第二个块可能还省有数据
	{
		dest[k++] = src[j++];
	}
}
void MergePass(int* src, int* dest, int n, int s)
{
	int i = 0;
	for (; i + 2 * s - 1 < n; i = i + 2 * s)//两个块都有数据
	{
		Merge(src, dest, i, i + s - 1, i + 2 * s - 1);
	}
	if ( i + s - 1< n - 1 )//一个块有数据
	{
		Merge(src, dest, i, i + s - 1, n - 1);
	}
	else//不够一个块的拷贝下来,肯定也是有序的
	{
		for (int j = i; j < n; ++j)
		{
			dest[j] = src[j];
		}
	}
}
void MergeSort(int* br, int n)
{
	if (br == NULL || n < 2)return;
	int* tmp = new int[n];
	int s = 1;//每个块中有几个数据
	while (s < n)
	{	//br和tmp两个地方来回倒
		MergePass(br, tmp, n, s);
		s += s;
		MergePass(tmp, br, n, s);
		s += s;
	}
	delete[]tmp;
}
int main()
{
	int ar[] = { 56,12,78,95,34,23,100,45,67,89 };
	int n = sizeof(ar) / sizeof(ar[0]);
	Print_Ar(ar, n);
	MergeSort(ar, n);
	Print_Ar(ar, n);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值