归并排序

归并排序

  • 算法思想
  1. 归并排序算法思想的核心是有序子序列的合并
      原始的Merge()函数的处理:
         - 数组A[],指针Aptr
         - 数组B[],,指针Bptr
         - 新增的数组C[],指针Cptr
       并且很容易知道Merge()函数的时间复杂度是O(N).
    在这里插入图片描述
      归并排序的Merge()函数的处理:
       - 因为排序的是一个数组,故要分成两部分
       - 参数:第一个序列的起始位置L,第二个序列的起始位置R,和RightEnd。(第一个序列的终止位置可以计算R-1)
       - 数组TmpA[]的指针Tmp,将元素从L开始,并没有从0开始(需要通过后面内容理解
    递归在这里插入图片描述
    2.递归算法实现:
      归并排序的递归实现时分治思想的典型应用。
      分治思想:分治法将原问题划分成若干个规模较小的子问题(结构相同或相似),然后分别解决这些子问题,最后将结果合并。即:
      1.分解(原问题)
      2.解决(子问题)
      3.合并(子问题的解)
  • 递归算法实现
/*
归并算法的实现
*/
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

void Merge(int A[],int tmpA[],int L,int R,int RightEnd)//最终合并的结果归并到A[]中
{
    int i;
    int LeftEnd = R - 1;
    int n = RightEnd - L + 1;
    int tmp_ptr = L;

    while(L<=LeftEnd && R <=RightEnd)
    {
        if(A[L]<A[R])
        {
            tmpA[tmp_ptr++] = A[L++];
        }
        else
        {
            tmpA[tmp_ptr++] = A[R++];
        }
    }

    while(L<=LeftEnd) tmpA[tmp_ptr++] = A[L++];
    while(R<=RightEnd) tmpA[tmp_ptr++] = A[R++];

    for(i = 0;i<n;i++,RightEnd--)
    {
        A[RightEnd] = tmpA[RightEnd];
    }
}

void Msort(int A[],int tmpA[],int L,int RightEnd)
{
    int center = 0;
    if(L>=RightEnd) return;

    if(L<RightEnd)
    {
        center = (L+RightEnd)/2;
        Msort(A,tmpA,L,center);
        Msort(A,tmpA,center+1,RightEnd);
        Merge(A,tmpA,L,center+1,RightEnd);
    }
}
void Merge_sort(int A[],int N)
{
    int *tmpA;
    tmpA = (int*)malloc(N*sizeof(int));
    Msort(A,tmpA,0,N-1);
    free(tmpA);
}
int A[8] = {1,4,9,13,8,6,3,2};
int main()
{
    int i;
    Merge_sort(A,8);
    for(i = 0;i<8;i++)
    {
        if(i == 7)
            printf("%d\n",A[7]);
        else printf("%d ",A[i]);
    }
	return 0;
}

//1 2 3 4 6 8 9 13

可以通过函数的调用实现Merge_sort(int A[],int N);即:
  Merge()->Msort()->Merge_sort()
注意:
Merge()中Tmp指针从何处开始?
此时的Merge()函数不应该从0开始,需要从L中开始,否则会将数据覆盖
Merge()中然后将TmpA[]临时数组导入A[]中?
会发现变量L是会发生变化的,但是RightEnd一直未发生变化,故可以采用从右往左,
for(i = 0;i<n;i++,RightEnd--)
这种写法简单易懂。需要直到最右边的位置,和数组的总长度。
TmpA[]数组的开辟,是在哪里?
通过观察可知,只有在Merge()的时候使用该数组,不应该在此处开辟空间,否则函数会反复的malloc()free()影响程序的效率。在递归函数Msort()中亦是如此。在Merge_sort()中只需要开辟一次空间即可。

3.非递归算法实现
非递归算法实现的思想:
数组A[]中有N个待排元素,可以看成有N个有序的子序列,每一个子序列中都只含有一个元素。每一次归并相邻的子序列,直到子序列的长度为N。
Q:额外的空间复杂度是什么?需要每一次合并都开辟空间 吗?
**A:**只需要开辟一个临时数组TmpA[],此时A[]和TmpA[]都可以看成是临时数组。
并且按照这样的合并方式,最后的结果有可能存放在A[]中或者tmpA[]中。
在这里插入图片描述

#include <stdio.h>
#include<stdlib.h>
void Merge1(int A[],int tmpA[],int L,int R,int RightEnd)
{

	int LeftEnd = R-1;
	int tmp_ptr = L;
	while(L<=LeftEnd && R<=RightEnd)
	{
		if(A[L] < A[R])
		{
			tmpA[tmp_ptr++] = A[L++];
		}
		else
		{
			tmpA[tmp_ptr++] = A[R++];
		}
	}
	while(L<=LeftEnd)
	{
		tmpA[tmp_ptr++] = A[L++];
	}
	while(R<=RightEnd)
	{
		tmpA[tmp_ptr++] = A[R++];
	}

}

void Merge_pass(int A[],int tmpA[],int N,int length)
{
	int i,j;
	for(i= 0;i<=N-2*length;i+=2*length)
	{
		Merge1(A,tmpA,i,i+length,i+2*length - 1);
	}
	if(i+length<N)
		Merge1(A,tmpA,i,i+length,N-1);
	else
	{
		for(j = i;j<N;j++) tmpA[j] = A[j];
	}

}

void Merge_sort(int A[],int N)
{
	int *tmpA = (int*)malloc(N*sizeof(int));
	int length = 1;
	while(length<N)
	{
		Merge_pass(A,tmpA,N,length);
		length  = length*2;
		Merge_pass(tmpA,A,N,length);
		length = length*2;
	}
	free(tmpA);
}


int main()
{
	int i;
	int A[8] = {1,4,9,13,8,6,3,2};
	Merge_sort(A,8);
    for(i = 0;i<8;i++)
    {
        if(i == 7)
            printf("%d\n",A[7]);
        else printf("%d ",A[i]);
    }
	return 0;
}

//1 2 3 4 6 8 9 13

注意:
Merge_pass()函数是一次有序子序列的合并。其中的Merge1()和递归中的Merge()有所不同,合并的思想是先一对一对的合并留下尾巴的部分单独处理,尾巴上有两个子序列或者一个子序列(有两个子序列的情况并不相等,若想进一步理解,可以考虑数组中元素有4,7,3这三种情况)
Merge_sort()中,实现中要注意,for(i= 0;i<=N-2*length;i+=2*length)

while(length<N)
	{
		Merge_pass(A,tmpA,N,length);
		length  = length*2;
		Merge_pass(tmpA,A,N,length);
		length = length*2;
	}

保证了结果一定在A[]中。
4.小结

归并排序算法的好处:

  • 算法是稳定的
  • 平均复杂度是O(Nlog(N))
  • 最坏复杂度是O(Nlog(N))
     归并排序算法的好处:
  • 需要额外的空间开辟临时数组
  • 来回的交换元素
    (递归实现中,在merge中会交换元素。在非递归实现中,在merge中虽然不会交换元素,单while循环中会不断的交换元素)

因此,在内部排序中基本不会使用归并排序,
但是在外部排序中,归并排序非常的有效。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值