2021-08-21leetcode刷题

背包问题进阶版,商品可无限选择直至选择到某固定金额

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

使用深度优先搜索直接超时

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #使用深度优先搜索
        if amount==0:
            return 0
        min_cnt,n=[amount+1],len(coins)
        def dfs(sum_,i,cnt):
            if sum_==amount:
                min_cnt[0]=min(min_cnt[0],cnt)
                return
            if sum_>amount or i>=n:
                return
            dfs(sum_,i+1,cnt)
            if sum_+coins[i]<=amount:
                dfs(sum_+coins[i],i,cnt+1)
        dfs(0,0,0)
        return min_cnt[0] if min_cnt[0]!=amount+1 else -1

感觉自己厉害的一刻

本想半小时之内解决,从0开始思考到解决只用了大概5分钟,而且一次通过

给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
示例:
输入: "sea", "eat"
输出: 2
解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/delete-operation-for-two-strings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        #二维规划
        m,n=len(word1),len(word2)
        dp=[[0]*(n+1) for _ in range(m+1)]
        for i in range(1,n+1):
            dp[0][i]=i
        for j in range(1,m+1):
            dp[j][0]=j
        for i in range(1,m+1):
            for j in range(1,n+1):
                if word1[i-1]==word2[j-1]:
                    dp[i][j]=dp[i-1][j-1]
                else:
                    dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1
        return dp[-1][-1]


高效性之原因疑惑

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        ans=[]
        if not root:
            return []
        def dfs(root,sum_,per):
            if sum_==targetSum and not root.left and not root.right:
                ans.append([i for i in per])
                return
            if root.left:
                per.append(root.left.val)
                dfs(root.left,sum_+root.left.val,per)
                per.pop()
            if root.right:
                per.append(root.right.val)
                dfs(root.right,sum_+root.right.val,per)
                per.pop()
        dfs(root,root.val,[root.val])
        return ans
        
        return []

数据结构知识点:

搜索二叉树不一定是平衡二叉树。

搜索二叉树最重要的性质:中序遍历是非递减序列

主要算法寻找该子树中有没有含有给出的节点

二叉树两节点的最近公共祖先

  • 如果含有,返回True,否则返回False
  • 最近公共祖先是
    1.该节点得左右子树都返回True
    2.左右子树有一个返回True,而节点本身是给出的两个节点中的一个。
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        ans=[]
        def judge(root):
            if not root :
                return False
            a,b=False,False
            if root.left:
                a=judge(root.left)
            if root.right:
                b=judge(root.right)
            if a and b or   (root==p or root==q)  and  (a or b):
                #print('hi')
                ans.append(root)
                
            else:
                return a or b or root==p or root==q
        judge(root)
        return ans[0]

算法之一大选择,能够记忆化的尽量浪费一点空间以换取时间的高效。

实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:
BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
int next()将指针向右移动,然后返回指针处的数字。
注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。
你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。
示例:
输入
["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
输出
[null, 3, 7, true, 9, true, 15, true, 20, false]
解释
BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
bSTIterator.next(); // 返回 3
bSTIterator.next(); // 返回 7
bSTIterator.hasNext(); // 返回 True
bSTIterator.next(); // 返回 9
bSTIterator.hasNext(); // 返回 True
bSTIterator.next(); // 返回 15
bSTIterator.hasNext(); // 返回 True
bSTIterator.next(); // 返回 20
bSTIterator.hasNext(); // 返回 False

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-search-tree-iterator
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class BSTIterator:

    def __init__(self, root: TreeNode):
        '''
        self.last=root
        while self.last.left:
            self.last=self.last.left
        '''
        self.deque=collections.deque()
        def inorder(root):
            if not root:
                return
            inorder(root.left)
            self.deque.append(root)
            inorder(root.right)
        inorder(root)


    def next(self) -> int:
        t=self.deque.popleft()
        return t.val


    def hasNext(self) -> bool:
        return len(self.deque)!=0

你可以设计一个满足下述条件的解决方案吗?next() 和 hasNext() 操作均摊时间复杂度为 O(1) ,并使用 O(h) 内存。其中 h 是树的高度。

该要求应该使用栈重写

class BSTIterator {
private:
    TreeNode* cur;
    stack stk;
public:
    BSTIterator(TreeNode* root): cur(root) {}
    
    int next() {
        while (cur != nullptr) {
            stk.push(cur);
            cur = cur->left;
        }
        cur = stk.top();
        stk.pop();
        int ret = cur->val;
        cur = cur->right;
        return ret;
    }
    
    bool hasNext() {
        return cur != nullptr || !stk.empty();
    }
};


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/binary-search-tree-iterator/solution/er-cha-sou-suo-shu-die-dai-qi-by-leetcod-4y0y/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

python的位运算

字典的删除是pop(key),集合的删除是remove()

简单题换个方法可能要重新考虑边界

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        right,n,i=nums[0],len(nums),0
        while i=n-1:
                return True
            right=max(right,nums[i]+i)
        return True if right>=n-1 else False
        '''
        is_tra_from_fir=[False]*len(nums)
        is_tra_from_fir[0]=True
        for i in range(len(nums)):
            #flag=False
            for j in range(1,nums[i]+1):
                if i+j
算法改善

\

在for循环中已经生成了遍历序列,不受for语句内部影响

小变量问题,不应使用随机数字的sorted,边遍历边移动数字

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-colors
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        #O(N)>Sort O(N^2)
        n,ptr=len(nums),0
        for i in range(n):
            if nums[i]==0:
                nums[i],nums[ptr]=nums[ptr],nums[i]
                ptr+=1
        for i in range(ptr,n):
            if nums[i]==1:
                nums[i],nums[ptr]=nums[ptr],nums[i]
                ptr+=1

1976. 到达目的地的方案数

你在一个城市里,城市由 n 个路口组成,路口编号为 0 到 n - 1 ,某些路口之间有 双向 道路。输入保证你可以从任意路口出发到达其他任意路口,且任意两个路口之间最多有一条路。
给你一个整数 n 和二维整数数组 roads ,其中 roads[i] = [ui, vi, timei] 表示在路口 ui 和 vi 之间有一条需要花费 timei 时间才能通过的道路。你想知道花费 最少时间 从路口 0 出发到达路口 n - 1 的方案数。
请返回花费 最少时间 到达目的地的 路径数目 。由于答案可能很大,将结果对 109 + 7 取余 后返回。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-ways-to-arrive-at-destination
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

一模一样的算法,python加cache与不加cache的区别

关于python中cache的用法

涉及最短路,最短路之后的路径选择dfs,路径选择条数的动态规划,记忆化搜索cache

class Solution:
    def countPaths(self, n: int, roads: List[List[int]]) -> int:
        mod = 10**9 + 7
        
        dist = [[float("inf")] * n for _ in range(n)]
        for i in range(n):
            dist[i][i] = 0
        
        for x, y, z in roads:
            dist[x][y] = dist[y][x] = z
        
        # Floyd 算法求解最短路
        # 完成后,dist[0][i] 即为正文部分的 dist[i]
        # for k in range(n):
        #     for i in range(n):
        #         for j in range(n):
        #             dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

        # Dijkstra 算法求解最短路
        # 完成后,dist[0][i] 即为正文部分的 dist[i]
        seen = set()
        for _ in range(n - 1):
            u = None
            for i in range(n):
                if i not in seen and (not u or dist[0][i] < dist[0][u]):
                    u = i
            seen.add(u)
            for i in range(n):
                dist[0][i] = min(dist[0][i], dist[0][u] + dist[u][i])

        # 构造图 G
        g = defaultdict(list)
        for x, y, z in roads:
            if dist[0][y] - dist[0][x] == z:
                g[x].append(y)
            elif dist[0][x] - dist[0][y] == z:
                g[y].append(x)

        @cache
        #有点类似记忆化搜索了
        def dfs(u: int) -> int:
            if u == n - 1:
                return 1

            ret = 0
            for v in g[u]:
                ret += dfs(v)
            return ret % mod
        
        ans = dfs(0)
        dfs.cache_clear()
        return ans

python中defaultdict用法详解

刷题降低时间复杂度

1.数学
例如和相等的最大连乘、1014. 最佳观光组合
2.数位运算,快速幂,模式匹配,批量加和(状态转移),排列组合,全排列。。。

全排列

存在一个未知数组需要你进行还原,给你一个整数 n 表示该数组的长度。另给你一个数组 sums ,由未知数组中全部 2n 个 子集的和 组成(子集中的元素没有特定的顺序)。
返回一个长度为 n 的数组 ans 表示还原得到的未知数组。如果存在 多种 答案,只需返回其中 任意一个 。
如果可以由数组 arr 删除部分元素(也可能不删除或全删除)得到数组 sub ,那么数组 sub 就是数组 arr 的一个 子集 。sub 的元素之和就是 arr 的一个 子集的和 。一个空数组的元素之和为 0 。
注意:生成的测试用例将保证至少存在一个正确答案。
示例 1:
输入:n = 3, sums = [-3,-2,-1,0,0,1,2,3]
输出:[1,2,-3]
解释:[1,2,-3] 能够满足给出的子集的和:
[]:和是 0
[1]:和是 1
[2]:和是 2
[1,2]:和是 3
[-3]:和是 -3
[1,-3]:和是 -2
[2,-3]:和是 -1
[1,2,-3]:和是 0
注意,[1,2,-3] 的任何排列和 [-1,-2,3] 的任何排列都会被视作正确答案。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-array-given-subset-sums
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution:
   def recoverArray(self, n: int, sums: List[int]) -> List[int]:
       BIAS = -min(sums)
       
       st = SortedList()
       for x in sums:
           st.add(x+BIAS)
       st.discard(st[0])
       ans = []
       ans.append(st[0])
       
       for i in range(1, n):
           for msk in range(1<>(i-1)) & 1: # 避免重复删除(保证ans最新的一位被取到)
                   sm = 0
                   for j in range(i):
                       if (msk>>j) & 1:
                           sm += ans[j]
                   st.discard(sm)
           ans.append(st[0])
           
       for i in range(1<>j) & 1:
                   sm += ans[j]
           if sm == BIAS:
               for j in range(n):
                   if (i>>j) & 1:
                       ans[j] = -ans[j]
               break
       return ans

代码顺序较大影响代码高效性

def productExceptSelf(self, nums: List[int]) -> List[int]:
        #设计一个O(1)时间复杂度
        ans=[1]
        L=1
        for i in range(1,len(nums)):
            L*=nums[i-1]#1
            ans.append(L)#2
            
        R=1
        for i in range(len(nums)-1,-1,-1):
            ans[i]*=R#3
            R*=nums[i]#4
        return ans

调整了一下#1和#2的顺序,提高了8ms


相同算法书写时的严谨性差,导致多处错误

给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。
返回规则如下:
如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。
示例 1:
输入:version1 = "1.01", version2 = "1.001"
输出:0
解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/compare-version-numbers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution:
    def compareVersion(self, version1: str, version2: str) -> int:
        l1=[int(i) for i in version1.split('.')]
        l2=[int(i) for i in version2.split('.')]
        t=0
        n,m=len(l1),len(l2)
        for i in range(n-1,-1,-1):
            if l1[i]==0:
                l1.pop()
                t+=1#warning
            else:
                n-=t
                t=0
                break#error1
        for i in range(m-1,-1,-1):
            if l2[i]==0:
                l2.pop()
                t+=1
            else:
                m-=t
                break
        ans=0
        #print(l1)
        #print(l2)
        for i in range(min(n,m)):
            if l1[i]>l2[i]:
                return 1
            elif l1[i]m:
            return 1
        elif n

其中#error1是因为当v1字符串全是0时,t记录的值没有去除。而且如果是0版本,该逻辑会清理全是0的字符串,不太合理
为了上一步不清理到第一位,应重写else的判断,在if中使用n-=1,否则退出,代码更严谨,且不容易出错。

同上一个错误,当code没有耐心时,简单题也会一直错

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-consecutive-sequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我还是有一定实力的嘛

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-duplicate-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二进制编码方式

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        #都是整数
        t=1<>i&1:
                return i
            else:
                t|=1<

你可能感兴趣的:(2021-08-21leetcode刷题)