分治算法其实很有趣(下)

简介: 分治法可能是最著名的通用算法设计技术了。虽然它的名气可能和它那好记的名字有关,但它的确是当之无愧:因为很多有效的算法实际上就是这个通用算法的特殊实现。

分治算法的应用


归并排序

归并排序是成功应用分治策略的一个完美例子,对于给定一组序列含 n 个数组 A[0..n-1],归并排序将其一分为二:A[0..n/2-1]A[n/2..n-1],并对每个子数组递归排序,然后将这两个排好序的子数组合并为一个有序数组。

image.png

Go 代码如下:

func mergeSort(items []int) []int {
    if len(items) < 2 {
        return items
    }
    first := mergeSort(items[:len(items)/2])
    second := mergeSort(items[len(items)/2:])
    return merge(first, second)
}

让我们先编写 mergeSort() 函数。它是一个递归函数,这意味着它会调用自己,在这种情况下,它实际上会调用自己两次。 mergeSort 函数的要点是将数组分成大致相等的两个部分,在这些部分上调用自身,然后调用 merge() 将这些部分重新组合在一起。


merge() 函数用于将两个排序列表合并回一个排序列表,这是真正发生“魔术”的地方。在递归的最低级别,两个“排序”列表的长度均为 1。这些单元素列表将合并为长度为 2 的排序列表,我们可以从那里构建。然后写出归并的 merge() 函数:

func merge(a []int, b []int) []int {
    final := []int{}
    i := 0
    j := 0
    for i < len(a) && j < len(b) {
        if a[i] < b[j] {
            final = append(final, a[i])
            i++
        } else {
            final = append(final, b[j])
            j++
        }
    }
    for ; i < len(a); i++ {
        final = append(final, a[i])
    }
    for ; j < len(b); j++ {
        final = append(final, b[j])
    }
    return final
}


然后在 main() 函数中验证:

func main() {
  unsorted := []int{8, 3, 2, 9, 7, 1, 5, 4}
  sorted := mergeSort(unsorted)
  fmt.Println(sorted)
  // sorted = [1, 2, 3, 4, 5, 6, 7, 8, 9]
}


image.png


二分查找

目的:在一个含有 N 个元素的有序数组中有效地的定位目标值。


思想:假设在有序数组 arr 中查找元素 k,返回 k 所在的下标(索引值)。设 arr[low,high] 是当前的查找区间,确定该区间的中间位置 mid=⌊(low+high)//2⌋ ,然后将待查的 k 值与 arr[mid] 比较:

  • k==arr[mid],说明找到 k,则查找成功并且终止,则找到该元素;
  • k<arr[mid],根据数组有序的前提,目标值 k 在左边的区域中,索引的范围改为 [low, mid-1]
  • k>arr[mid],目标值在右边的区域中,查找索引范围改为 [mid+1, high]


示例:

输入    nums = [-1,0,3,5,9,12], target = 9
输出    4
解释    9 出现在 nums 中并且下标为 4


Python 代码如下:

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        # 在区间 [left, right] 内查找 target
        while left < right:
            # 取区间中间节点
            mid = left + (right - left) // 2
            # nums[mid] 小于目标值,排除掉不可能区间 [left, mid],在 [mid + 1, right] 中继续搜索
            if nums[mid] < target:
                left = mid + 1 
            # nums[mid] 大于等于目标值,目标元素可能在 [left, mid] 中,在 [left, mid] 中继续搜索
            else:
                right = mid
        # 判断区间剩余元素是否为目标元素,不是则返回 -1
        return left if nums[left] == target else -1

其他应用


快速排序也是另一种基于分治算法的重要排序算法,不像归并排序是按照元素数组中的位置对它们进行划分,快速排序是按照元素的值对它们进行划分。


二叉树遍历中前序、中序和后序遍历都可以用到分治,因为二叉树本身的定义就是将同样类型的树分为两个更小的组成部分——左子树和右子树。


其他如大整数乘法和 Strassen 矩阵乘法也可以巧妙的运用分治算法来获得更好的渐近效率,感兴趣的读者可以继续探索。

相关文章
|
2月前
|
人工智能 自然语言处理 算法
当prompt策略遇上分治算法,南加大、微软让大模型炼成“火眼金睛”
【2月更文挑战第24天】当prompt策略遇上分治算法,南加大、微软让大模型炼成“火眼金睛”
26 2
当prompt策略遇上分治算法,南加大、微软让大模型炼成“火眼金睛”
|
5月前
|
算法
2017级《算法设计与分析》--实验1--分治算法-骨牌铺方格
2017级《算法设计与分析》--实验1--分治算法-骨牌铺方格
|
5月前
|
算法
2017级《算法设计与分析》--实验1--分治算法
2017级《算法设计与分析》--实验1--分治算法
|
3月前
|
算法
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-分治算法
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-分治算法
20 0
|
6月前
|
算法 JavaScript
分治算法
分治算法
51 0
|
7月前
|
人工智能 算法
【算法分析与设计】递归与分治策略(二)
【算法分析与设计】递归与分治策略
|
2月前
|
存储 缓存 算法
【数据结构与算法】【小白也能学的数据结构与算法】递归 分治 迭代 动态规划 无从下手?一文通!!!
【数据结构与算法】【小白也能学的数据结构与算法】递归 分治 迭代 动态规划 无从下手?一文通!!!
|
7月前
|
算法
算法:分治思想处理归并递归问题
算法:分治思想处理归并递归问题
|
3天前
|
并行计算 算法 索引
数据结构与算法 分治
数据结构与算法 分治
6 0
|
15天前
|
算法 JavaScript
算法(分治、贪心、dp、回溯、分支限界)总结
算法(分治、贪心、dp、回溯、分支限界)总结