Leetcode分治

Datawhale-2020年8月组队学习: 编程实践(LeetCode分类练习)
Task01:分治
编程语言: Python

1. LeetCode 50. Pow(x, n)

实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例1:

输入: 2.00000, 10
输出: 1024.00000

示例2:

输入: 2.10000, 3
输出: 9.26100

示例3:

输入: 2.00000, -2
输出: 0.25000
解释: 2 − 2 = 1 / 2 2 = 1 / 4 = 0.25 2^{-2} = 1 / 2^{2} = 1/4 = 0.25 22=1/22=1/4=0.25

解题思路(分治)

  • 当计算 x n x^{n} xn时,我们可以先递归的计算 y = x ⌊ n / 2 ⌋ y=x^{\lfloor n/2\rfloor} y=xn/2
  • 根据递归的结果计算,如果 n n n为偶数,那么 x n = y 2 x^{n}=y^{2} xn=y2;如果 n n n为奇数,那么 x n = y 2 ∗ x x^{n}=y^{2}*x xn=y2x
  • 递归的边界为 n = 0 n=0 n=0,任意数的0次方均为1
# 题库难度:中等
class Solution(object):
    def myPow(self, x, n):
        """
        :type x: float
        :type n: int
        :rtype: float
        """
        # 设计递归子模块
        def multi(N):
            if N == 0:
                return 1.0
            y = multi(N//2)
            return y * y if N % 2 == 0 else y * y * x

        return multi(n) if n >= 0 else 1.0 / multi(-n)

时间复杂度
O ( log ⁡ n ) O(\log n) O(logn),递归层数。

空间复杂度
O ( log ⁡ n ) O(\log n) O(logn),分治算法没有分配额外数组空间,但在递归过程中使用了额外的栈空间。算法每次将数组从中间分成两部分,所以数组长度变为 1 之前需要进行 O ( log ⁡ n ) O(\log n) O(logn) 次递归,即空间复杂度为 O ( log ⁡ n ) O(\log n) O(logn).

2. LeetCode 53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:

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

解题思路(分治)
可以整个数组的最大子序和分解为多个子数组的最大子序和。最大子序和的子序要么在左半数组,要么在右半数组,或穿过中间。因此,通过递归求解三个数组的最大子序和,能够确定整个数组的最大子序和。

# 题库难度:简单
class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        length = len(nums)

        # 子数组中最少包含一个元素
        # if length == 0:
        #     return null
        if length == 1:
            return nums[0]
        else:
            # 递归计算左边字串的最大子序和
            left_max = self.maxSubArray(nums[0 : len(nums)//2])
            # 递归计算右边子串的最大子序和
            right_max = self.maxSubArray(nums[len(nums)//2 : len(nums)])

        # 计算中间的最大子序和,从中间开始计算
        # 往左最大
        l_max = nums[len(nums)//2 -1]
        tmp = 0
        for i in range(len(nums)//2 - 1, -1, -1):
            tmp += nums[i]
            l_max = max(tmp, l_max)

        # 往右最大
        r_max = nums[len(nums)//2]
        tmp = 0
        for i in range(len(nums)//2, len(nums)):
            tmp += nums[i]
            r_max = max(tmp, r_max)
        
        # 返回左中右中的最大值
        return max(left_max, l_max+r_max, right_max)

时间复杂度
O ( n ) O(n) O(n),会遍历所有节点。

空间复杂度
O ( log ⁡ n ) O(\log n) O(logn),分治算法没有分配额外数组空间,但在递归过程中使用了额外的栈空间。算法每次将数组从中间分成两部分,所以数组长度变为 1 之前需要进行 O ( log ⁡ n ) O(\log n) O(logn) 次递归,即空间复杂度为 O ( log ⁡ n ) O(\log n) O(logn).

3. LeetCode 169. 多数元素

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素
示例1:

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

示例2:

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

解题思路(分治)
将求解数组多数元素分解为求子数组的多数元素,常用二分的方法。将数组分成左右两部分,分别求两部分的多数元素,若相同则返回该元素,否则分别对两元素进行计数,得出较多元素,并返回。最后,通过递归对整个问题进行求解。

# 题库难度:简单
class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        def subMajorityElement(low, high):
            # 左右下标相同
            if low == high:
                return nums[low]

            # 确定中间下表,并分治递归求解
            mid = (high-low)//2 + low
            left = subMajorityElement(low, mid)
            right = subMajorityElement(mid+1, high)

            # 当左边和右边众数相同时
            if left == right:
                return left

            # 当左边和右边众数不同时,对不同众数进行计数,返回最多的数
            left_count = sum(1 for i in range(low, high+1) if nums[i]==left)
            right_count = sum(1 for i in range(low, high+1) if nums[i]==right)

            return left if left_count > right_count else right

        return subMajorityElement(0, len(nums)-1)

时间复杂度
O ( n log ⁡ n ) O(n\log n) O(nlogn),算法会求解两个长度为 n 2 \frac{n}{2} 2n的子问题,并作两遍长度为 n n n的线性扫描。则有 T ( n ) = 2 T ( n 2 ) + 2 n T(n)=2T(\frac{n}{2})+2n T(n)=2T(2n)+2n

空间复杂度
O ( log ⁡ n ) O(\log n) O(logn),分治算法没有分配额外数组空间,但在递归过程中使用了额外的栈空间。算法每次将数组从中间分成两部分,所以数组长度变为 1 之前需要进行 O ( log ⁡ n ) O(\log n) O(logn) 次递归,即空间复杂度为 O ( log ⁡ n ) O(\log n) O(logn).

你可能感兴趣的:(算法)