一,概念及其介绍
归并排序(Merge sort)是建立在归并操作上的一种有效、稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;若将两个有序表合并成一个有序表,称为二路归并。
二,复杂度说明
时间复杂度: O(nlogn)
对于自顶向下的归并排序, 归并排序通过不断将数组对半划分,直到每个子数组只有一个元素(这需要 logn次划分)。然后,合并这些子数组的过程中,每个元素都需要被处理一次,总共需要处理 n个元素。所以总的操作次数约为 nlog n ,因此时间复杂度为 O(nlog n)
空间复杂度: O(n)
空间复杂度为 O(n) 是因为在合并子数组的过程中,需要额外创建一个与原数组大小相同的辅助数组来存放合并后的结果。所以空间复杂度主要取决于这个辅助数组的大小,即为 O(n)。
三,过程图示
归并排序是递归算法的一个实例,这个算法中基本的操作是合并两个已排序的数组,取两个输入数组 A 和 B,一个输出数组 C,以及三个计数器 i、j、k,它们初始位置置于对应数组的开始端。
A[i] 和 B[j] 中较小者拷贝到 C 中的下一个位置,相关计数器向前推进一步。
当两个输入数组有一个用完时候,则将另外一个数组中剩余部分拷贝到 C 中
四.代码及解析
4.1 python
思路:
使用两个函数完成,一个函数用于连接两个有序列表,返回一个有序的新列表,一个函数用于递归将已知列表分为左右两部分,递归分割,再进行连接,当列表中只有一个元素时则一定是有序的
知识点:
1.列表的append用于向列表添加一个元素
2.列表的extend用于向列表添加一个可迭代对象
# merge_sort的主要作用是通过递归地将输入的数组不断地分割成更小的子数组,
# 直到子数组的长度为 1 (此时子数组本身就是有序的),然后调用 merge 函数将这些有序的子数组合并起来,
# 最终实现对整个数组的排序
def merge_sort(arr):if len(arr) <= 1:return arrmid = len(arr) // 2left_half = merge_sort(arr[:mid])right_half = merge_sort(arr[mid:])return merge(left_half, right_half)# arr = [12, 11, 13, 5, 6]
# merge函数用于连接两个有序数列。并存放到新数列中
def merge(left, right):merged = []left_index = 0right_index = 0# 两两对比,小的放入新列表,所在列表的下标指向往后移动,大的所在列表下标指向不变while left_index < len(left) and right_index < len(right):if left[left_index] < right[right_index]:merged.append(left[left_index])left_index += 1else:merged.append(right[right_index])right_index += 1# 利用切片将剩下的元素放到新列表中merged.extend(left[left_index:])merged.extend(right[right_index:])return merged# 测试示例
arr = [12, 11, 13, 5, 6]
sorted_arr = merge_sort(arr)
print("排序后的数组:", sorted_arr)
排序后的数组: [5, 6, 11, 12, 13]
4.2 C
思路:
使用两个函数,一个函数用于递归将数组左右两部分割开,再递归分割,直到分割到只有一个元素时一定是有序的,再进行两个有序数组的连接,这里再定义另一个函数作用是连接两个有序数组
注意点:
1.直接传入数组进行操作,所以两个函数都不需要返回值
2.merge函数利用两个新的数组,分别存放左右两边的原数组,再进行比较,依次放入原数组中
3.int m = l + (r - l) / 2;
这个表达式中使用 r - l
是为了计算数组区间 [l, r]
的长度。
通过 (r - l)
得到区间的长度后再除以 2,就能得到区间长度的一半。然后加上起始索引 l
,就可以得到区间的中间位置 m
,从而将数组划分为左右两个子区间 [l, m]
和 [m + 1, r]
,以便进行归并排序的递归操作
#include <stdio.h>// 合并两个已排序的子数组为一个排序数组
void merge(int arr[], int l, int m, int r) {int n1 = m - l + 1; // 左边子数组的大小int n2 = r - m; // 右边子数组的大小// 创建临时数组int L[n1], R[n2];// 复制数据到临时数组for (int i = 0; i < n1; i++)L[i] = arr[l + i];for (int j = 0; j < n2; j++)R[j] = arr[m + 1 + j];int i = 0, j = 0, k = l;// 合并临时数组回到原始数组while (i < n1 && j < n2) {if (L[i] <= R[j]) {arr[k++] = L[i++];} else {arr[k++] = R[j++];}}// 复制剩余的元素while (i < n1) {arr[k++] = L[i++];}while (j < n2) {arr[k++] = R[j++];}
}// 归并排序函数
void mergeSort(int arr[], int l, int r) {if (l < r) {int m = l + (r - l) / 2; // 取中间元素作为分割点// 对左边的子数组进行排序mergeSort(arr, l, m);// 对右边的子数组进行排序mergeSort(arr, m + 1, r);// 合并两个已排序的子数组merge(arr, l, m, r);}
}// 打印数组函数
void printArray(int arr[], int size) {for (int i = 0; i < size; i++)printf("%d ", arr[i]);printf("\n");
}// 测试案例
int main() {int arr[] = {12, 11, 13, 5, 6};int arr_size = sizeof(arr) / sizeof(arr[0]);printf("before sort: \n");printArray(arr, arr_size);mergeSort(arr, 0, arr_size - 1);printf("after sort: \n");printArray(arr, arr_size);return 0;
}
before sort:
12 11 13 5 6
after sort:
5 6 11 12 13
4.3 C++
与C语言思路一致,只有一些语法上的小差别
#include <iostream>// 合并两个已排序的子数组为一个排序数组
void merge(int arr[], int l, int m, int r) {int n1 = m - l + 1;int n2 = r - m;int L[n1], R[n2];for (int i = 0; i < n1; i++)L[i] = arr[l + i];for (int j = 0; j < n2; j++)R[j] = arr[m + 1 + j];int i = 0, j = 0, k = l;while (i < n1 && j < n2) {if (L[i] <= R[j]) {arr[k++] = L[i++];} else {arr[k++] = R[j++];}}while (i < n1) {arr[k++] = L[i++];}while (j < n2) {arr[k++] = R[j++];}
}// 归并排序函数
void mergeSort(int arr[], int l, int r) {if (l < r) {int m = l + (r - l) / 2;mergeSort(arr, l, m);mergeSort(arr, m + 1, r);merge(arr, l, m, r);}
}// 打印数组函数
void printArray(int arr[], int size) {for (int i = 0; i < size; i++)std::cout << arr[i] << " ";std::cout << std::endl;
}// 测试案例
int main() {int arr[] = {12, 11, 13, 5, 6};int arr_size = sizeof(arr) / sizeof(arr[0]);std::cout << "排序前的数组为: ";printArray(arr, arr_size);mergeSort(arr, 0, arr_size - 1);std::cout << "排序后的数组为: ";printArray(arr, arr_size);return 0;
}
排序前的数组为: 12 11 13 5 6
排序后的数组为: 5 6 11 12 13