Datawhale-2020年8月组队学习: 编程实践(LeetCode分类练习)
Task01:分治
编程语言: Python
实现 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 2−2=1/22=1/4=0.25
解题思路(分治)
# 题库难度:中等
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).
给定一个整数数组 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).
给定一个大小为 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).