归并排序的递归和非递归实现,思想分析,时间复杂度分析

  1. 思路分析
  2. 递归版代码实现及分析
  3. 非递归版代码实现及分析
  4. 时空复杂度分析

思路分析

在这里插入图片描述
首先要明白的是归并排序的整个过程其实就是两部分,分解和合并。在递归代码中分解就是不断向下递归的过程,而合并就是递归到叶子结点后进行合并排序然后返回的过程。而核心过程是合并过程。
合并是将两个已经有序的数组和合并成一个有序数组下面给出合并过程的思路。

在这里插入图片描述
合并的前提的这两个数组已经有序,分解就是为了解决这一前提的。将这两个数组再分别分解成两个数组,分解后的数组再分解,直到分解后的两个数组中只有一个元素,可以认为只有一个元素的数组是有序的,然后再向上合并。

代码

1.递归版代码

void _MergeSort(int *a, int left, int right,int *temp)
{
    
    
	if (left >= right)
	{
    
    
		return;
	}
	//将整个数组分成两个区间[left,mid] [mid+1,right]
	int mid = (left + right) / 2;

	//对[left,mid]进行归并排序
	_MergeSort(a, left, mid, temp);
	//对[mid+1,right]进行归并排序
	_MergeSort(a, mid+1, right, temp);

	//当[left,mid]和[mid+1,right]两个区间的数分有序时需要借助辅助数组进行合并使得 [left,right] 整个区间的数都是有序的	
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	
	//合并排序的过程。
	SortArray(a, begin1, end1, begin2, end2, temp);
}

2.合并过程的代码

void SortArray(int *a, int begin1, int end1, int begin2, int end2,int *temp)
{
    
    
	int left = begin1;
	int right = end2;
	//[begin1,end1] [begin2,end2] 是两个有序的区间
	//合并成一个有序区间
	int i = 0;
	while (begin1 <= end1 && begin2 <= end2)
	{
    
    
		if (a[begin1] < a[begin2])
		{
    
    
			temp[i++] = a[begin1++];
		}
		else
		{
    
    
			temp[i++] = a[begin2++];
		}
	}

	//将两个区间有剩余的数据放到temp数组的后面
	while (begin1 <= end1)
	{
    
    
		temp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
    
    
		temp[i++] = a[begin2++];
	}

	//将temp数组的内容重新写到 a 数组中
	memcpy(a + left, temp, sizeof(int)*(right - left + 1));
}

3.非递归版本代码

void MergeSortNonR(int *a, int n)
{
    
    
	int *temp = malloc(sizeof(int)*n);
	int gap = 1;
	while (gap < n)
	{
    
    
		for (int i = 0; i < n; i += 2 * gap)
		{
    
    
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			
			//合并的时候是进行两个数组的合并,如果第二个数组的范围不存在(即第二个数组不存在)则不进行合并 
			//这种情况下第一个数组的范围的右区间end2可能存在也可能不存在
			//但是不论它是否存在,只要第二个数组不存在就不进行合并
			if (begin2 >= n)
			{
    
    
				break;
			}

			//走到这说明第二个数组的左区间存在右区间不存在,此时第二个数组存在仅仅是元素的个数与第一个数组不同
			//需要修正右区间的大小为n-1保证它不越界,然后再和第一个数组合并 。合并的时候两个数组元素的个数不一定相同
			if (end2 >= n)
			{
    
    
				end2 = n-1;
			}
			
			//合并排序的过程 (实现的过程还是上面那个函数)
			SortArray(a, begin1, end1, begin2, end2, temp);
		}
		gap *= 2;
	}

3.时间复杂度分析

时间复杂度为O(NlogN),空间复杂度O(N)
空间复杂度就是开辟了一个和原数组一样大的零食数组。
归并排序中,最后一次合并的过程时间复杂度是O(N)。
在这里插入图片描述
可以看到每一层的时间复杂度都是O(N),树总共右log(N+1)层,总的时间复杂度O(N
logN)。

猜你喜欢

转载自blog.csdn.net/weixin_50168448/article/details/113192114