leetcode刷题 307~
题目307题
区域和检索
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。
思路
线段树:
实现
class Node: def __init__(self, sum,pos_left,pos_right,left=None,right=None): self.sum = sum self.pos_left = pos_left self.pos_right = pos_right self.left = left self.right = right def sumRange(self, i, j): if self.pos_left ==i and self.pos_right == j: return self.sum mid = (self.pos_left + self.pos_right)// 2 if j <= mid : return self.left.sumRange(i,j) elif i > mid: return self.right.sumRange(i,j) else: return self.left.sumRange(i,mid)+self.right.sumRange(mid+1,j) def update(self, i: int, val: int) -> None: if self.pos_right == self.pos_left == i: self.sum = val return mid = (self.pos_left + self.pos_right)// 2 if i <= mid: self.left.update(i,val) else: self.right.update(i,val) self.sum = self.left.sum + self.right.sum class NumArray: def __init__(self, nums: List[int]): if not nums: return self.nums=nums self.root = self.build_tree(0, len(nums)-1) def update(self, i: int, val: int) -> None: self.root.update(i,val) def sumRange(self, i: int, j: int) -> int: return self.root.sumRange(i, j) def build_tree(self,left,right): if left == right: return Node(self.nums[left],left,right) mid=(left + right)//2 leftnode=self.build_tree(left, mid) rightnode=self.build_tree(mid+1, right) return Node(leftnode.sum + rightnode.sum, left, right, leftnode, rightnode)
题目309题
最佳买卖股票时机含冷冻期
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
思路
最开始使用分治来实现,但时间效率太慢了
因此选择动态规划的方法:
持有一支股票,对应的「累计最大收益」记为 f[i][0] = max(f[i−1][0],f[i−1][2]−prices[i])
不持有任何股票,并且处于冷冻期中,对应的「累计最大收益」记为 f[i][1] = f[i−1][0]+prices[i];
不持有任何股票,并且不处于冷冻期中,对应的「累计最大收益」记为 f[i][2] = max(f[i−1][1],f[i−1][2])。
第 0 天的情况作为动态规划中的边界条件:
⎧f[0][0] = −prices[0]
⎨f[0][1] = 0
⎩f[0][2] = 0
实现
class Solution: def maxProfit(self, prices: List[int]) -> int: if not prices: return 0 days = len(prices) f = [[-prices[0], 0, 0]] + [[0] * 3 for _ in range(days - 1)] for i in range(1, days): f[i][0] = max(f[i - 1][0], f[i - 1][2] - prices[i]) f[i][1] = f[i - 1][0] + prices[i] f[i][2] = max(f[i - 1][1], f[i - 1][2]) return max(f[days - 1][1], f[days - 1][2])
题目310题
最小高度树
对于一个具有树特征的无向图,我们可选择任何一个节点作为根。图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树。给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的根节点。
思路
深度优先遍历:
常规解法从每点依次遍历,但会超时
可以多源点遍历:叶子开始,层层往里剥,每次去除度为1的节点,只到最内层为止,便是最小高度树的根。
实现
class Solution: def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: from collections import defaultdict if not edges: return [0] d=defaultdict(list) for i,j in edges: d[i].append(j) d[j].append(i) leaves={i for i in d if len(d[i])==1 } while n >2: n -= len(leaves) next = set() for leave in leaves: for neighbor in d[leave]: d[neighbor].remove(leave) if len(d[neighbor])==1: next.add(neighbor) leaves=next return list(leaves)
题目313题
超级丑数
编写一段程序来查找第 n
个超级丑数。
超级丑数是指其所有质因数都是长度为 k
的质数列表 primes
中的正整数。
思路
与264题完全相同
实现
class Solution: def nthSuperUglyNumber(self, n: int, primes: List[int]) -> int: l = len(primes) lable = [0 for _ in range(l)] result = [1] while len(result) != n: ugly = float("inf") for i in range(l): ugly = min(result[lable[i]] * primes[i],ugly) result.append(ugly) for i in range(l): if ugly == result[lable[i]] * primes[i]: lable[i] += 1 return result[-1]
题目318题
最大单词长度
给定一个字符串数组 words,找到 length(word[i]) * length(word[j]) 的最大值,并且这两个单词不含有公共字母。你可以认为每个单词只包含小写字母。如果不存在这样的两个单词,返回 0。
思路
1.遍历
实现
class Solution: def maxProduct(self, words: List[str]) -> int: def compare(word1, word2): word1 = set(word1) word2 = set(word2) return word1&word2 == set([]) n = len(words) result = 0 for i in range(n-1): for j in range(i+1,n): if compare(words[i],words[j]): result = max(len(words[i]) * len(words[j]), result) return result
题目318题
灯泡开关
初始时有 n 个灯泡关闭。 第 1 轮,你打开所有的灯泡。 第 2 轮,每两个灯泡你关闭一次。 第 3 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭)。第 i 轮,每 i 个灯泡切换一次开关。 对于第 n 轮,你只切换最后一个灯泡的开关。 找出 n 轮后有多少个亮着的灯泡。
思路
找规律,平方数亮着。
实现
class Solution: def bulbSwitch(self, n: int) -> int: return int(math.sqrt(n))
题目322题
零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
思路
1.贪心算法:超时
2.动态规划:记录每个amout需要的最少次数
实现
1. class Solution: def coinChange(self, coins: List[int], amount: int) -> int: def change(round, amount): if round == 0: return amount == 0 for coin in coins: if change(round-1, amount-coin): return True return False for i in range(amount+1): if change(i,amount): return i return -1 2.动态规划 class Solution: def coinChange(self, coins: List[int], amount: int) -> int: dp = [float('inf')] * (amount + 1) dp[0] = 0 for coin in coins: for x in range(coin, amount + 1): dp[x] = min(dp[x], dp[x - coin] + 1) return dp[amount] if dp[amount] != float('inf') else -1
! 待解决 题目324题
摆动排序II
给定一个无序的数组 nums
,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]...
的顺序。
思路
1.题解的快速排序没有看懂
2.排序后依次插入
实现
class Solution: def wiggleSort(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ nums.sort(reverse = True) mid = len(nums)//2 nums[1::2], nums[0::2] = nums[:mid], nums[mid:]
题目326题
3的幂
给定一个整数,写一个函数来判断它是否是 3 的幂次方。
思路实现
class Solution: def isPowerOfThree(self, n: int) -> bool: if n == 0 : return False while n != 1: m = n %3 if m != 0: return False n = n // 3 return True
题目328题
奇偶链表
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
思路
实现
1. 双指针
class Solution: def oddEvenList(self, head: ListNode) -> ListNode: if not head: return odd = head even_pre = even = head.next while odd.next and even.next: odd.next = odd.next.next even.next = even.next.next odd = odd.next even = even.next odd.next = even_pre return head
2.
题目331题
验证二叉树的前序序列化
序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #
。
思路
在遍历过程中,每遇到逗号字符就更新可用槽位的数量。首先,将槽位减一(空节点和非空节点都要消耗一个槽位)。其次,如果当前节点是非空节点(即逗号字符前不是 #),新增两个槽位。
实现
class Solution: def isValidSerialization(self, preorder: str) -> bool: preorder += "," slot = 1 pre = None for ch in preorder: if ch == ",": slot -= 1 if slot < 0: return False if pre != "#": slot += 2 pre = ch return slot == 0
题目332题
重新安排行程
给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。
提示:
如果存在多种有效的行程,请你按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
所有的机场都用三个大写字母表示(机场代码)。
假定所有机票至少存在一种合理的行程。
所有的机票必须都用一次 且 只能用一次。
示例 1:
输入:[["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
输出:["JFK", "MUC", "LHR", "SFO", "SJC"]
示例 2:
输入:[["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
输出:["JFK","ATL","JFK","SFO","ATL","SFO"]
解释:另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后。
思路
求解欧拉回路:
Hierholzer 算法用于在连通图中寻找欧拉路径,其流程如下:
从起点出发,进行深度优先搜索。
每次沿着某条边从某个顶点移动到另外一个顶点的时候,都需要删除这条边。
如果没有可移动的路径,则将所在节点加入到栈中,并返回。
实现
class Solution: def findItinerary(self, tickets: List[List[str]]) -> List[str]: result = list() def dfs(start): print("0",start,result) while vec[start]: dfs(vec[start].pop(0)) result.insert(0,start) print("1",start,result) vec = collections.defaultdict(list) for depart, arrive in tickets: vec[depart].append(arrive) for key in vec: vec[key].sort() dfs("JFK") return result
题目334题
递增的三元子序列
给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。
数学表达式如下:
如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1) 。
思路
建立small和mid两个指针来保存递增子序列的最小值和中间值。遍历数组,每遇到一个数字,我们将它和 small 和 mid 相比,若小于等于 small ,则替换 small;否则,若小于等于 mid,则替换 mid;否则,若大于 mid,则说明我们找到了长度为 3 的递增数组。small 在 mid 后面,没有严格遵守递增顺序,但它隐含着的真相是,有一个比 small 大比 mid 小的前·最小值出现在 mid 之前。因此,当后续出现比 mid 大的值的时候,我们一样可以通过当前 small 和 mid 推断的确存在着长度为 3 的递增序列。
实现
class Solution: def increasingTriplet(self, nums: List[int]) -> bool: mid, small = float("inf"), float("inf") for num in nums: if num <= small: small = num elif num <= mid: mid = num else: return True return False
题目337题
打家劫舍III
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 1:
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
思路
1.深度优先遍历:
实现
class Solution: def rob(self, root: TreeNode) -> int: def steal(node): if not node: return 0,0 left_select,left_notsel = steal(node.left) right_select,right_notsel = steal(node.right) select = node.val + left_notsel + right_notsel notsel = max(left_select,left_notsel) + max(right_notsel,right_select) return select,notsel root_select,root_notsel = steal(root) return max(root_select,root_notsel)
题目338题
比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 2:
输入: 5
输出: [0,1,1,2,1,2]
思路
动态规划
实现
class Solution: def countBits(self, num: int) -> List[int]: indicator = 0 result = [0 for _ in range(num+1)] for idx in range(1, num+1): if idx & (idx -1) == 0: indicator = 0 result[idx] = result[indicator] + 1 indicator += 1 return result
题目341题
扁平化嵌套列表迭代器
给你一个嵌套的整型列表。请你设计一个迭代器,使其能够遍历这个整型列表中的所有整数。
列表中的每一项或者为一个整数,或者是另一个列表。其中列表的元素也可能是整数或是其他列表。
示例 1:
输入: [[1,1],2,[1,1]]
输出: [1,1,2,1,1]
解释: 通过重复调用 next 直到 hasNext 返回 false,next 返回的元素的顺序应该是: [1,1,2,1,1]。
思路实现
# """ # This is the interface that allows for creating nested lists. # You should not implement it, or speculate about its implementation # """ #class NestedInteger: # def isInteger(self) -> bool: # """ # @return True if this NestedInteger holds a single integer, rather than a nested list. # """ # # def getInteger(self) -> int: # """ # @return the single integer that this NestedInteger holds, if it holds a single integer # Return None if this NestedInteger holds a nested list # """ # # def getList(self) -> [NestedInteger]: # """ # @return the nested list that this NestedInteger holds, if it holds a nested list # Return None if this NestedInteger holds a single integer # """ class NestedIterator: def __init__(self, nestedList: [NestedInteger]): self.integer = [] for item in nestedList: if item.isInteger() is True: self.integer.append(item.getInteger()) else: new = item.getList() nest = NestedIterator(new) while nest.hasNext(): self.integer.append(nest.next()) def next(self) -> int: return self.integer.pop(0) def hasNext(self) -> bool: return self.integer != [] # Your NestedIterator object will be instantiated and called as such: # i, v = NestedIterator(nestedList), [] # while i.hasNext(): v.append(i.next())
题目342题
4的幂
给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方。
思路
位运算,2的倍数,且1位于奇数位置
实现
class Solution: def isPowerOfFour(self, num: int) -> bool: return num > 0 and num & (num - 1) == 0 and num & 0xaaaaaaaa == 0
题目343题
整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
思路
动态规划
实现
class Solution: def integerBreak(self, n: int) -> int: dp = [0 for _ in range(n+1)] for i in range(2,n+1): for j in range(i): dp[i] = max(j*(i-j), j*dp[i-j],dp[i]) return dp[n]
题目344题
反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
思路实现
class Solution: def reverseString(self, s: List[str]) -> None: """ Do not return anything, modify s in-place instead. """ if not s: return left, right = 0,len(s)-1 while left < right: s[left],s[right] = s[right],s[left] left += 1 right -= 1
题目345题
翻转字符串中的元音字符
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
思路实现
class Solution: def reverseVowels(self, s: str) -> str: s = list(s) left, right = 0,len(s)-1 vowel = ['a','e','i','o','u','A','E','I','O','U'] while left < right: if s[left] in vowel and s[right] in vowel: s[left],s[right] = s[right],s[left] left += 1 right -= 1 elif s[left] in vowel: right -= 1 elif s[right] in vowel: left += 1 else: left += 1 right -= 1 return "".join(s)
题目347题
前k个高频元素
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
思路
1.最小堆
1.遍历nums建立(数字:频次)字典 O(n)
2.将字典转为二维列表
3.取前K个元素建立大小为K的最小堆O(k)
4.剩余k+1到N个个元素依次和堆顶比较,如果比堆顶大,则替换当前堆顶,并维护最小堆 每次调整花费O(logk),最坏的情况O((n-k)logk)
5.最终最小堆里就是前K频次高的元素
2.桶排序
将频次相同的元素放进相应的数组中
实现
class Solution: def topKFrequent(self, nums: List[int], k: int) -> List[int]: occur = dict() for num in nums: occur[num] = occur.get(num,0)+1 occur_list = [[key, value] for key,value in occur.items()] def sink(idx, k): while True: t = 2*idx +1 if t >= k: return if t+1<k and occur_list[t][1] > occur_list[t+1][1]: t = t+1 if occur_list[t][1] < occur_list[idx][1]: occur_list[t],occur_list[idx] = occur_list[idx],occur_list[t] idx = t else: return for idx in range(k//2,-1,-1): sink(idx,k) for idx in range(k,len(occur_list)): if occur_list[idx][1] > occur_list[0][1]: occur_list[0] = occur_list[idx] sink(0,k) return [occur_list[i][0] for i in range(k)] 2. class Solution: def topKFrequent(self, nums: List[int], k: int) -> List[int]: counter = collections.Counter(nums) n = len(nums) fre = [[] for _ in range(n + 1)] for v, c in counter.items(): fre[c].append(v) res = [] for idx in range(n,0,-1): res += fre[idx] if len(res) == k: return res
题目349题
两个数组的交集
思路实现
class Solution: def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: temp1,temp2 =set(nums1),set(nums2) res = [] for item in temp1: if item in temp2: res.append(item) return res
题目343题
两个数组的交集II
给定两个数组,编写一个函数来计算它们的交集。
思路实现
class Solution: def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]: if len(nums1) > len(nums2): return self.intersect(nums2,nums1) occur = dict() result =[] for num in nums1: occur[num] = occur.get(num,0)+1 for num in nums2: if num in occur: result.append(num) occur[num] = occur[num] -1 if occur[num] == 0: occur.pop(num) return result
题目355题
设计推特
设计一个简化版的推特(Twitter),可以让用户实现发送推文,关注/取消关注其他用户,能够看见关注人(包括自己)的最近十条推文。你的设计需要支持以下的几个功能:
postTweet(userId, tweetId): 创建一条新的推文
getNewsFeed(userId): 检索最近的十条推文。每个推文都必须是由此用户关注的人或者是用户自己发出的。推文必须按照时间顺序由最近的开始排序。
follow(followerId, followeeId): 关注一个用户
unfollow(followerId, followeeId): 取消关注一个用户
思路实现
class Twitter: class User: def __init__(self): self.followee = set() self.tweet = list() def __init__(self): """ Initialize your data structure here. """ self.time = 0 self.users = dict() self.tweets = dict() def postTweet(self, userId: int, tweetId: int) -> None: """ Compose a new tweet. """ if userId not in self.users: self.users[userId] = Twitter.User() self.users[userId].tweet.append(tweetId) self.time += 1 self.tweets[tweetId] = self.time def getNewsFeed(self, userId: int) -> List[int]: """ Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. """ if userId not in self.users: return list() ans = self.users[userId].tweet[-10:][::-1] for followee in self.users[userId].followee : if followee in self.users: temp = self.users[followee].tweet[-10:][::-1] i,j,com = 0,0,[] while i < len(ans) and j < len(temp): if self.tweets[ans[i]] > self.tweets[temp[j]]: com.append(ans[i]) i += 1 else: com.append(temp[j]) j += 1 com.extend(ans[i:]) com.extend(temp[j:]) ans = com[:10] return ans def follow(self, followerId: int, followeeId: int) -> None: """ Follower follows a followee. If the operation is invalid, it should be a no-op. """ if followeeId != followerId: if followerId not in self.users: self.users[followerId] = Twitter.User() self.users[followerId].followee.add(followeeId) def unfollow(self, followerId: int, followeeId: int) -> None: """ Follower unfollows a followee. If the operation is invalid, it should be a no-op. """ if followeeId != followerId: if followerId in self.users: self.users[followerId].followee.discard(followeeId)
题目357题
计算各个位数不同的数字
给定一个非负整数 n,计算各位数字都不同的数字 x 的个数,其中 0 ≤ x < 10n 。
示例:
输入: 2
输出: 91
解释: 答案应为除去 11,22,33,44,55,66,77,88,99 外,在 [0,100) 区间内的所有数字。
思路实现
class Solution: def countNumbersWithUniqueDigits(self, n: int) -> int: if n == 0: return 1 res,temp,first = 10,9,9 for i in range(2,n+1): temp *= first first -= 1 res += temp return res
题目365题
水壶问题
有两个容量分别为 x升 和 y升 的水壶以及无限多的水。请判断能否通过使用这两个水壶,从而可以得到恰好 z升 的水?
如果可以,最后请用以上水壶中的一或两个来盛放取得的 z升水。
思路实现
class Solution:
def canMeasureWater(self, x: int, y: int, z: int) -> bool:
if x + y < z:
return False
if x == 0 or y == 0:
return z == 0 or x + y == z
return z % math.gcd(x, y) == 0
题目367题
有效的完全平方数
思路
二分法
实现
class Solution: def isPerfectSquare(self, num: int) -> bool: if num < 2: return True left, right = 2, num//2 while left <= right: mid = left + (right-left)//2 temp = mid*mid if temp == num: return True elif temp > num: right = mid -1 else: left = mid+1 return False
题目368题
最大整除子集
给出一个由无重复的正整数组成的集合,找出其中最大的整除子集,子集中任意一对 (Si,Sj) 都要满足:Si % Sj = 0 或 Sj % Si = 0。
如果有多个目标子集,返回其中任何一个均可。
思路实现
class Solution: def largestDivisibleSubset(self, nums: List[int]) -> List[int]: n = len(nums) if n <= 1: return nums nums.sort() dp = {i :[nums[i]] for i in range(n)} for i in range(1,n): for j in range(i-1,-1,-1): if nums[i] % nums[j] == 0: temp = dp[j] + [nums[i]] dp[i] = temp if len(temp) > len(dp[i]) else dp[i] return max(dp.values(), key = len)
题目367题
思路
递归+模运算性质:
a**2 mod b = ((a mod b) * (a mod b)) mod b
实现
class Solution: def superPow(self, a: int, b: List[int]) -> int: if not b: return 1 last = b.pop() left = self.mypow(a, last) right = self.mypow(self.superPow(a,b),10) return (left*right)%1337 def mypow(self,a,b): res =1 a %= 1337 for i in range(b): res *= a res %= 1337 return res