Leetcode刷题全记录,每题都有长进(medium 271-300)

279 完全平方数

Leetcode刷题全记录,每题都有长进(medium 271-300)_第1张图片
是一道dp问题.

  • 状态dp[i]表示组成和为i最少的完全平方数的个数。
  • 状态转移方程是dp[i] = min(dp[i-j]+dp[j], dp[i])
    优化就在于如何合适的选择j的范围,最暴力的方法是j in (1, i),稍作优化就是j in (1, i//2),还可继续优化dp[i] = min(dp[i], 1+dp[i-j*j])其中j是完全平方数的因子。
class Solution(object):
    def numSquares(self, n):
        """
        :type n: int
        :rtype: int
        """
        dp = [float('inf') for i in range(n+1)]
        dp[0] = 1
        base = 1
        for i in range(1, n+1):
            if base*base == i:
                dp[i] = 1
                base += 1
                continue
            for j in range(1, base):
                dp[i] = min(dp[i], 1+dp[i-j*j])
        #print(dp)
        return dp[-1]

时间复杂度 O ( n n ) O(n\sqrt{n}) O(nn )

287 寻找重复数字

Leetcode刷题全记录,每题都有长进(medium 271-300)_第2张图片
题目是一道搜索类型的题目,但是给出了两个相对苛刻的条件,一个是空间复杂度 O ( 1 ) O(1) O(1),一个是时间复杂度小于 O ( n 2 ) O(n^2) O(n2)。如果不考虑空间复杂度的要求,可以采用哈希表判重时间复杂度只需要 O ( n ) O(n) O(n)。在不能够更改数组的前提下,应该想到采用基于抽屉原理的二分法进行搜索的方法。抽屉原理在之前的题目用出现过,采用这种方法的特点在于题目中提到只有一个重复数字

二分法搜索的具体细节可以参见这篇文章

这道题目搜索的范围是[1, n]我们需要找到重复的那个数字。基于抽屉原理,小于k的元素个数最多等于k个。否则就说明存在重复。基于此我们判断k值的选择是否合理。

特别需要分析k == mid的情况,也就是小于k的数字个数正好k个。此时说明重复的数字大小应该还是大于k的,因此应该移动左边界,返回值最终也应返回左边界。

class Solution(object):
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        right_index = len(nums)-1
        left_index = 1
        while left_index<=right_index:
            mid = left_index + (right_index-left_index)//2
            cur = 0
            for i in nums:
                if mid >= i:
                    cur += 1
            if cur>mid:
                right_index = mid - 1
            elif cur<mid:
                left_index = mid + 1
            elif cur == mid:
                left_index = mid + 1
        return left_index

这道题目还有一个巧妙的方法完成,时间复杂度是 O ( n ) O(n) O(n)。也就是将数组作为一个链表进行跳转处理。每次都跳转到当前数值对应的索引值的位置。
因为数组的长度为n+1,但是不存在n+1这个数值,因此无法结束循环,一直存在环。我们只需要判断是否存在环且返回环的起点坐标即可。

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        ## 一种复杂度为n的方法
        fast = 0
        slow = 0
        while fast!=slow or slow == 0:
            fast = nums[nums[fast]]
            slow = nums[slow]
        begin = 0
        while slow!=begin or begin == 0:
            slow = nums[slow]
            begin = nums[begin]
        return begin

300 最长上升子序列

Leetcode刷题全记录,每题都有长进(medium 271-300)_第3张图片看到只需要返回最长子序列的长度,想到应该使用dp方法。

  • 状态设定:dp[i]i为结尾得到的最长上升子序列长度
  • 状态转移方程:dp[i] = max(dp[i], dp[j]+1)其中j的范围从[0, i)
  • 初始状态:dp[0] = 1
class Solution(object):
    def lengthOfLIS(self, nums):
        if len(nums) == 0:
            return 0
        r = []
        for i in range(0, len(nums)):
            r.append(1)
            for j in range(0, i):
                if nums[i] > nums[j]:
                    r[i] = max(r[i], r[j]+1)
        return max(r)

另外一种对dp算法的优化是修改策略,另外维护一个最小的升序序列的尾巴tail。借助二分法可以时间更快。

你可能感兴趣的:(进军medium,算法,python,leetcode)