归并排序的流程
归并排序是一种基于分治思想的排序算法,其基本思路可以分为三个步骤:
步骤一:分解(Divide):归并排序使用递归算法来实现分解过程,具体实现中可以分为以下几个步骤:
如果待排序数组长度为1,认为这个数组已经有序,直接返回;
将待排序数组分成两个长度相等的子数组,分别对这两个子数组进行递归排序;
将两个排好序的子数组合并成一个有序数组,返回这个有序数组。 步骤二:合并(Merge):合并过程中,需要比较每个子数组的元素并将它们有序地合并成一个新的数组:
可以使用两个指针 i 和 j 分别指向两个子数组的开头,比较它们的元素大小,并将小的元素插入到新的有序数组中。
如果其中一个子数组已经遍历完,就将另一个子数组的剩余部分直接插入到新的有序数组中。
最后返回这个有序数组。
步骤三:归并排序的递归终止条件:
归并排序使用递归算法来实现分解过程,当子数组的长度为1时,认为这个子数组已经有序,递归结束。总体来看,归并排序的基本思路是分治法,分成子问题分别解决,然后将子问题的解合并成整体的解。
归并排序的图解
归并排序的代码
// 定义函数mergeSort,参数是待排序数组arr
function mergeSort(arr: number[]): number[] {
// 如果数组长度小于等于1,则直接返回该数组
if (arr.length === 1) return arr
// 计算中间位置
let mid = Math.floor(arr.length / 2);
// 对左边的数组进行归并排序
let left = mergeSort(arr.slice(0,mid));
// 对右边的数组进行归并排序
let right = mergeSort(arr.slice(mid));/* */
// 合并两个排好序的数组
return merge(left,right);
}
// 定义函数merge,参数是两个排好序的数组left和right
function merge(left: number[], right: number[]): number[] {
let i = 0;
let j = 0;
let result = [];
// 比较两个数组的第一个元素,将较小的放入result数组
while(i<left.length&& j <right.length) {
if (left[i] < right[j]) {
result.push(left[i]);
i ++
} else {
result.push(right[j]);
j++
}
}
// 将没有比较完的剩余元素放入result数组
while(i < left.length) {
result.push(left[i]);
i++
}
while(j < right.length) {
result.push(right[j]);
j++
}
return result
}
// 测试数据
const testArr = [5, 2, 9, 1, 5, 6];
// 调用插入排序函数
const sortedArr = mergeSort(testArr);
// 打印结果
console.log(sortedArr);
归并排序的时间复杂度
复杂度的分析过程:
假设数组长度为 n,需要进行 logn 次归并操作;
每次归并操作需要 O(n) 的时间复杂度;
因此,归并排序的时间复杂度为 O(nlogn)。
最好情况: O(log n)
最好情况下,待排序数组已经是有序的了,那么每个子数组都只需要合并一次,即只需要进行一次归并操作。
因此,此时的时间复杂度是 O(log n)。
最坏情况: O(nlogn)
最坏情况下,待排序数组是逆序的,那么每个子数组都需要进行多次合并。
因此,此时的时间复杂度为 O(nlogn)。
平均情况: O(nlogn)
在平均情况下,我们假设待排序数组中任意两个元素都是等概率出现的。
此时,可以证明归并排序的时间复杂度为 O(nlogn)。
总结
到这里,归并排序的研究就结束啦。在以后的实际场景中,归并排序很适合数据量大的排序,并且归并排序的算法实现也比较简单,归并排序是一种不需要过多研究的算法,适合于所有的排序场景。