Datawhale第十六周组队学习-Leetcode(分治)

中心思想

分治算法的中心思想是将原问题递归地分成若干个子问题,直到子问题满足边界条件,停止递归。将子问题逐个击破,将已经解决地子问题合并,最后,算法会层层合并得到答案。

分治步骤及递归三定律

分治步骤

分:递归地将问题分解为各个的子问题(性质相同的、相互独立的子问题);
治:将这些规模更小的子问题逐个击破;
合:将已解决的子问题逐层合并,最终得出原问题的解;

递归三定律

具体地,可以以下步骤进行
1.基本结束条件:递归算法必须有一个基本结束条件,最小规模的问题可以直接解决;
2.减小问题规模:递归算法必须能改变状态想基本结束条件演进;
3.调用自身:递归算法必须能够调用自身,解决减小规模的相同问题,同时需要对相同问题归并。

以本周分治的三个题目为例:

第一题: Leetcode 169 多数元素
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入: [3,2,3]
输出: 3
示例 2:

输入: [2,2,1,1,1,2,2]
输出: 2

牢记分治策略以及递归三定律,具体应在题目中:
1.基本结束条件:如果数组中只有一个元素,多数元素肯定是该元素;
2.减小问题规模:二分策略,将目标缩小;
3.调用自身:原数组被分成了多个不同的数组,当子问题无法进行拆分的时候,开始归并问题,具体过程如下:

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
   		#当列表为空时 
        if nums == None:  
            return None
        #基本结束条件:数组里只有一个元素
        if len(nums) == 1:     
            return nums[0]
		#缩小规模:将原来数组二分为两个数组
		#分别调用自身,将一个父问题分为两个子问题
        left = self.majorityElement(nums[:len(nums)//2]) 
        right = self.majorityElement(nums[len(nums)//2:])  

        #对划分的两个子问题归并,归并结果就是原来父问题的结果
        if left == right:
            return left
        if nums.count(left) > nums.count(right):
            return left
        else:
            return right

重点在于对归并理解,如果对归并的过程比较模糊,可以手动写一下代码的执行过程,这样有利于理解整个分治的过程,从而加深对归并过程的理解;
时间复杂度:O(nlogn);
空间复杂度:O(logn)
另外,本题其实还有其他的解法:如哈希表(时间复杂度和空间复杂度均为O(N))等,效率高于分治这里不做讨论;

第二题: Leetcode 53.最大子序和.
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
    	#基本结束条件:当数组长度为1
        if len(nums) == 1:
            return nums[0]
		#减小规模:将元素组二分为两个数组
		#调用自身:对两个子问题求子序列
        left = self.maxSubArray(nums[:len(nums)//2])
        right = self.maxSubArray(nums[len(nums)//2:])
        
		#对子问题进行归并
        max_l = nums[len(nums)//2 - 1]
        tmp_l = 0
        for i in range(len(nums)//2 - 1, -1, -1):
            tmp_l = tmp_l + nums[i]
            max_l = max(max_l, tmp_l)

        max_r = nums[len(nums)//2]
        tmp_r = 0
        for i in range(len(nums)//2, len(nums)):
            tmp_r = tmp_r + nums[i]
            max_r = max(max_r, tmp_r)

        return max(left, right, max_l + max_r)

时间复杂度:渐进时间复杂度为 O(n)。
空间复杂度:递归会使用 O(logn) 的栈空间,故渐进空间复杂度为 O(logn)。
和这个题目类似的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值