数据结构经典排序之归并排序(超详细!!)

归并排序

归并排序(Merge Sort)是一种分治策略的排序算法, 其基本思想是将待排序的序列划分为若干个子序列,每个子序列是一个有序的序列。然后再把有序子序列合并为整体有序序列。

算法步骤
归并排序的算法可以概括为以下步骤:

分解:将当前待排序的序列分解为两个子序列。
递归进行排序并合并:对子序列递归地应用归并排序算法,然后将已排序的子序列合并成一个有序的序列,这是归并排序算法的核心步骤。
合并:将两个已经排序的子序列合并成一个最终的排序序列。
具体实现
递归分解
将待排序的数组一直对半分割,直到每个子数组只包含一个元素,此时可以认为每个子数组都是有序的。
递归合并
从最底层的只包含一个元素的子数组开始,逐步向上层合并,每次合并都将两个有序数组合并成一个更大的有序数组。
在合并过程中,通过比较两个子数组的首元素,选择较小的元素放入新的数组中,然后移动下标,直到一个数组为空。之后,将另一个非空子数组的剩余元素依次放入新数组。
示例
假设有一个待排序的数组 [8, 4, 5, 7, 1, 3, 6, 2]。

分解:一直分割到每个子数组只含一个元素,此时每个子数组都是有序的。
[8], [4], [5], [7], [1], [3], [6], [2]
递归合并:开始合并这些子数组,使其变得有序。
合并 [8] 和 [4],得到 [4, 8]
合并 [5] 和 [7],得到 [5, 7]
合并 [1] 和 [3],得到 [1, 3]
合并 [6] 和 [2],得到 [2, 6]

再合并上述得到的三个子数组:
合并 [4, 8] 和 [5, 7],得到 [4, 5, 7, 8]
合并 [1, 3] 和 [2, 6],得到 [1, 2, 3, 6]

最后,合并两个有序数组[4, 5, 7, 8]和 [1, 2, 3, 6],得到最终排序结果:
[1, 2, 3, 4, 5, 6, 7, 8]
复杂度分析
时间复杂度:归并排序的时间复杂度为 O(n log n),其中 n 是数组的长度。这是因为归并排序将数组拆分为两半,然后对每一半分别进行排序,最后再将它们合并。这个过程会递归进行,直到数组被拆分为只包含一个元素的子数组。由于每次拆分都会使数组长度减半,因此递归的深度是 log n。在每一层递归中,都需要对所有的元素进行操作,因此每一层的时间复杂度是 O(n)。所以总的时间复杂度是 O(n log n)。
空间复杂度:由于归并排序需要额外的空间来存储合并过程中的临时数组,因此其空间复杂度为O(n)。
稳定性:归并排序是一种稳定的排序算法,即相同值的元素在排序后保持原有的相对顺序。
适用场景:归并排序适用于对大量数据进行排序,特别是当数据完全无序时,归并排序的性能表现较好。此外,归并排序也常用于外部排序,即处理存储在外部存储介质(如磁盘)上的大数据集。

代码实现

首先要定义一个_MergerSort函数,该函数是归并排序的主要实现部分。然后定义了一个MergerSort函数,该函数主要用于初始化临时数组tmp并调用_MergerSort函数进行排序。排序完成后,MergerSort函数会释放tmp数组的内存空间。

// 辅助函数,用于递归地对数组进行归并排序  
void _MergerSort(int* a, int begin, int end, int* tmp) {  
    // 如果只有一个元素或没有元素,直接返回,因为一个元素或没有元素本身就是有序的  
    if (begin == end) {  
        return;  
    }  
      
    // 找到数组的中间位置  
    int mid = (begin + end) / 2;  
      
    // 对左半部分进行归并排序  
    _MergerSort(a, begin, mid, tmp);  
    // 对右半部分进行归并排序  
    _MergerSort(a, mid + 1, end, tmp);  
  
    // 定义两个子数组的起始和结束位置  
    int begin1 = begin;  
    int end1 = mid;  
    int begin2 = mid + 1;  
    int end2 = end;  
    int i = begin; // 用于tmp数组的索引  
  
    // 当两个子数组都有元素时,选择较小的元素放入tmp数组  
    while (begin1 <= end1 && begin2 <= end2) {  
        if (a[begin1] < a[begin2]) {  
            tmp[i] = a[begin1];  
            i++;  
            begin1++;  
        } else {  
            tmp[i] = a[begin2];  
            i++;  
            begin2++;  
        }  
    }  
  
    // 如果左子数组还有元素,将其剩余元素依次放入新数组。
    while (begin1 <= end1) {  
        tmp[i] = a[begin1];  
        i++;  
        begin1++;  
    }  
  
    // 如果右子数组还有元素,将其剩余元素依次放入新数组。
    while (begin2 <= end2) {  
        tmp[i] = a[begin2];  
        i++;  
        begin2++;  
    }  
  
    // 将tmp数组中的有序元素复制回原数组a  
    memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));  
}  
  
// 主函数,用于初始化并调用归并排序的辅助函数  
void MergerSort(int* a, int n) {  
    // 动态分配一个与原数组相同大小的临时数组tmp  
    int* tmp = (int*)malloc(sizeof(int) * n);  
    if (tmp == NULL) { // 如果内存分配失败,打印错误信息并返回  
        perror("malloc fail");  
        return;  
    }  
  
    // 调用辅助函数进行归并排序  
    _MergerSort(a, 0, n - 1, tmp);  
  
    // 释放临时数组tmp的内存空间,并将tmp设置为NULL,防止野指针的产生  
    free(tmp);  
    tmp = NULL;  
}

使用示例

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void PrintArray(int* a, int n)//打印
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}


void _MergerSort(int* a, int begin, int end, int* tmp) {
	if (begin == end) {
		return;
	}
	int mid = (begin + end) / 2;
	_MergerSort(a, begin, mid, tmp);
	_MergerSort(a, mid + 1, end, tmp);

	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;
	int i = begin;

	while (begin1 <= end1 && begin2 <= end2) {
		if (a[begin1] < a[begin2]) {
			tmp[i] = a[begin1];
			i++;
			begin1++;
		}
		else {
			tmp[i] = a[begin2];
			i++;
			begin2++;
		}
	}

	while (begin1 <= end1) {
		tmp[i] = a[begin1];
		i++;
		begin1++;
	}

	while (begin2 <= end2) {
		tmp[i] = a[begin2];
		i++;
		begin2++;
	}

	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}

void MergerSort(int* a, int n) {
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL) {
		perror("malloc fail");
		return;
	}

	_MergerSort(a, 0, n - 1, tmp);

	free(tmp);
	tmp = NULL;
}

void TestMergerSort()
{
	int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };
	PrintArray(a, sizeof(a) / sizeof(int));//计算数组元素个数并打印

	MergerSort(a, sizeof(a) / sizeof(int));

	PrintArray(a, sizeof(a) / sizeof(int));
}


int main() {
	TestMergerSort();

	return 0;
}
  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值