排序
- 排序:把某个乱序的数组变成升序或降序的数组 (这里用数组来做举例)
归并排序
- 该排序属于 分治 策略
- 将一个问题分解为两个问题来计算,计算完成之后,就会得到子任务的解,这些解不是最终问题的解,还需要merge起来
- 性能好,火狐的sort方法
- 思路
- 分:把数组分成2半,再递归地对子数组进行"分"操作,直到分成一个个单独的数
- 合:把两个数合并为有序数组,再对有序数组进行合并,直到全部子数组,合并为一个完整数组
- 两个单独的数组成的数组,也是两个有序数组,这两个数组里都只有1个数
- 合这个操作,就是不断合并有序数组
- 如何合并两个有序数组
- 新建一个空数组res, 用于存放最终排序后的数组
- 比较两个有序数组的头部,较小者出队并推出res中
- 如果两个数组还有值,就重复第二步
- 两个数组都空了,合并完成
Typescript 版算法实现
1 ) 方案1
function mergeSort(nums: number[]) {
// 1. 内部核心函数
function msFn(arr:number[]) {
const len: number = arr.length;
if (len < 2) return arr;
const mid: number = Math.floor(len / 2);
const left: number[] = arr.slice(0, mid);
const right: number[] = arr.slice(mid);
const orderLeft: any = msFn(left); // 排好序的左侧
const orderRight: any = msFn(right); // 排好序的右侧
const res: number[] = [];
while(orderLeft.length || orderRight.length) {
if(orderLeft.length && orderRight.length) {
res.push(orderLeft[0] < orderRight[0] ? orderLeft.shift() : orderRight.shift());
} else if (orderLeft.length) {
res.push(orderLeft.shift());
} else if(orderRight.length) {
res.push(orderRight.shift());
}
}
return res;
}
// 2. 开始排序
const result = msFn(nums);
// 3. 替换原数组
nums.splice(0, nums.length, ...result);
}
const list: number[] = [5, 4, 3, 2, 1];
mergeSort(list);
console.log(list); // [1, 2, 3, 4, 5]
- 时间复杂度:O(nlogn)
- 分:每次把数组分成两半,用时 O(logn), log函数用于求2?2^?2? = n, 自然 ? = log2nlog_2 nlog2n, 即 O(logn)
- 注意,凡是分的操作,基本都是logn
- 合:O(n), 一个while循环体
- 分:每次把数组分成两半,用时 O(logn), log函数用于求2?2^?2? = n, 自然 ? = log2nlog_2 nlog2n, 即 O(logn)
- 上述开辟了额外空间, 一般是不推荐的,一般是交换下标 TODO
2 )方案2
function mergeSort(nums: number[]): number[] {
// 内部核心函数,使用索引进行归并排序
function msFn(arr: number[], left: number, right: number): number[] {
if (left >= right) {
// 如果左索引大于或等于右索引,说明子数组只有一个元素或为空,直接返回该子数组(或空数组)
return arr.slice(left, right + 1);
}
const mid = Math.floor((left + right) / 2);
// 递归排序左右子数组,但这里不直接修改原数组,而是返回排序后的新数组片段
const leftSorted = msFn(arr, left, mid);
const rightSorted = msFn(arr, mid + 1, right);
// 合并两个有序数组片段
const merged: number[] = [];
let i = 0, j = 0;
while (i < leftSorted.length && j < rightSorted.length) {
if (leftSorted[i] < rightSorted[j]) {
merged.push(leftSorted[i]);
i++;
} else {
merged.push(rightSorted[j]);
j++;
}
}
// 将剩余的元素(如果有)添加到合并后的数组中
return merged.concat(leftSorted.slice(i)).concat(rightSorted.slice(j));
}
// 开始排序,并获取排序后的新数组
const sortedArray = msFn(nums, 0, nums.length - 1);
// 如果你确实需要将排序结果复制回原数组,可以取消下面一行的注释
// nums.splice(0, nums.length, ...sortedArray);
// 返回排序后的新数组(通常不需要修改原数组)
return sortedArray;
}
Python3 版算法实现
1 ) 方案1
def merge_sort(nums):
def ms_fn(arr):
arr_len = len(arr)
if arr_len < 2:
return arr
mid = arr_len // 2
left = arr[:mid]
right = arr[mid:]
ordered_left = ms_fn(left) # 排好序的左侧
ordered_right = ms_fn(right) # 排好序的右侧
res = []
while ordered_left and ordered_right:
if ordered_left[0] <= ordered_right[0]:
res.append(ordered_left.pop(0))
else:
res.append(ordered_right.pop(0))
# 将剩余元素添加到结果中
res.extend(ordered_left)
res.extend(ordered_right)
return res
nums[:] = ms_fn(nums) # 替换原数组内容
# 测试代码
if __name__ == "__main__":
test_list = [5, 4, 3, 2, 1]
print("原始列表:", test_list)
merge_sort(test_list)
print("排序后列表:", test_list)
2 )方案2
def merge_sort(nums):
# 内部核心函数,使用索引进行归并排序
def ms_fn(arr, left, right):
if left >= right:
# 如果左索引大于或等于右索引,说明子数组只有一个元素或为空,直接返回
return
mid = (left + right) // 2
# 递归排序左右子数组
ms_fn(arr, left, mid)
ms_fn(arr, mid + 1, right)
# 合并两个有序数组片段
merge(arr, left, mid, right)
# 合并两个有序的子数组为一个有序数组
def merge(arr, left, mid, right):
temp = []
i, j = left, mid + 1
# 合并两个有序数组
while i <= mid and j <= right:
if arr[i] <= arr[j]:
temp.append(arr[i])
i += 1
else:
temp.append(arr[j])
j += 1
# 将剩余的元素添加到临时列表中
temp.extend(arr[i:mid + 1])
temp.extend(arr[j:right + 1])
# 将临时列表中的元素复制回原列表
for k in range(len(temp)):
arr[left + k] = temp[k]
# 开始排序
ms_fn(nums, 0, len(nums) - 1)
# 示例用法
if __name__ == "__main__":
# 测试代码
list_ = [5, 4, 3, 2, 1]
merge_sort(list_)
print(list_) # 输出:[1, 2, 3, 4, 5]
Golang 版算法实现
1 ) 方案1
package main
import "fmt"
// mergeSort 对给定的整数切片进行归并排序
func mergeSort(nums []int) {
// 内部核心函数
var msFn func([]int) []int
msFn = func(arr []int) []int {
arrLen := len(arr) // 使用不同的变量名来存储长度
if arrLen < 2 {
return arr
}
mid := arrLen / 2
left := arr[:mid]
right := arr[mid:]
orderLeft := msFn(left) // 排好序的左侧
orderRight := msFn(right) // 排好序的右侧
res := make([]int, 0, arrLen)
for len(orderLeft) > 0 && len(orderRight) > 0 {
if orderLeft[0] < orderRight[0] {
res = append(res, orderLeft[0])
orderLeft = orderLeft[1:]
} else {
res = append(res, orderRight[0])
orderRight = orderRight[1:]
}
}
// 将剩余元素添加到结果中
res = append(res, orderLeft...)
res = append(res, orderRight...)
return res
}
// 开始排序
result := msFn(nums)
// 替换原数组内容
copy(nums, result)
}
func main() {
list := []int{5, 4, 3, 2, 1}
mergeSort(list)
fmt.Println(list) // 输出:[1 2 3 4 5]
}
2 ) 方案2
package main
import "fmt"
// mergeSort 对给定的整数切片进行归并排序
func mergeSort(nums []int) {
// 内部核心函数,使用索引进行归并排序
var msFn func(arr []int, left, right int)
msFn = func(arr []int, left, right int) {
if left >= right {
// 如果左索引大于或等于右索引,说明子数组只有一个元素或为空,直接返回
return
}
mid := (left + right) / 2
// 递归排序左右子数组
msFn(arr, left, mid)
msFn(arr, mid+1, right)
// 合并两个有序数组片段
merge(arr, left, mid, right)
}
// 开始排序
msFn(nums, 0, len(nums)-1)
}
// merge 将两个有序的子数组合并为一个有序数组
func merge(arr []int, left, mid, right int) {
// 创建临时数组存储合并结果
temp := make([]int, right-left+1)
i, j, k := left, mid+1, 0
// 合并两个有序数组
for i <= mid && j <= right {
if arr[i] <= arr[j] {
temp[k] = arr[i]
i++
} else {
temp[k] = arr[j]
j++
}
k++
}
// 将剩余的元素添加到临时数组中
for i <= mid {
temp[k] = arr[i]
i++
k++
}
for j <= right {
temp[k] = arr[j]
j++
k++
}
// 将临时数组中的元素复制回原数组
copy(arr[left:right+1], temp)
}
func main() {
list := []int{5, 4, 3, 2, 1}
mergeSort(list)
fmt.Println(list) // 输出:[1 2 3 4 5]
}
509



被折叠的 条评论
为什么被折叠?



