算法实现:归并(合并)排序(C/C++、Python)

版权声明:站在巨人的肩膀上学习。 https://blog.csdn.net/zgcr654321/article/details/83049893

合并排序的关键步骤在于合并步骤中的合并两个已排序子序列。为做合并,引入一个辅助过程MERGE(A, p, q, r), 其中A是一个数组,p、q和r是下标,满足p小于等于q小于r。该过程假设子数组A[p...q] 和A[q+1...r]都已排好序,并将它们合并成一个已排好序的子数组代替当前子数组A[p.. r] 。

下面来说明该算法的工作过程:

举扑克牌这个例子,假设有两堆牌面朝上地放在桌上,每一堆都是已排序的,最小的牌在最上面。我们希望把这两堆牌合并成一个排好序的输出堆,面朝下地放在桌上。基本步骤包括在面朝上的两堆牌中,选取顶上两张中较小的一张,将其取出后(它所在堆的顶端又会露出一张新的牌)面朝下地放到输出堆中。重复这个步骤,直到某一输入堆为空时为止。这时,把输入堆中余下的牌面朝下地放入输出堆中即可。从计算的角度来看,每一个基本步骤所花时间是个常量,因为我们只是查香并比较顶上的两张牌。又因为至多进行n次比较,所以合并排序的时间为。

在伪代码实现时,我们增加一张“哨兵牌”。在每一堆的底部放上一张“哨兵牌" (sentinel card) , 它包含了一个特殊的值,用于简化代码。此处,利用来作为哨兵值,这样每当露出一张值为的牌时,它不可能是两张中较小的牌,除非另一堆也露出了哨兵牌。但是,一且发生这种两张哨兵牌同时出现的情况时,说明两堆牌中的所有非哨兵牌都已经被放到输出堆中去了。因为我们预先知道只有r-p+1张牌会被放到输出堆中去,因此, 一旦执行了r-p+1个基本步骤后(两堆牌合并过程中的运行次数),算法就可以停止下来了。

伪代码:

MERGE(A,p,q,r)
n1 <- q-p+1
n2 <- r-q
create arrays L[1...n1+1] and R[1...n2+1]
for i<-1 to n1
    do L[i] <- A[p+i-1]
for j<-1 to n2
    do R[j] <- A[q+j]
L[n1+1] <- 极大值哨兵元素
R[n2+1] <- 极大值哨兵元素
i<-1
j<-1
for k<- p to r
    do if L[i] <= R[j]
        then A[k] <- L[i]
            i <- i+1
        else A[k] <- R[j]
            j <- j+1
 
MERGE-SORT(A,p,r)
if p<r
    then q<-(p+r)/2
        MERGE-SORT(A,p,q)
        MERGE-SORT(A,q+1,r)
        MERGE(A,p,q,r)

C/C++代码:

#include <stdio.h>
#include <string.h>
#include <limits.h>
 
using namespace std;
 
void Merge(int *A, int p, int q, int r) {
	int n1 = q - p + 1, n2 = r - q;
	int *L = new int[n1 + 1];
	int *R = new int[n2 + 1];
	//分成两部分的子数组分别存在L和R中
	for (int i = 0; i < n1; i++)
		L[i] = A[p + i];
	for (int j = 0; j < n2; j++)
		R[j] = A[q + 1 + j];
	L[n1] = R[n2] = INT_MAX;
	//L和R的哨兵元素
	int i = 0, j = 0;
	//当L和R均未遍历到哨兵元素时,哪个小哪个就先放到数组A中相应位置
	//当其中有一个遍历到哨兵元素时,由于哨兵元素是极大值,故if选择时就会将另一个子数组剩余元素放到数组A中剩余位置中
	for (int k = p; k <= r; k++) {
		if (L[i] <= R[j]) {
			A[k] = L[i];
			i = i + 1;
		} else {
			A[k] = R[j];
			j = j + 1;
		}
	}
}
 
void MergeSort(int A[], int p, int r) {
	if (p < r) {
		int q = (p + r) / 2;
		//分解,递归地调用MergeSort函数
		// 继续分解直到子数组足够小时(即p和q相差1时,此时再调用MergeSort函数已经无法再拆分成更小子问题)开始合并解决子问题
		MergeSort(A, p, q);
		MergeSort(A, q + 1, r);
		//合并子问题的解
		Merge(A, p, q, r);
	}
}
 
int main() {
	int n;
	scanf("%d", &n);
	int *A = new int[n];
	for (int i = 0; i < n; i++)
		scanf("%d", &A[i]);
	printf("input complete\n");
	MergeSort(A, 0, n - 1);
	printf("print the sorted number:\n");
	for (int i = 0; i < n; i++)
		printf("%d ", A[i]);
	return 0;
}

运行结果如下:

8
8 7 6 5 4 3 2 1
input complete
print the sorted number
1 2 3 4 5 6 7 8 
Process finished with exit code 0

Python3代码:

def merge(a, p, q, r):
	L, R = [], []
	for k, element in enumerate(a):
		if p <= k <= q:
			L.append(element)
		elif q + 1 <= k <= r:
			R.append(element)
	# 分成两部分的子数组分别存在L和R中
	L.append(float('inf'))
	R.append(float('inf'))
	# 给L和R两个列表末尾各添加一个无穷大值作为哨兵
	i, j = 0, 0
	# 当L和R均未遍历到哨兵元素时,哪个小哪个就先放到数组A中相应位置
	# 当其中有一个遍历到哨兵元素时,由于哨兵元素时极大值,故if选择时就会将另一个子数组剩余元素放到数组A中剩余位置中
	for k, element in enumerate(a, p):
		if k <= r:
			if L[i] <= R[j]:
				a[k] = L[i]
				i = i + 1
			else:
				a[k] = R[j]
				j = j + 1
 
 
def merge_sort(a, p, r):
	if p < r:
		q = int((p + r) / 2)
		# 分解,递归地调用MergeSort函数
		# 继续分解直到子数组足够小时(即p和q相差1时,此时再调用MergeSort函数已经无法再拆分成更小子问题)开始合并解决子问题
		merge_sort(a, p, q)
		merge_sort(a, q + 1, r)
		# 合并子问题的解
		merge(a, p, q, r)
 
 
A = []
while True:
	try:
		A.append(int(input()))
	except:
		print('input complete')
		break
merge_sort(A, 0, len(A) - 1)
print("print the sorted number:")
for index, item in enumerate(A):
	print(item, end=' ')
# python3默认打印会换行,加上end='  ',则每次后面会自动加上' '中内容而不是换行

运行结果如下:

8
7
6
5
4
3
2
1
 
input complete
print the sorted number:
1 2 3 4 5 6 7 8 
Process finished with exit code 0

猜你喜欢

转载自blog.csdn.net/zgcr654321/article/details/83049893