侧边栏壁纸
博主头像
LittleAO的学习小站 博主等级

在知识的沙漠寻找绿洲

  • 累计撰写 125 篇文章
  • 累计创建 27 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

基本算法之分治

LittleAO
2023-12-10 / 0 评论 / 0 点赞 / 18 阅读 / 0 字
温馨提示:
本文最后更新于2023-12-10,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

基本算法之分治

分而治之就是分治法的基本思想。简单来说,就是将一个大问题分解成若干个小问题,分别对这几个小问题进行求解,一直这样分解下去,直到解决这个大问题。能用分治法的题目需要符合以下两个特征:

  • 平衡子问题,子问题的规模大致相同;
  • 独立子问题:子问题之间相互独立。

在我们之前讲过的二分法就是分治法的一个典型案例。分治法的经典应用有汉诺塔、归并排序,快速排序。

汉诺塔

传说中有三根柱子(A、B、C),开始时所有的盘子都叠在第一根柱子上,盘子的大小都不相同,都是小的在上,大的在下。目标是把所有盘子从第一根(A)柱子移动到第三根(C)柱子上,但是在移动的过程中有两个限制:

  1. 每次只能移动一个盘子。
  2. 大盘子不能放在小盘子上面。

输入要求:两个正整数,第一个正整数​N是要移动的牌子总数,第二个正整数​M是移动步骤的第​M步。
输出要求:共两行,第一行输出第​M步的移动方式,例如:#2: A->B,第二行输出最少的移动步数。

汉诺塔问题可以分解成两个子问题:

  1. ​x杆的​n-1个盘子移动到​y杆,然后把第​n个大盘移动到​z杆;
  2. ​y杆上的​n-1个小盘移动到​z杆。

【汉诺塔小游戏和递归思想-哔哩哔哩】 https://b23.tv/HFzxZQ2

step = 0
m,n = 0,0
def Hanoi(x,y,z,n):
    global step,m
    if n==1:
        if m == step + 1:
            print(f'#{step + 1}: {x}->{z}')
        step += 1
    else:
        Hanoi(x,z,y,n-1)
        if m == step + 1:
            print(f'#{step + 1}: {x}->{z}')
        step += 1
        Hanoi(y,x,z,n-1)

n,m = map(int, input().split())
Hanoi('A','B','C',n)
print(step)

归并排序

如何使用分治法来解决排序问题?根据分治法的分解、解决、合并三个步骤,我们可以这样想:

  • 分解:把无序的数组分成两部分,对于每一个部分再分解成更小的两个部分;
  • 解决:分解到不能再分解,然后进行排序;
  • 合并:排完序的数组两两合并。

归并排序就是体现分治法思想例子,
1702198120018.png

值得注意的是,在“解决”这一步骤中,由于归并排序的子序列只包含一个数,其实不用排序。而最重要的时合并的步骤,合并的步骤需要进行排序。

分析得到归并排序的时间复杂度是​O(n\log_2n),空间复杂度为​O(n)

def merge_sort(l:list):
    if len(l) > 1:
        mid = len(l) // 2; # 找到中间的索引
        left = l[0:mid] # 分为左右两个数组
        right = l[mid:]
        merge_sort(left)    # 分割成更小的数组
        merge_sort(right)

        # 合并
        i,j,k = 0,0,0 # 双指针法合并
        while i < len(left) and j < len(right):
            if left[i] > right[j]:
                l[k] = right[j]
                k += 1
                j += 1
            else:
                l[k] = left[i]
                k += 1
                i += 1
        # 将剩余数组合并
        while i<len(left):
            l[k] = left[i]
            k += 1
            i += 1
        while j<len(right):
            l[k] = right[j]
            k += 1
            j += 1
    return l
      
l = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
print(merge_sort(l))

在大多数需要排序的情况下,我们只需要使用自带的排序函数就行,但是有时候我们需要给排序函数加上一些自己的功能,这种情况下就需要我们手写代码。

快速排序

快速排序的思路就是把序列分成左、右两个部分,使左边所有的数都比右边的数小,递归这个过程,直到不能再分为止。

下图介绍了一种很容易进行划分的方法:

1702204480275.png

对于复杂度:快速排序的总复杂度是​O(n\log_2n)。在极端情况下,左部分只有一个数,剩下的都在右部分,此时的总复杂度为​O(n^2)。所以,快速排序是不稳定的。

在Python中,划分的代码编写起来十分容易,这里用第一个元素作为基准元素:

def quicksort(arr):
    if len(arr) <= 1:
        return arr
    else:
        pivot = arr[0]
        less_than_pivot = [x for x in arr[1:] if x <= pivot]
        greater_than_pivot = [x for x in arr[1:] if x > pivot]
        return quicksort(less_than_pivot) + [pivot] + quicksort(greater_than_pivot)


# 示例用法
my_list = [3, 6, 8, 10, 1, 2, 1]
sorted_list = quicksort(my_list)
print(sorted_list)
0

评论区