排序算法:归并排序(非递归)

8b274fc479a64fd8980da5b189c7ddf3.png

以数组为例说明归并排序的思想:

归并排序又称为二路归并排序,其思想就是分治策略;每一个元素自身都是有序的,然后俩个结合为一组,组内进行排序,然后俩个组再结合为一组,组内排序。

 

从概念上讲,归并排序的工作原理如下:

1.将未排序的数组划分为n个子数组,每个子数组包含一个元素(一个元素的数组认为是有序的)。

2.反复合并子数组以产生新的有序子数组,直到只剩一个子数组。这将是归并排序。

 

先将数组中1个元素划分为一块:

b84873d5644a491bbe5edb977ab00384.png

l1:当前操作的第一个组的起始点  初始值l1=0;

h1:当前操作的第一个组的终止点  初始值h1=l1+gap-1  (gap是分组规模,也就是分为gap个组)

l2:当前操作的第二个组的起始点    初始值l2=h1+1   (l2永远在h1后边一个的位置)

h2:当前操作的第二个组的终止点   初始值 h2=l2+gap-1<len?l2+gap-1:len-1;(下面再解释)

第一次分组的时候,l1和h1都指向5,l2和h2都指向了9;然后将这四个指针所指向的俩块数组合并:

合并的时候要进行排序,先从里面各取一个最小值进行比较,也就是第一块子数组的 l1和第二块子数组的 l2,进行比较,较小的 l1 的值放入到我们已经申请好的堆内存当中,接着 l1 = l1+1;在进行比较 l1 和l2的值,较小的放入到堆内存当中,再向后走,一直到  l1大于终止点h1或者l2大于终止点 h2,就认为其中一个子数组的元素已经被取完了,再将另一个未取完的数组的值直接挪到堆内存当中即可。

b070e1ba356f44f1b1882ee037ae8a75.png

 前俩个元素处理完,这四个指针就接着向后跑,

        l1 = h2 + 1;
        h1 = l1 + gap - 1;
        l2 = h1 + 1;
        h2 = l2 + gap - 1 < len ? l2 + gap - 1 : len - 1;

25949021eceb46e2af3d990a12c83a3a.png

继续执行上述操作,一直到将数组遍历完,也就是当l1>=数组长度len的时候,认为数组遍历完了。 

数组遍历完后得到的新数组是这样:b09064bd30484e37bf395e5c7e77e3a7.png

 之前我们是一个元素为一组,俩俩合并,现在由于合并的元素都是有序的,进行俩个元素为一组;俩俩合并:

a3375e1be5564270a7b03f11e712a93b.png 

 继续分别取l1  和  l2的元素进行比较,小的(l2)放入申请好的数组中;l2+1;再次进行比较  l1 与l2 元素的大小……,一直到  l1大于终止点h1或者l2大于终止点 h2,再将还有剩余的子数组的元素挪到新数组中(子数组一定是有序的,因为是我们之前排序过的)。

第二次分组执行完后:

1592edba6c7f482eb4593531a670f12c.png

再接着gap=4,四个一组进行俩个组之间的合并。 

62686f7ab2d944fc9f4ac12812ae110e.png

 一直这样排序,知道分的组比数组本身都大,那么排序就完成了!

下面说这个问题:

初始值 h2=l2+gap-1<len?l2+gap-1:len-1;

h2可能会有溢出的情况,所以当h2溢出的时候,直接把数组最后一个下表赋值给他就好了,不需要再多加进行判断。

 

可以看到,归并排序的时间复杂度为O(nlogn),时间复杂度比起其他算法略低但是空间复杂度为O(n)

 下面是归并排序的代码:

const int SIZE = 10;

void Merge(int arr[], int gap)
{
	int l1 = 0;
	int h1 = l1 + gap - 1;
	int l2 = h1 + 1;
	int h2 = l2 + gap - 1 < SIZE ? l2 + gap - 1 : SIZE - 1;

	int* brr = new int[SIZE] {};
	int i = 0;
	while (l2 < SIZE)
	{
		while (l1 <= h1 && l2 <= h2)               
		{
			if (arr[l1] > arr[l2])
			{
				brr[i++] = arr[l2++];
			}
			else
			{
				brr[i++] = arr[l1++];
			}
		}
		while (l1 <= h1)
		{
			brr[i++] = arr[l1++];
		}
		while (l2 <= h2)
		{
			brr[i++] = arr[l2++];
		}
		l1 = h2 + 1;
		h1 = l1 + gap - 1;
		l2 = h1 + 1;
		h2 = l2 + gap - 1 < SIZE ? l2 + gap - 1 : SIZE - 1;
	}
	while (l1 <SIZE)
	{
		brr[i++] = arr[l1++];
	}
	memmove(arr, brr,SIZE*sizeof(int));
	delete[] brr;
	brr = nullptr;
}

int main()
{
	int arr[SIZE] = { 5,9,4,2,6,7,1,3,8,0 };
	for (int i = 1; i < SIZE; i *= 2)
	{
		Merge(arr, i);
	}
	for (int i = 0; i < SIZE; ++i)
	{
		cout << arr[i] << "  ";
	}
}

结果:6213817de92f4f6a8438c39f7abe5be3.png

感谢阅读!

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

g162512

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值