版权声明:站在巨人的肩膀上学习。 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