排序(四)归并排序 c/c++与python实现

归并排序(Merge Sort)

归并,即将两个或两个以上的有序表组成一个新的有序表。

2-路归并排序的基本思想:将序列视为n个组,将这些组两两归并归并为n/2个组,然后将这些组再两两归并,生成n/4个组,以此类推,直到只剩下一个组为止。

归并排序使用的就是分治思想,即先把序列从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分归并在一起,这样整个序列就都有序了。
在这里插入图片描述
关于递归与分治:

  • 分治是一种解决问题的处理思想,而递归是一种编程技巧。
  • 分治算法一般都是用递归来实现的。
#include <stdio.h>
#include <stdlib.h>
//	归并排序递归c/c++实现
/**
 * Author: gamilian
*/
const int maxn =100;
//将数组a的[L1,R1]与[L2,R2]区间合并为有序区间(此处L2即为R1+1)
void merge(int a[], int L1, int R1, int L2, int R2){
	int i = L1, j = L2;				
	int temp[maxn], index = 0;		
	while(i <= R1 && j <= R2){
		if(a[i] <= a[j])
			temp[index++] = a[i++];//将a[i]加入序列temp
		else	
			temp[index++] = a [j++];
	}
	while(i <= R1)
		temp[index++] = a[i++];//将[L1,R1]的剩余元素加入序列temp
	while(j <= R2)
		temp[index++] = a[j++];//将[L2,R2]的剩余元素加入序列temp 
	
	for(i = 0; i < index; i++){
		a[L1 + i] = temp[i];//将合并后的序列赋值回数组A
	}
}
void merge_sort_between(int a[], int left, int right){
	if (left < right){
	int mid = left + (right - left) / 2;
	merge_sort_between(a, left, mid);		//左区间归并排序
	merge_sort_between(a, mid + 1, right);	//右区间归并排序
	merge(a, left, mid, mid + 1, right);
	}	//将左、右区间归并
}
void merge_sort(int a[], int n){
	merge_sort_between(a, 0 ,n - 1);
}
//	归并排序非递归c/c++实现
/**
 * Author: gamilian
*/
#define min(x, y) (x) < (y) ? (x) : (y)
void merge_sort(int a[], int n){
	for(int step = 2; step / 2 <= n; step *= 2){	//step为组内元素个数,step/2为左子区间元素的个数
		for(int i = 0; i < n; i += step){			//每step个元素一组,组内前step/2个元素与后step/2个元素归并
			int mid = i + step / 2 - 1;
			if (mid + 1 < n){	//右区间存在则合并
				merge(a, i ,mid, mid + 1, min(i + step - 1 , n - 1)); 
			}
		}
	}
}
#	归并排序递归python实现
"""
    Author: gamilian
"""
from typing import List
def merge_sort(a: List[int]):
    merge_sort_between(a, 0, len(a) - 1)
    
# 对[left, right]区间内的元素归并排序
def merge_sort_between(a: List[int], left: int, right: int):
    if left < right:
        mid = left + (right - left) // 2
        #   左区间归并排序
        merge_sort_between(a, left, mid) 
        #   右区间归并排序
        merge_sort_between(a, mid + 1, right)
        #   将左、右区间归并
        merge(a, left, mid, mid + 1, right)

def merge(a: List[int], L1: int, R1: int, L2: int, R2: int):
    # 默认L2 = R1 + 1
    i, j = L1, L2
    tmp = []
    while i <= R1 and j <= R2:
        if a[i] <= a[j]:
            tmp.append(a[i])
            i += 1
        else:
            tmp.append(a[j])
            j += 1
    start = i if i <= R1 else j
    end = R1 if i <= R1 else R2
    tmp.extend(a[start:end + 1])
    length = len(tmp)
    a[L1:L1 + length] = tmp
#	归并排序非递归python实现
"""
    Author: gamilian
"""
from typing import List
def merge_sort(a: List[int]):
    length = len(a)
    step = 2
    for step in range(2, length * 4, 2):
        for i in range(0, length, step):
            mid = i + step // 2 - 1
            if mid + 1 < length:
                merge(a, i, mid, mid + 1, min(i + step - 1 , length - 1))

算法的稳定性:值相同的元素,在归并前后的先后顺序不变。所以,归并排序是一个稳定的排序算法

空间复杂度 :在归并两个有序序列为一个有序序列时,需要借助额外的存储空间。尽管每次归并操作都需要申请额外的内存空间,但在合并完成之后,临时开辟的内存空间就被释放掉了。在任意时刻,CPU 只会有一个函数在执行,也就只会有一个临时的内存空间在使用。临时内存空间最大也不会超过 n 个数据的大小,所以空间复杂度是 O(n)

时间复杂度:归并排序采用分治的思想:将一个大问题a分为多个子问题b、c。如果我们定义求解问题 a 的时间是 T(n),求解问题 b、c 的时间分别是 T(n/2) 和 T(n/2),而merge() 函数合并两个有序子数组的时间复杂度是 O(n)。那我们就可以得到这样的递推关系式:

T(1) = C;		// n = 1时,只需要常量级的执行时间,所以表示为C。
T(n) = 2 * T(n/2)  + n;	//n > 1

故:

T(n) = 2*T(n/2) + n 
	 = 2*(2*T(n/4) + n/2) + n 
   	 = 4*T(n/4) + 2*n 
   	 = 4*(2*T(n/8) + n/4) + 2*n 
   	 = 8*T(n/8) + 3*n = 8*(2*T(n/16) + n/8) + 3*n 
   	 = 16*T(n/16) + 4*n ...... 
   	 = 2^k * T(n/2^k) + k * n ......

当 T(n/2^k) =T(1) 时,也就是 n/2^k=1,我们得到 k=log2n 。T(n)=Cn+nlog2n 。

归并排序的执行效率与要排序的原始数组的有序程度无关,所以其时间复杂度是非常稳定的,不管是最好情况、最坏情况,还是平均情况,时间复杂度都是 O(nlogn)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值