分治策略
将问题分解成更小的子问题,然后分别解决这些子问题,最后将子问题的解合并起来得到原问题的解
分治策略具备以下特性:
- 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
- 解决:若子问题规模较小而容易被解决则直接解决,否则递归地解决各个子问题
- 合并:将各个子问题的解合并为原问题的解
递归
在函数或对象的定义中使用自身的特性或方法,通常用于解决可以被分解为相似但规模较小的子问题的问题
递归通常具备以下特性:
- 基本情况(Base Case):停止条件或边界条件。基本情况是指递归过程中最简单的情况,它直接返回结果而不再进行递归调用。没有基本情况或基本情况不正确会导致递归无限循环或栈溢出等问题
- 递归步骤(Recursive Step):描述问题如何分解为规模更小、与原问题相似的子问题的部分。在递归步骤中,问题会通过调用自身来解决规模较小的同类问题,直到达到基本情况为止
# python
# fibonacci数列的递归实现
def fib(n):
# 基本情况: 当输入小于等于1时,直接返回
if n <= 1:
return n
# 递归步骤: 将问题拆分为两个原输入更小的子问题
else:
return fib(n-1) + fib(n-2)
// golang
func fib(n int) int {
if n <= 1 {
return n
} else {
return fib(n-1) + fib(n-2)
}
}
归并算法
将数组分成两个子数组,分别对子数组进行排序,然后将两个已排序的子数组合并成一个更大的有序数组来实现排序
算法步骤
经典的分治思想及递归使用。
将无序数组递归的以中心分解为若干个子数组(默认只有一个元素的数组是排序好的),再将得到的已排序的子数组进行合并,直到所有元素都在正确位置
图解
代码实现
# python
def merge_sort(array):
# 将拆分的两数组进行排序汇总成一个
def merge(l, r):
l_idx = r_idx = 0
res = []
while l_idx < len(l) and r_idx < len(r):
if l[l_idx] <= r[r_idx]:
res.append(l[l_idx])
l_idx += 1
else:
res.append(r[r_idx])
r_idx += 1
res += l[l_idx:]
res += r[r_idx:]
return res
# 基本情况;子数组中只有一个元素
if len(array) < 2:
return array
mid = len(array) // 2
# 递归步骤:将当前分割的数组做为输入再次进行拆分
l_part = merge_sort(array[:mid])
r_part = merge_sort(array[mid:])
return merge(l_part, r_part)
// golang
func merge_sort(array []int) []int {
if len(array) < 2 {
return array
}
merge := func(l []int, r []int) []int {
l_idx := 0
r_idx := 0
res := []int{}
for l_idx < len(l) && r_idx < len(r) {
if l[l_idx] <= r[r_idx] {
res = append(res, l[l_idx])
l_idx ++
} else {
res = append(res, r[r_idx])
r_idx ++
}
}
res = append(res, l[l_idx:]...)
res = append(res, r[r_idx:]...)
return res
}
mid := len(array) / 2
l_part := merge_sort(array[:mid])
r_part := merge_sort(array[mid:])
return merge(l_part, r_part)
}
算法特点
- 稳定性:归并排序是一种稳定的排序算法,稳定性是指在排序过程中相同元素的相对位置不会改变。在归并排序中,当两个元素值相同时,原始序列中靠前的元素会优先进入合并后的有序序列
- 时间复杂度:归并排序的时间复杂度为 𝑂(𝑛log𝑛), 其中 𝑛 是待排序序列的长度。这是因为归并排序每次将序列分成两半,分别排序,然后将排好序的子序列合并起来。分割过程需要 𝑂(log𝑛) 次,每次合并的复杂度为 𝑂(𝑛),所以总的时间复杂度为 𝑂(𝑛log𝑛)
- 空间复杂度:归并排序的空间复杂度为 𝑂(𝑛),其中 𝑛 是待排序序列的长度。归并排序需要额外的空间来存储临时的合并结果,通常需要一个与原始序列相同大小的辅助数组。因此,归并排序不是原地排序算法,空间复杂度相对较高
- 适用性:归并排序适用于各种数据类型和数据量较大的情况,特别适合用于链表排序
- 不适合小规模数据:尽管归并排序的时间复杂度较优,但由于其需要额外的空间开销和递归调用,对于小规模的数据排序可能比较低效。在数据量较小时,插入排序等简单排序算法可能更为适合