@thousfeet
2024-10-12T02:08:13.000000Z
字数 102933
阅读 674
LeetCode
Given two binary trees and imagine that when you put one of them to cover the other, some nodes of the two trees are overlapped while the others are not.
You need to merge them into a new binary tree. The merge rule is that if two nodes overlap, then sum node values up as the new value of the merged node. Otherwise, the NOT null node will be used as the node of new tree.Example 1:
Input:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
Output:
Merged tree:
3
/ \
4 5
/ \ \
5 4 7Note: The merging process must start from the root nodes of both trees.
【题意】
二叉树合并,把相同位置的val加和
【思路】
根节点按要求加和,左右节点扔进递归
【代码】
# 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 mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
if t1 or t2:
new_root = TreeNode()
else:
return
if t1:
new_root.val += t1.val
if t2:
new_root.val += t2.val
if t1 and t2:
new_root.left = self.mergeTrees(t1.left, t2.left)
new_root.right = self.mergeTrees(t1.right, t2.right)
elif t1:
new_root.left = self.mergeTrees(t1.left, None)
new_root.right = self.mergeTrees(t1.right, None)
elif t2:
new_root.left = self.mergeTrees(None, t2.left)
new_root.right = self.mergeTrees(None, t2.right)
return new_root
【discussion】
简洁写法 啊呜呜呜突然觉得自己写的也太丑了
def mergeTrees(self, t1, t2):
if t1 and t2:
root = TreeNode(t1.val + t2.val)
root.left = self.mergeTrees(t1.left, t2.left)
root.right = self.mergeTrees(t1.right, t2.right)
return root
else:
return t1 or t2
Given a binary tree, find its maximum depth.
The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
Note: A leaf is a node with no children.
Example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its depth = 3.
【题意】
求二叉树的树深
【代码】
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if root==None:
return 0
cnt = 0
que = [root]
while len(que):
cnt += 1
new_que = []
for x in que:
if x.left:
new_que.append(x.left)
if x.right:
new_que.append(x.right)
que = new_que[::]
return cnt
【discussion】
One line solution
def maxDepth(self, root):
return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right)) if root else 0
Given a non-empty array of integers nums, every element appears twice except for one. Find that single one.
Follow up: Could you implement a solution with a linear runtime complexity and without using extra memory?Example 1:
Input: nums = [2,2,1]
Output: 1
Example 2:
Input: nums = [4,1,2,1,2]
Output: 4
Example 3:
Input: nums = 1
Output: 1Constraints:
1 <= nums.length <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
Each element in the array appears twice except for one element which appears only once.
【题意】
只有一个数出现了一次 其他都出现了两次 找这个数
【代码】
class Solution:
def singleNumber(self, nums: List[int]) -> int:
ans = nums[0]
for x in nums[1:]:
ans = ans^x
return ans
【discussion】
其实还有多种做法
def singleNumber1(self, nums):
dic = {}
for num in nums:
dic[num] = dic.get(num, 0)+1
for key, val in dic.items():
if val == 1:
return key
def singleNumber2(self, nums):
res = 0
for num in nums:
res ^= num
return res
def singleNumber3(self, nums):
return 2*sum(set(nums))-sum(nums)
from functools import reduce
def singleNumber4(self, nums):
return reduce(lambda x, y: x ^ y, nums)
def singleNumber(self, nums):
return reduce(operator.xor, nums)
Invert a binary tree.
Example:
Input:
4
/ \
2 7
/ \ / \
1 3 6 9
Output:
4
/ \
7 2
/ \ / \
9 6 3 1
【代码】
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if root==None:
return
self.invertTree(root.left)
self.invertTree(root.right)
tmp = root.left
root.left = root.right
root.right = tmp
return root
【discussion】
更Pythonic的写法
def invertTree(self, root):
if root:
root.left, root.right = self.invertTree(root.right), self.invertTree(root.left)
return root
Reverse a singly linked list.
Example:
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
【思路】
遍历每个节点,插到第一个的前面
【代码】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
pre_head = ListNode(next=head)
while head and head.next:
now = head.next
head.next, now.next, pre_head.next = now.next, pre_head.next, now
return pre_head.next
【discussion】
递归的写法
class Solution(object):
def reverseList(self, head, prev=None):
if not head:
return prev
curr, head.next = head.next, prev
return self.reverseList(curr, head)
Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.
You may assume that the array is non-empty and the majority element always exist in the array.
Example 1:
Input: [3,2,3]
Output: 3
Example 2:
Input: [2,2,1,1,1,2,2]
Output: 2
【题意】
找到在列表中出现次数超过n/2的元素
【代码】
(1)
class Solution:
def majorityElement(self, nums: List[int]) -> int:
nums.sort()
n = len(nums)
return nums[int(n/2)]
(2)
class Solution:
def majorityElement(self, nums: List[int]) -> int:
num, cnt = 0, 0
for x in nums:
if cnt==0:
num = x
cnt += 1
elif num==x:
cnt += 1
elif num!=x:
cnt -= 1
return num
Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.
Example:
Input: [0,1,0,3,12]
Output: [1,3,12,0,0]
Note:
You must do this in-place without making a copy of the array.
Minimize the total number of operations.
【题意】
把数组中的0挪到最后面 要求是in-place操作
【思路】
先想到把0的pop掉然后在加上少掉的0,更好的是用双指针的做法把0和非0的直接交换(没前者好写就没写了orz)
【代码】
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
length = len(nums)
now = 0
while now<len(nums):
if nums[now]==0:
nums.pop(now)
else:
now+=1
zero_num = length-len(nums)
nums += [0]*zero_num
【discussion】
比双指针做法更简单,因为其实不要想着是和0交换,而是直接按序排下去就好了,第一个非0的要放在数组第一个位置,第二个要放第二个位置,所以当不为0的时候idx++就是正确的位置,最后0都是会被交换到末尾的
def moveZeroes(self, nums):
idx = 0
for i in xrange(len(nums)):
if nums[i] != 0:
nums[i], nums[idx] = nums[idx], nums[i]
idx += 1
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.
Find all the elements of [1, n] inclusive that do not appear in this array.
Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space.
Example:
Input:
[4,3,2,7,8,2,3,1]
Output:
[5,6]
【题意】
找到1~n中没有出现在数组里的数 n=len(nums)
【代码】
没想出来 还是用了额外空间OTL
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
all_nums = [0]*len(nums)
for x in nums:
all_nums[x-1]=1
ans = []
for i in range(len(nums)):
if all_nums[i]==0:
ans.append(i+1)
return ans
【discussion】
超级机智!!
对nums中的每个数 如果这个数k出现了 就把index=k位置的置为负数,然后遍历一遍找到还是正数的,那这些位置的index就是答案
def findDisappearedNumbers(self, nums):
for num in nums:
index = abs(num) - 1
nums[index] = -abs(nums[index])
return [i + 1 for i, num in enumerate(nums) if num > 0]
Merge two sorted linked lists and return it as a new sorted list. The new list should be made by splicing together the nodes of the first two lists.
Example 1:
Input: l1 = [1,2,4], l2 = [1,3,4]
Output: [1,1,2,3,4,4]
Example 2:
Input: l1 = [], l2 = []
Output: []
Example 3:
Input: l1 = [], l2 = [0]
Output: [0]
Constraints:
The number of nodes in both lists is in the range [0, 50].
-100 <= Node.val <= 100
Both l1 and l2 are sorted in non-decreasing order.
【题意】
合并两个递增的list
【思路】
先建个头,然后两个list的头看下取哪个就拿过来
【代码】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
ans = ListNode()
now = ans
while l1 or l2:
if l1 and l2:
if l1.val < l2.val:
now.next, l1 = l1, l1.next
else:
now.next, l2 = l2, l2.next
else:
now.next = l1 or l2
l1, l2 = l1.next if l1 else l1, l2.next if l2 else l2
now = now.next
return ans.next
【discussion】
递归的写法
def mergeTwoLists2(self, l1, l2):
if not l1 or not l2:
return l1 or l2
if l1.val < l2.val:
l1.next = self.mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2
Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Note that you cannot sell a stock before you buy one.
Example 1:
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Not 7-1 = 6, as selling price needs to be larger than buying price.
Example 2:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
【题意】
给一list代表了每天的股价,问买一次卖一次最多能赚多少钱
【思路】
先算出每日价格的差 就相当于是最大连续子串和的问题了
【代码】
class Solution:
def maxProfit(self, prices: List[int]) -> int:
days = len(prices)
if days<=1:
return 0
diff = [prices[i]-prices[i-1] for i in range(1,days)]
ans, now = 0, 0
for x in diff:
now+=x
if now<0:
now=0
ans = max(ans, now)
return ans
【discussion】
只遍历一遍 在每个位置更新下当前见过的min_price,然后在这个点卖出的收益就是当前price-min_price
def maxProfit(prices):
ans, min_price = 0, float('inf')
for price in prices:
min_price = min(min_price, price)
profit = price - min_price
ans = max(ans, profit)
return ans
Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.
Example:
Given a binary tree
1
/ \
2 3
/ \
4 5
Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].
Note: The length of path between two nodes is represented by the number of edges between them.
【题意】
求二叉树的直径(一节点到另一节点的最长路径)
【思路】
如果知道直径的根是哪个点,那直径就等于以这个点为根的左边最长+右边最长,所以要遍历到每个点上求一下;因为递归求树高的时候就会遍历到所有点,所以直接在求树高的时候顺带更新一下ans
【代码】
class Solution:
def __init__(self):
self.ans = 0
def diameterOfBinaryTree(self, root: TreeNode) -> int:
if not root:
return 0
def length(root):
if not root:
return 0
l, r = length(root.left), length(root.right)
self.ans = max(self.ans, l+r)
return max(l, r)+1
length(root)
return self.ans
(顺带发现 写成内嵌函数会比写成类函数更快)
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Example 1:
Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps
Example 2:
Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step
Constraints:
1 <= n <= 45
【题意】
一次可以跨一级台阶或两级台阶,问要上n级台阶有几种走法
【思路】
经典DP,原问题P(N) = P(N-1)+P(N-2),但因为觉得简单写的太随意还错了3次 555。错的点:一定要开数组存memory不然会T,提前判n小于3就返回是因为后面dp[0],1,2的赋值需要保证n>3
class Solution:
def climbStairs(self, n: int) -> int:
if n<3:
return n
dp = [-1]*(n+1)
dp[0], dp[1], dp[2] = 0, 1, 2
def dfs(n):
if dp[n]!=-1:
return dp[n]
else:
dp[n] = dfs(n-1)+dfs(n-2)
return dp[n]
return dfs(n)
【discussion】
非递归的写法
# Bottom up, O(n) space
def climbStairs2(self, n):
if n == 1:
return 1
res = [0 for i in xrange(n)]
res[0], res[1] = 1, 2
for i in xrange(2, n):
res[i] = res[i-1] + res[i-2]
return res[-1]
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
For example, this binary tree [1,2,2,3,4,4,3] is symmetric:
1
/ \
2 2
/ \ / \
3 4 4 3
But the following [1,2,2,null,3,null,3] is not:
1
/ \
2 2
\ \
3 3
【题意】
判断二叉树是否左右对称
【思路】
递归
【代码】
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
def issym(left, right):
if left==None and right==None:
return True
elif left==None or right==None:
return False
elif left.val==right.val and issym(left.left, right.right) and issym(left.right, right.left):
return True
else:
return False
if root==None:
return True
return issym(root.left, root.right)
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Follow up: If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
Example 1:
Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Example 2:
Input: nums = 1
Output: 1
Example 3:
Input: nums = [0]
Output: 0
Example 4:
Input: nums = [-1]
Output: -1
【题意】
求最大连续子序列的最大值(子序列至少要有一个数)
【思路】
最大连续子序列常规操作闭眼写,但那个是默认全为负就取0个数,所以特判一下如果全为负就直接返回最大
【代码】
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
ans, now = 0, 0
flag = False
for x in nums:
if x>0:
flag = True
now += x
if now < 0:
now = 0
ans = max(ans, now)
if flag == False:
return max(nums)
else:
return ans
【discussion】
超级简单的写法,直观的解释就是,num存的是从头开始到我这的最大连续子序列。那么对于每个位置,如果前面的大于0,就对我这有利可以加过来,如果小于0,就放弃掉。
本质是和常规操作一样的想法,但就可以存下每个位置为结尾且至少有一个数的最大连续子序列,然后取max。
for i in range(1, len(nums)):
if nums[i-1] > 0:
nums[i] += nums[i-1]
return max(nums)
Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
You can return the answer in any order.
Example 1:
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Output: Because nums[0] + nums1 == 9, we return [0, 1].
Example 2:
Input: nums = [3,2,4], target = 6
Output: [1,2]
Example 3:
Input: nums = [3,3], target = 6
Output: [0,1]
Constraints:
2 <= nums.length <= 105
-109 <= nums[i] <= 109
-109 <= target <= 109
Only one valid answer exists.
【题意】
返回数组中的两个不同的数位置,使得它们的和是target
【思路】
遍历数组中每个数,查target-nums[i]有没有在nums里,这样是O(N^2)
【代码】
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
for i, x in enumerate(nums):
if target-x in nums:
idx = nums.index(target-x)
if idx==i:
continue
else:
return([i, idx])
【discussion】
O(N)的做法,一边遍历一边用dict记录这个值出现的位置,不同时存d[m]是因为不允许重复,所以就只记录在遍历到此处之前的数
def twoSum(self, nums, target):
d = {}
for i, n in enumerate(nums):
m = target - n
if m in d:
return [d[m], i]
else:
d[n] = i
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
push(x) -- Push element x onto stack.
pop() -- Removes the element on top of the stack.
top() -- Get the top element.
getMin() -- Retrieve the minimum element in the stack.
Example 1:
Input
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
Output
[null,null,null,null,-3,null,0,-2]
Explanation
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); // return -3
minStack.pop();
minStack.top(); // return 0
minStack.getMin(); // return -2
【题意】
实现一个能getMin的栈
【思路】
除了另外开一个minval数组,专门存从开始到每个位置的最小值,其他就没啥了
【代码】
class MinStack:
def __init__(self):
self.nums = []
self.minval = []
def push(self, x: int) -> None:
self.nums.append(x)
if len(self.minval)==0:
self.minval = [x]
else:
self.minval.append(min(x,self.minval[-1]))
def pop(self) -> None:
self.nums.pop()
self.minval.pop()
def top(self) -> int:
return self.nums[-1]
def getMin(self) -> int:
return self.minval[-1]
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
Example 1:
Input: nums = [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.
Example 2:
Input: nums = [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
Total amount you can rob = 2 + 9 + 1 = 12.
【题意】
只能抢劫不相邻位置的房子,问最多能得多少
【思路】
DP
【代码】
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums)==0:
return 0
dp = nums[::]
for i, x in enumerate(nums):
if i-2>=0:
dp[i] = max(dp[i], dp[i-2]+nums[i])
if i-1>=0:
dp[i] = max(dp[i], dp[i-1])
return dp[-1]
【discussion】
更节省空间的做法
# Approach 2:- Constant space use two variables and compute the max respectively
prev = curr = 0
for num in nums:
temp = prev # This represents the nums[i-2]th value
prev = curr # This represents the nums[i-1]th value
curr = max(num + temp, prev) # Here we just plug into the formula
return curr
Write a program to find the node at which the intersection of two singly linked lists begins.
【题意】
给两个链表,他们可能会在最后一部分有交叉(两个轨道汇入一个轨道),返回汇入的这个节点的value,如果没有交叉就返回None,要求时间O(N)空间O(1)
【思路】
第一想法就是遍历一遍把前序节点都找出来,然后反着找最后的交汇点,但是这样空间要多占O(N)。然后想着拆成两条链,先遍历一条扔进set,再看第二条时会遇到set中的值,但发现值不是唯一的,只能判断节点是不是相同而不能判断值。
扫了眼discussion,先把两条链的长度算出来,然后就能知道长的那条要从哪里开始能两条同步找,然后用节点判断。
【代码】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
def getLen(head):
cnt = 0
while head:
cnt+=1
head = head.next
return cnt
alen = getLen(headA)
blen = getLen(headB)
if blen>alen:
headA, headB = headB, headA
alen, blen = blen, alen
diff = alen-blen
for _ in range(diff):
headA = headA.next
while headA:
if headA==headB:
return headA
headA, headB = headA.next, headB.next
return None
Given a singly linked list, determine if it is a palindrome.
Example 1:
Input: 1->2
Output: false
Example 2:
Input: 1->2->2->1
Output: true
Follow up:
Could you do it in O(n) time and O(1) space?
【思路】
没想到O(1)空间的解法
思路就是先遍历一遍得到整条链的长度,然后第二遍搜到一半长度的位置,一边存下前一半的values,然后在后一半和前面的对比。这样是时间O(2N)空间O(N/2)。
【代码】
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
lenth = 0
now = head
while now:
lenth += 1
now = now.next
now = head
vals = []
for i in range(lenth//2):
vals.append(now.val)
now = now.next
if lenth%2:
now = now.next
# print(vals, now.val)
for i in range(lenth//2):
if not now.val==vals[-1]:
return False
else:
vals.pop()
now = now.next
return True
【discussion】
用walk和run指针的想法走到一半的位置(一个一次走两步一个一次走一步),然后把后一半reverse,就可以比较两条链了。不用担心奇数个的问题,因为后一半这样做完reverse之后相当于是两个不同head的链交汇到mid节点的位置。
def isPalindrome(self, head):
fast = slow = head
# find the mid node
while fast and fast.next:
fast = fast.next.next
slow = slow.next
# reverse the second half
node = None
while slow:
nxt = slow.next
slow.next = node
node = slow
slow = nxt
# compare the first and second half nodes
while node: # while node and head:
if node.val != head.val:
return False
node = node.next
head = head.next
return True
Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.
An input string is valid if:
Open brackets must be closed by the same type of brackets.
Open brackets must be closed in the correct order.
Example 1:
Input: s = "()"
Output: true
Example 2:
Input: s = "()[]{}"
Output: true
Example 3:
Input: s = "(]"
Output: false
Example 4:
Input: s = "([)]"
Output: false
Example 5:
Input: s = "{[]}"
Output: true
Constraints:
1 <= s.length <= 104
s consists of parentheses only '()[]{}'.
【思路】
经典栈题
觉得简单写的太草率还wa了两次orz 没有判最后栈非空、没有判遍历时遇到栈是空的情况
【代码】
class Solution:
def isValid(self, s: str) -> bool:
stack = []
dic = {'(':')', '{':'}', '[':']'}
for x in s:
if x in dic:
stack.append(x)
elif len(stack)==0:
return False
elif x==dic[stack[-1]]:
stack.pop()
else:
return False
if len(stack)!=0:
return False
return True
A string S of lowercase English letters is given. We want to partition this string into as many parts as possible so that each
letter appears in at most one part, and return a list of integers
representing the size of these parts.Example 1:
Input: S = "ababcbacadefegdehijhklij"
Output: [9,7,8]
Explanation: The partition is "ababcbaca", "defegde", "hijhklij". This is a partition so that each letter appears in at most one part. A partition like "ababcbacadefegde", "hijhklij" is incorrect, because it splits S into less parts.Note:
S will have length in range [1, 500]. S will consist of lowercase
English letters ('a' to 'z') only.
【题意】
给一字符串,要切成尽量多个片段,并使得同个字母只出现在一个片段
【思路】
记录下每个字母第一次出现的位置和最后一次出现的位置,那么他们中间这一段就必然要连在一起了,再进行片段之间的合并
【代码】
class Solution:
def partitionLabels(self, S: str) -> List[int]:
dic = {}
for i in range(len(S)):
x = S[i]
if x not in dic:
dic[x] = [i,i]
else:
dic[x][1] = i
frag = list(dic.values())
frag.sort()
# print(frag)
ans = []
l, r = frag[0][0], frag[0][2]
for x in frag[1:]:
ll, rr = x[0], x[1]
if ll < r:
r = max(r, rr)
else:
ans.append(r-l+1)
l, r = ll, rr
return ans+[len(S)-sum(ans)]
【discussion】
复杂度更低的解法O(N):遍历两遍,第一遍记录每个字母最后出现的位置,第二遍开始,对于每个看到的字母去查它的最后位置,维护right=max(right,rightmost[now]),直到当前遍历到的就是之前扫过的所有字母的最后位置,说明当前这一段结束了,result+=[...]。
def partition_labels(S):
rightmost = {c:i for i, c in enumerate(S)}
left, right = 0, 0
result = []
for i, letter in enumerate(S):
right = max(right,rightmost[letter])
if i == right:
result += [right-left + 1]
left = i+1
return result
Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array.
Example 1:
Input: 2
Output: [0,1,1]
Example 2:
Input: 5
Output: [0,1,1,2,1,2]
Follow up:
It is very easy to come up with a solution with run time O(n*sizeof(integer)). But can you do it in linear time O(n) /possibly in a single pass?
Space complexity should be O(n).
Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language.
【题意】
输出从0到num每个数的二进制表示中1的个数
【思路】
只要这个二进制数去掉一个1,就会得到一个更小的数,而这个更小的数是我们算过的,知道答案,就可以写成 1+ans[更小的数]。还记得 -x&x
可以把x二进制变成只剩最后一个1其他都是0,所以就把这个位置的值去掉,得到 ans[x-(-x&x)]+1。
【代码】
class Solution:
def countBits(self, num: int) -> List[int]:
ans = [0 for _ in range(num+1)]
for x in range(1, num+1):
if x<=2:
ans[x]=1
else:
ans[x] = ans[x-(-x&x)]+1
return ans
【discussion】
x & (x-1)
就是给 x 删去最后一个1,所以其实可以直接写作 ans[i] = ans[x & (x-1)] + 1
还有一个挺有趣的做法,就是直接按照1,2,4,8..这样一段一段增加下去,每一段都相当于在前面得到的所有数加上最高位的1,但这样不好的地方就是最坏会到O(2N)
def countBits(self, num):
iniArr = [0]
if num > 0:
amountToAdd = 1
while len(iniArr) < num + 1:
iniArr.extend([x+1 for x in iniArr])
return iniArr[0:num+1]
Suppose you have a random list of people standing in a queue. Each person is described by a pair of integers (h, k), where h is the height of the person and k is the number of people in front of this person who have a height greater than or equal to h. Write an algorithm to reconstruct the queue.
Note:
The number of people is less than 1,100.
Example
Input:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
Output:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
【题意】
在一个队伍中知道每个人的身高和排在他前面的更高或和他一样高的人数,还原出这个队伍
【思路】
按照x[0]降序、x1升序,这样先安排个子高的,然后再排矮的,同样个高的站前面的先安排,此时x1位置的值就是他应该插入的位置
【代码】
class Solution:
def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
people = sorted(people, key=lambda x:x[1])
people = sorted(people, key=lambda x:x[0], reverse=True)
ans = []
for x in people:
idx = x[1]
if len(ans) < idx+1:
ans.append(x)
else:
ans = ans[:idx]+[x]+ans[idx:]
return ans
【discussion】
更简洁的写法
def reconstructQueue(self, people):
people.sort(key=lambda x: (-x[0], x[1]))
queue = []
for p in people:
queue.insert(p[1], p)
return queue
Given a collection of distinct integers, return all possible permutations.
Example:
Input: [1,2,3]
Output:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
【题意】
给一串数组,输出全排列
【思路】
练习dfs
【代码】
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
ans = []
length = len(nums)
def dfs(tmp_l, dic):
if len(tmp_l)==length:
ans.append(tmp_l)
return
for i in nums:
if i not in dic:
dic[i]=1
dfs(tmp_l+[i], dic)
dic.pop(i)
dfs([],{})
return ans
【discussion】
不用dfs,对于num中每个元素一个个插:每拿到一个新的元素,就把之前已经排好的拎出来插在所有可以插入的位置
def permute(self, nums):
perms = [[]]
for n in nums:
new_perms = []
for perm in perms:
for i in xrange(len(perm)+1):
new_perms.append(perm[:i] + [n] + perm[i:]) #insert n
perms = new_perms
return perms
Given the root of a binary tree, return the inorder traversal of its nodes' values.
Example 1:
Input: root = [1,null,2,3]
Output: [1,3,2]
Example 2:
Input: root = []
Output: []
Example 3:
Input: root = 1
Output: 1
Example 4:
Input: root = [1,2]
Output: [2,1]
【题意】
给一个树 输出中序遍历
【思路】
中序是[左根右] 直接dfs就vans了
【代码】
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if root==None:
return []
return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
【discussion】
非递归的写法 自己压栈
def inorderTraversal(self, root):
res, stack = [], []
while True:
while root:
stack.append(root)
root = root.left
if not stack:
return res
node = stack.pop()
res.append(node.val)
root = node.right
Given a list of daily temperatures T, return a list such that, for each day in the input, tells you how many days you would have to wait until a warmer temperature. If there is no future day for which this is possible, put 0 instead.
For example, given the list of temperatures T = [73, 74, 75, 71, 69, 72, 76, 73], your output should be [1, 1, 4, 2, 1, 1, 0, 0].
Note: The length of temperatures will be in the range [1, 30000]. Each temperature will be an integer in the range [30, 100].
【题意】
给一个每日温度的列表 输出对于这个列表中的每一天 要等几天会温度变暖
【思路】
最后一天的日子好算就是0 所以从后往前遍历比较好求
维护一个单调栈 从后往前的压栈 如果遇到栈里的数比它小 就可以直接踹掉 因为对于靠前面的日子来说 是不可能指到一个更小还更靠后的数的(后来这个地方wa了 因为题意是需要小于等于都踹掉)
然后这时候栈首-现在这个idx就是所求值,如果栈空就是0
【代码】
class Solution:
def dailyTemperatures(self, T: List[int]) -> List[int]:
idx = len(T)-1
stack = [(T[-1],idx)]
ans = [0]
for x in T[-2::-1]:
idx -= 1
while len(stack) and x >= stack[-1][0]:
stack.pop()
if len(stack)==0:
ans.append(0)
else:
ans.append(stack[-1][3]-idx)
stack.append((x, idx))
return ans[::-1]
【discussion】
更简洁的写法 而且其实栈里不需要存value只存idx就好了
def dailyTemperatures(self, T):
ans = [0] * len(T)
stack = []
for i, t in enumerate(T):
while stack and T[stack[-1]] < t:
cur = stack.pop()
ans[cur] = i - cur
stack.append(i)
return ans
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
Example 1:
Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
Example 2:
Input: n = 1
Output: ["()"]
Constraints:
1 <= n <= 8
【题意】
输出n对括号能组成的所有valid的字符串
【思路】
dfs 记录现在有多少左括号了 如果 > 0 就能放右括号 如果 < n 就还能放左括号 到最后如果left=0说明是合法的 加入ans
【代码】
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
ans = []
def dfs(tmp_s, left):
if len(tmp_s)==2*n:
if left==0:
ans.append(tmp_s)
return
if left>0:
dfs(tmp_s+")", left-1)
if left<n:
dfs(tmp_s+"(", left+1)
dfs("",0)
return ans
【discussion】
这样dfs虽然好写 但会有很多重复计算 比如n=3的时候就会重复算n=1+2和n=2+1 好在n不大就没超时
所以这个DP的思路确实很赞 十分奇妙
To generate all n-pair parentheses, we can do the following:
Generate one pair: ()
Generate 0 pair inside, n - 1 afterward: () (...)...
Generate 1 pair inside, n - 2 afterward: (()) (...)...
...
Generate n - 1 pair inside, 0 afterward: ((...))I bet you see the overlapping subproblems here. Here is the code:
(you could see in the code that x represents one j-pair solution and y represents one (i - j - 1) pair solution, and we are taking into account all possible of combinations of them)
class Solution(object):
def generateParenthesis(self, n):
dp = [[] for i in range(n + 1)]
dp[0].append('')
for i in range(n + 1):
for j in range(i):
dp[i] += ['(' + x + ')' + y for x in dp[j] for y in dp[i - j - 1]]
return dp[n]
Given a set of distinct integers, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: nums = [1,2,3]
Output:
[
3,
1,
2,
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
【题意】
给一个列表 输出所有的子集
【思路】
遍历nums 对于每一个元素都把之前得到的所有子集拿出来把它塞进去
【代码】
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans = [[]]
for x in nums:
ans += [ a+[x] for a in ans]
return ans
【discussion】
除了dfs外 还看到一个思路 是把取子集当做是Bit Manipulation
给nums的每个位置赋予0/1 0代表不取1代表取 那么比如当n=3的时候 就相当于i从000遍历到111 (1 << len(nums)
)对于0~len(num)-1的这j个位置上 如果是1就把nums[j] append进来
def subsets(self, nums):
res = []
for i in xrange(1<<len(nums)):
tmp = []
for j in xrange(len(nums)):
if i & 1 << j: # if i >> j & 1:
tmp.append(nums[j])
res.append(tmp)
return res
Given a non-empty array of integers, return the k most frequent elements.
Example 1:
Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]
Example 2:
Input: nums = 1, k = 1
Output: 1
Note:
You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
Your algorithm's time complexity must be better than O(n log n), where n is the array's size.
It's guaranteed that the answer is unique, in other words the set of the top k frequent elements is unique.
You can return the answer in any order.
【题意】
输出列表中出现最多次的前K个元素
【思路】
先遍历一遍把元素个数记下来 然后按照次数sort一下
【代码】
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
dic = {}
for x in nums:
if x in dic:
dic[x]+=1
else:
dic[x]=1
dic = sorted(dic.items(), key=lambda x: -x[1])
ans = []
for i in range(k):
ans.append(dic[i][0])
return ans
【discussion】
最后一步更好的写法
arr = sorted(dic, key = dic.get,reverse = True)
return([arr[:k])
sorted(dic)
另一种思路,为避免sort的O(NlogN),可以把dict的key和value换一下,key换成次数后就是有范围的,从nums的长度到1,去找前K个,这样就O(N)了
def topKFrequent(self, nums, k):
frq = defaultdict(list)
for key, cnt in Counter(nums).items():
frq[cnt].append(key)
res = []
for times in reversed(range(len(nums) + 1)):
res.extend(frq[times])
if len(res) >= k: return res[:k]
return res[:k]
Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.
Example 1:
Input: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
Output: 1
Example 2:
Input: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \
3 6
/ \
2 4
/
1
Output: 3
Follow up:
What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?
【题意】
找出二叉搜索树上的第K大元素
【思路】
如果知道左子树的节点个数M,root就是第M+1大。如果K < M,就在左子树继续找,否则就在右子树找第K-(M+1)大。
【代码】
class Solution:
def cul(self, root):
if root==None:
return 0
return self.cul(root.left)+self.cul(root.right)+1
def kthSmallest(self, root: TreeNode, k: int) -> int:
left_num = self.cul(root.left)
if left_num == k-1:
return root.val
elif left_num > k-1:
return self.kthSmallest(root.left, k)
else:
return self.kthSmallest(root.right, k-(left_num+1))
【discussion】
上面那种做法最坏复杂度可能到O(N^2)
其实不用那么复杂,BST按照中序遍历得到的就是从小到大的序列,然后返回列表中的第k个就行了,这样是O(N)
class Solution(object):
def kthSmallest(self, root, k):
count = []
self.helper(root, count)
return count[k-1]
def helper(self, node, count):
if not node:
return
self.helper(node.left, count)
count.append(node.val)
self.helper(node.right, count)
或者不用存下来,直接在遍历左子树的时候k-=1,当k为0时就得到ans了
def kthSmallest(self, root, k):
self.k = k
self.res = None
self.helper(root)
return self.res
def helper(self, node):
if not node:
return
self.helper(node.left)
self.k -= 1
if self.k == 0:
self.res = node.val
return
self.helper(node.right)
Given a string, your task is to count how many palindromic substrings in this string.
The substrings with different start indexes or end indexes are counted as different substrings even they consist of same characters.
Example 1:
Input: "abc"
Output: 3
Explanation: Three palindromic strings: "a", "b", "c".
Example 2:
Input: "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".
Note:
The input string length won't exceed 1000.
【题意】
给一字符串 输出所有子串中是回文串的个数
【思路】
遍历,把每个当做中心,然后往周围找,这样是O(N^2),除了这种奇数个的情况,还得再处理一下偶数
【代码】
class Solution:
def countSubstrings(self, s: str) -> int:
ans = len(s)
for i, x in enumerate(s):
k = min(i, len(s)-i-1)
for j in range(1, k+1):
if s[i-j]==s[i+j]:
ans += 1
else:
break
for i, x in enumerate(s):
if i+1<len(s) and s[i]==s[i+1]:
ans += 1
k = min(i, len(s)-i-2)
for j in range(1, k+1):
if s[i-j]==s[i+1+j]:
ans += 1
else:
break
return ans
【discussion】
DP的写法,dp[i][j]代表这一段是回文
def countSubstrings(self, s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
res = 0
for i in range(n-1, -1, -1):
for j in range(i, n):
dp[i][j] = s[i] == s[j] and ((j-i+1) < 3 or dp[i+1][j-1])
res += dp[i][j]
return res
有个超级机智的统一奇偶长度的方法,就是在每个字符中间插空加上任意一个符号,对结果没有影响,比如加上“#”,奇偶的情况就是#a#b#a#和#a#b#b#a#,这样统一都变成奇数个了
O(N)的做法,Manacher算法: (知乎的讲解)
def ManacherAlgm(self, s):
# Transform S into T.
# For example, S = "abba", T = "^#a#b#b#a#$".
# ^ and $ signs are sentinels appended to each end to avoid bounds checking
T = '#'.join('^{}$'.format(s))
n = len(T)
P = [0] * n
C = R = 0
for i in range (1, n-1):
# (R > i) and min(R - i, P[2*C - i]) considering Case1 and Case2
P[i] = (R > i) and min(R - i, P[2*C - i])
# Attempt to expand palindrome centered at i
while T[i + 1 + P[i]] == T[i - 1 - P[i]]:
P[i] += 1
# If palindrome centered at i expand past R,
# adjust center based on expanded palindrome.
if i + P[i] > R:
C, R = i, i + P[i]
return (sum(P) + len(s)) // 2
Given an array nums of n integers where n > 1, return an array output such that output[i] is equal to the product of all the elements of nums except nums[i].
Example:
Input: [1,2,3,4]
Output: [24,12,8,6]
Constraint: It's guaranteed that the product of the elements of any prefix or suffix of the array (including the whole array) fits in a 32 bit integer.
Note: Please solve it without division and in O(n).
Follow up:
Could you solve it with constant space complexity? (The output array does not count as extra space for the purpose of space complexity analysis.)
【题意】
对于列表中的每个位置,输出整个列表除了该位置的数的乘积,限制是不让用除法
【思路】
算个前缀积和后缀积
【代码】
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
pre = [1]
suf = [1]
for x in nums:
pre.append(pre[-1]*x)
for x in nums[::-1]:
suf.append(suf[-1]*x)
suf.reverse()
ans = []
for i in range(len(nums)):
ans.append(pre[i]*suf[i+1])
return ans
【discussion】
只用O(1)空间的做法
也是正反两次遍历,维护一个在这个位置需要乘下去的p,比如n=4的时候:
第一遍遍历nums 得到output[-, 0, 01, 012]
第二遍反过来遍历nums 得到output[123, 0 23, 01 3, 012]
def productExceptSelf(self, nums):
p = 1
n = len(nums)
output = []
for i in range(0,n):
output.append(p)
p = p * nums[i]
p = 1
for i in range(n-1,-1,-1):
output[i] = output[i] * p
p = p * nums[i]
return output
You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise).
You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.
Example 1:
Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]
Output: [[7,4,1],[8,5,2],[9,6,3]]
Example 2:
Input: matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
Output: [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
Example 3:
Input: matrix = [1]
Output: [1]
Example 4:
Input: matrix = [[1,2],[3,4]]
Output: [[3,1],[4,2]]Constraints:
matrix.length == n
matrix[i].length == n
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000
【题意】
把二维矩阵顺时针转90度,要求是in-place的操作
【思路】
对着图看了几秒,第一反应是先求个转置(对称交换),观察一下,这样之后再每行反过来就行了
【代码】
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
n = len(matrix)
for i in range(n):
for j in range(i,n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
for i in range(n):
matrix[i] = matrix[i][::-1]
Given an array of strings strs, group the anagrams together. You can return the answer in any order.
An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.
Example 1:
Input: strs = ["eat","tea","tan","ate","nat","bat"]
Output: [["bat"],["nat","tan"],["ate","eat","tea"]]
Example 2:
Input: strs = [""]
Output: [[""]]
Example 3:
Input: strs = ["a"]
Output: [["a"]]Constraints:
1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i] consists of lower-case English letters.
【题意】
给一堆字符串,输出字符相同的组成的集合
【思路】
开始想的是对于a-z分别判断,把它们分成有a/没a的两堆,再每堆里分成有b/没b,再... (然后才发现同字母可能有多个,卒)
于是先给各个字符串做下sort,然后以sort后的字符串作为key造dict
【代码】
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
from collections import defaultdict
tmp = []
for x in strs:
chars = "".join(sorted(list(x)))
tmp.append(chars)
dic = defaultdict(list)
for i, x in enumerate(tmp):
dic[x].append(strs[i])
return dic.values()
【discussion】
更简洁的写法
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = collections.defaultdict(list)
for items in strs:
ans[''.join(sorted(items))].append(items)
return (list(ans.values()))
Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order.
The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different.
It is guaranteed that the number of unique combinations that sum up to target is less than 150 combinations for the given input.
Example 1:
Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],7]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.
Example 2:
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]
Example 3:
Input: candidates = 2, target = 1
Output: []Constraints:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
All elements of candidates are distinct.
1 <= target <= 500
【题意】
从一个list中选取数 使他们的和为target 可以重复选
【思路】
dp的思想,如果知道一个小于这个的数能被怎么组成,就能转移到更大的数。比如要求8,如果知道6能怎么得到,就把得到6的所有答案多加一个2就行了。一个小trick就是先sort一下候选数,遇到比target大就直接break掉
【旧的代码】
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
ans = [[] for _ in range(target+1)]
mark = [0 for _ in range(target+1)]
def dfs(num):
for x in candidates:
if num <= x:
break
if mark[num-x]==0:
dfs(num-x)
if len(ans[num-x])>0:
ans[num] += [tmp+[x] for tmp in ans[num-x]]
mark[num] = 1
ans[num] = remove_dup(ans[num])
def remove_dup(lis):
[x.sort() for x in lis]
key_l = list(set([" ".join(list(map(str, x))) for x in lis]))
lis = [list(map(int, key.split())) for key in key_l]
return lis
for x in candidates:
if x > target:
continue
ans[x]=[[x]]
dfs(target)
return ans[target]
这里有个很烦人的地方在于需要去重,我写了个remove_dup把每个list sort之后再join起来扔进set来去重,但看了discussion才发现,其实不用那么麻烦,因为candidates是sort过的,当转移的时候,从数字更小的ans中取出来的某个答案tmp,如果转移差值x是小于tmp[-1]的,那就不用加了
比如求5的时候,先拿出2,给它加上3;再拿出3,要给它加上2,就重复了
【新的代码】
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
ans = [[] for _ in range(target+1)]
mark = [0 for _ in range(target+1)]
def dfs(num):
for x in candidates:
if num <= x:
break
if mark[num-x]==0:
dfs(num-x)
if len(ans[num-x])>0:
for tmp in ans[num-x]:
if x >= tmp[-1]:
ans[num] += [tmp+[x]]
mark[num] = 1
for x in candidates:
if x > target:
continue
ans[x]=[[x]]
dfs(target)
return ans[target]
【discussion】
不把每个数的ans都存下来,直接dfs(运行时间和内存都差不多)
class Solution(object):
def combinationSum(self, candidates, target):
result = []
candidates = sorted(candidates)
def dfs(remain, stack):
if remain == 0:
result.append(stack)
return
for item in candidates:
if item > remain: break
if stack and item < stack[-1]: continue
else:
dfs(remain - item, stack + [item])
dfs(target, [])
return result
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
Example 1:
Input: [3,2,1,5,6,4] and k = 2
Output: 5
Example 2:
Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
【题意】
找出列表中第K大的元素
【思路】
快排中的partition
要注意的坑是 i要从0开始遍历而不是从1开始,不然会在0和j位置交换的时候有问题,比如nums=[2,1] k=2, i,j都为1,while逻辑没进去,于是[2,1]被交换成[1,2],而正确的时候应该i=1 j=0
【代码】
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
p = nums[0]
i, j = 0, len(nums)-1
while i<j:
while i<len(nums) and nums[i]>=p: i+=1
while j>=0 and nums[j]<p: j-=1
if i<j:
nums[i], nums[j] = nums[j], nums[i]
nums[0], nums[j] = nums[j], nums[0]
if j+1==k:
return p
elif j+1>k:
return self.findKthLargest(nums[:j], k)
else:
return self.findKthLargest(nums[j+1:], k-j-1)
【discussion】
更方便的写法 虽然运行时间和空间都更大
class Solution:
def findKthLargest(self, nums, k):
pivot = nums[0]
left = [l for l in nums if l < pivot]
equal = [e for e in nums if e == pivot]
right = [r for r in nums if r > pivot]
if k <= len(right):
return self.findKthLargest(right, k)
elif (k - len(right)) <= len(equal):
return equal[0]
else:
return self.findKthLargest(left, k - len(right) - len(equal))
Given an array of integers nums containing n + 1 integers where each integer is in the range [1, n] inclusive.
There is only one duplicate number in nums, return this duplicate number.
Can you solve the problem using only constant, O(1) extra space?
【题意】
一个列表中有n+1个数,数的大小在[1,n]之间,其中有一个数重复出现了多次,找出这个重复的数
【思路】
因为数的大小有限制 所以可以直接开个列表来存个数 然后再遍历一下 这样时间是O(N) 但空间开销做不到O(1)
【代码】
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
cnt = [0 for i in nums]
for x in nums:
cnt[x]+=1
for i,x in enumerate(cnt):
if x>1:
return i
【discussion】
神解法...... [link] 看做是图 找回环的入口点
1)比如[2 3 1 1] 这个路径指向就是 0->2 1->3 2->1 3->1 从0开始进入就是0->2->1->3->1->3->1....
第一个while循环停在了1
第二个while循环 留一个slow在继续3->1->3->1... 再开一个finder 2->1->3->1->3...
2)比如[1,3,4,2,2] 路径是0->1->3->2->4->2->4->2.....
第一个while循环停在了4
第二个while循环 留一个slow在继续2->4->2->4... 再开一个finder 1->3->2->4->2->4...
def findDuplicate(self, nums):
slow = fast = finder = 0
while True:
slow = nums[slow]
fast = nums[nums[fast]]
if slow == fast:
while finder != slow:
finder = nums[finder]
slow = nums[slow]
return finder
Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).
For example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its level order traversal as:
[
3,
[9,20],
[15,7]
]
【题意】
二叉树按行输出
【代码】
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if not root:
return []
ans, tmp_nodes = [], [root]
while len(tmp_nodes):
ans.append([x.val for x in tmp_nodes])
new_nodes = []
for x in tmp_nodes:
if x.left: new_nodes.append(x.left)
if x.right: new_nodes.append(x.right)
tmp_nodes = new_nodes[::]
return ans
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right, which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
Example 1:
Input: grid = [[1,3,1],[1,5,1],[4,2,1]]
Output: 7
Explanation: Because the path 1 → 3 → 1 → 1 → 1 minimizes the sum.
Example 2:
Input: grid = [[1,2,3],[4,5,6]]
Output: 12Constraints:
m == grid.length
n == grid[i].length
1 <= m, n <= 200
0 <= grid[i][j] <= 100
【题意】
从网格的左上角走到右下角所经过数字和的最小值
【思路】
DP
这次写了一维的DP 因为每行都是从上一行或从左边转来,而上一行就是前一步得到的dp值,所以只开一个行向量长度的dp数组就够了
初始化dp时 除了第一个位置取0 其他取一个很大的值 这样强制在第一行的位置能全部从左边转移 有个坑点是 这个很大的值我开始就取101(矩阵上元素的最大值)但其实应该取n*101 这样才能保证在加和第一行的之后一直小于这个MAX
【代码】
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
dp = [0] + [m*n*101 for _ in range(n-1)]
for i in range(m):
for j in range(n):
dp[j] = min(dp[j], dp[j-1])+grid[i][j]
return dp[n-1]
A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).
How many possible unique paths are there?
【题意】
和上面一题几乎是一毛一样的emmm 唯一不同就是从路径和变成了路径条数
【代码】
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [1 for _ in range(n)]
for i in range(1, m):
for j in range(1,n):
dp[j] = dp[j] + dp[j-1]
return dp[n-1]
Given n, how many structurally unique BST's (binary search trees) that store values 1 ... n?
Example:
Input: 3
Output: 5
Explanation:
Given n = 3, there are a total of 5 unique BST's:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3Constraints:
1 <= n <= 19
【题意】
给一个数n 问用1~n组成的二叉搜索树有几种
【思路】
因为是二叉搜索树,所以其实只要确定了树长什么样,上面的节点就是固定的。所以其实相当于从一个完整的满二叉树上挑出一个包含根节点的子树有多少种挑法的问题。
把挑节点想成挑树边会更容易,于是我把问题这样定义(几层是指节点多少层):
1层的树选0条边:1种
2层的树选1条边:2种
3层的树选2条边:5种
...
假设现在要选n条边,对于一棵大树,如果选根左边那条边要、右边不要,就从更小的那棵左子树上选n-1条边,反之,右子树上选n-1条边 ans = select[i-1]*2
;如果根左右的两条边都要,那就从两棵更小的子树上选出n-2条(remain条)边就可以,选法可以有0 + remain、1 + remain-1、2 + remain-2 ... remain + 0这么多种 ans += select[sel] * select[remain-sel]
【代码】
class Solution:
def numTrees(self, n: int) -> int:
if n==1: return 1
if n==2: return 2
select = [1, 2]
n = n-1
for i in range(2, n+1):
ans = select[i-1]*2
remain = i-2
for sel in range(0, remain+1):
ans += select[sel] * select[remain-sel]
select.append(ans)
return select[-1]
【discussion】
哎我去 是我sb了 第一层完全不需要单独区分的 就直接是0 + n-1、1 + n-1-1、...、n-1 + 0 的问题 OTL
# DP
def numTrees1(self, n):
res = [0] * (n+1)
res[0] = 1
for i in xrange(1, n+1):
for j in xrange(i):
res[i] += res[j] * res[i-1-j]
return res[n]
啊这居然就是Catalan数 看了下它的wikipidia感觉还蛮有趣的
# Catalan Number (2n)!/((n+1)!*n!)
def numTrees(self, n):
return math.factorial(2*n)/(math.factorial(n)*math.factorial(n+1))
Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of the line i is at (i, ai) and (i, 0). Find two lines, which, together with the x-axis forms a container, such that the container contains the most water.
Notice that you may not slant the container.
Example 1:
Input: height = [1,8,6,2,5,4,8,3,7]
Output: 49
Explanation: The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.
Example 2:
Input: height = [1,1]
Output: 1
Example 3:
Input: height = [4,3,2,1,4]
Output: 16Constraints:
n = height.length
2 <= n <= 3 * 10^4
0 <= height[i] <= 3 * 10^4
【题意】
array代表了若干个隔板 在两个隔板之间倒水最多能倒多满
【思路】
今天这道题有点难
一开始想的就是 每个隔板都有可能成为短板 就以它为左边 去右边找最远的超过它高度的位置 这样O(N^2)不能接受
然后又想 那就不从左往右而是从下往上看 最终的水高一定会是某个板子的高度,所以遍历板子高度存下他们的idx得到一个 高度->idx表 但这样也是O(N^2) 然后就卡住了orz
看了discussion才发现 其实是一次遍历就能解决的:
在优先保证宽度的情况下 左右两个隔板尽量往高的取 如果最左边矮 就抛弃掉最左边的 如果最右边矮 就抛弃右边
这是因为:如果扔掉的是更高的那个 下一个遇到更高的 那么面积是宽度小了但高度不变,总之不可能得到更大的解了;如果遇到了一个更矮的 宽高都小了;如果扔掉矮的,就有可能遇到一个能使水面变高的
【代码】
def maxArea(self, height):
L, R, width, res = 0, len(height) - 1, len(height) - 1, 0
for w in range(width, 0, -1):
if height[L] < height[R]:
res, L = max(res, height[L] * w), L + 1
else:
res, R = max(res, height[R] * w), R - 1
return res
Given an encoded string, return its decoded string.
The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer.
You may assume that the input string is always valid; No extra white spaces, square brackets are well-formed, etc.
Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there won't be input like 3a or 24.
Example 1:
Input: s = "3[a]2[bc]"
Output: "aaabcbc"
Example 2:
Input: s = "3[a2[c]]"
Output: "accaccacc"
Example 3:
Input: s = "2[abc]3[cd]ef"
Output: "abcabccdcdcdef"
【题意】
看example即可OTL
【思路】
一看就知道是栈 难的是coding 本来还想着递归 看了下答案写的挺简洁的 就是难想555
栈里存的是之前的str和当前这个[]要重复的次数
遇到 [ 的时候是新的开始 把之前的str压栈 需要重复的次数已经知道了也压栈 然后一个个加字符 遇到 ] 之后当前的str和栈里的num乘上 再前面补上pre_s 就是上一 [ ] 里的cur了
【代码】
class Solution:
def decodeString(self, s: str) -> str:
sta = []
num, cur_s = 0, ""
for x in s:
if x.isdigit():
num = num*10+int(x)
elif x=='[':
sta.append(cur_s)
sta.append(num)
num, cur_s = 0, ""
elif x==']':
cur_num = sta.pop()
pre_s = sta.pop()
cur_s = pre_s + cur_num*cur_s
else:
cur_s += x
return cur_s
The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night.
Determine the maximum amount of money the thief can rob tonight without alerting the police.
Example 1:
Input: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
Output: 7
Explanation: Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
Example 2:
Input: [3,4,5,1,3,null,1]
3
/ \
4 5
/ \ \
1 3 1
Output: 9
Explanation: Maximum amount of money the thief can rob = 4 + 5 = 9.
【题意】
又来抢劫了 相邻的节点不能同时抢 问能抢到的最大值
【思路】
一看到树上dp就有点懵了 但其实仔细想想没那么难 唯一的不同是没法用一维dp存 就是怎么记录节点的问题 因为是二叉树 所以我就用满二叉数的idx作为标记了 这样就开个dict来存dp的结果
维护一下“取”或“不取”两种情况 对每个idx都开个长度2的数组 0表示不取1表示取 如果父节点不取,那子节点取不取都行,直接两个max相加;如果父节点取,那么只能子节点都不取 再加上父节点的value
【代码】
class Solution:
def rob(self, root: TreeNode) -> int:
dp = {}
def dfs(root, idx):
if root==None:
return [0, 0]
if idx in dp:
return dp[idx]
l, r = dfs(root.left, idx*2), dfs(root.right, idx*2+1)
dp[idx] = [max(l)+max(r), root.val+l[0]+r[0]]
return dp[idx]
return max(dfs(root, 1))
【discussion】
不记录dp 直接递归
想了想似乎每个结点递归完也就只访问一次 确实不需要233 解法是一毛一样的
class Solution(object):
def rob(self, root):
def superrob(node):
if not node: return (0, 0)
left, right = superrob(node.left), superrob(node.right)
rob = node.val + left[1] + right[1]
no_rob = max(left) + max(right)
return (rob, no_rob)
return max(superrob(root))
Given a characters array tasks, representing the tasks a CPU needs to do, where each letter represents a different task. Tasks could be done in any order. Each task is done in one unit of time. For each unit of time, the CPU could complete either one task or just be idle.
However, there is a non-negative integer n that represents the cooldown period between two same tasks (the same letter in the array), that is that there must be at least n units of time between any two same tasks.
Return the least number of units of times that the CPU will take to finish all the given tasks.
Example 1:
Input: tasks = ["A","A","A","B","B","B"], n = 2
Output: 8
Explanation:
A -> B -> idle -> A -> B -> idle -> A -> B
There is at least 2 units of time between any two same tasks.
Example 2:
Input: tasks = ["A","A","A","B","B","B"], n = 0
Output: 6
Explanation: On this case any permutation of size 6 would work since n = 0.
["A","A","A","B","B","B"]
["A","B","A","B","A","B"]
["B","B","B","A","A","A"]
...
And so on.
Example 3:
Input: tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2
Output: 16
Explanation:
One possible solution is
A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> idle -> idle -> A -> idle -> idle -> AConstraints:
1 <= task.length <= 104
tasks[i] is upper-case English letter.
The integer n is in the range [0, 100].
【题意】
两个相同任务之间需要间隔至少n时 问排完这些任务所需的最少时间
【思路】
我还记得这道题 两种情况:
1)全塞满 就是len(task)
2)没有塞满 只需要排好次数最多的 中间间隙不用去管一定能塞得下
原因:[link](我还问过建明为什么可以保证塞得下hhh)
【代码】
class Solution:
def leastInterval(self, tasks: List[str], n: int) -> int:
from collections import Counter
cnts = Counter(tasks).values()
cnt_max, max_num = max(cnts), 0
for x in cnts:
if x==cnt_max: max_num+=1
return max((cnt_max-1)*(n+1)+max_num, len(tasks))
【discussion】
统计出现最多次数的有几个字母可以用task_counts.count()
class Solution:
def leastInterval(self, tasks: List[str], n: int) -> int:
tasks_count = list(collections.Counter(tasks).values())
max_count = max(tasks_count)
max_count_tasks = tasks_count.count(max_count)
return max(len(tasks), (max_count-1)*(n+1)+max_count_tasks)
Implement a trie with insert, search, and startsWith methods.
Example:
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // returns true
trie.search("app"); // returns false
trie.startsWith("app"); // returns true
trie.insert("app");
trie.search("app"); // returns trueNote:
You may assume that all inputs are consist of lowercase letters a-z.
All inputs are guaranteed to be non-empty strings.
【思路】
用dict嵌套dict来模拟这个char树,当一个word完成的时候记录一下它是一个end点
有一点就是,我开始觉得每个节点都要有一个end标记,所以value应该是个(dict, bool)这种的,就写的有点繁琐。但其实py不用那么严苛,随便给一个特定的东西作为表征end的key,随时需要加再加就可以了
最终的数据结构是:
key是字母,value是一个dict 代表它的子树;dict里还可能有一个特殊标记‘-1’代表是否是word_end(所以dict里的key可能是一堆字母以及一个-1)
【代码】
class Trie:
def __init__(self):
"""
Initialize your data structure here.
"""
self.root = {}
self.word_end = -1
def insert(self, word: str) -> None:
"""
Inserts a word into the trie.
"""
node_now = self.root
for x in word:
if x not in node_now:
node_now[x] = {}
node_now = node_now[x]
node_now[self.word_end] = True
def search(self, word: str) -> bool:
"""
Returns if the word is in the trie.
"""
node_now = self.root
for x in word:
if x not in node_now:
return False
node_now = node_now[x]
if self.word_end in node_now:
return True
else:
return False
def startsWith(self, prefix: str) -> bool:
"""
Returns if there is any word in the trie that starts with the given prefix.
"""
node_now = self.root
for x in prefix:
if x not in node_now:
return False
node_now = node_now[x]
return True
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
Given a binary tree, flatten it to a linked list in-place.
For example, given the following tree:
1
/ \
2 5
/ \ \
3 4 6
The flattened tree should look like:
1
\
2
\
3
\
4
\
5
\
6
【题意】
给定一颗二叉树,按前序遍历的顺序转为单链表 并且以 TreeNode.right 作为链表的 next,要求操作是in-place的
【思路】
如果左右都拍平了 就能直接根->左->右 问题是不知道左的尾端在哪 写了dfs不太对劲
看discussion 发现可以用类似自底向上的做法,对于每个节点,每次都拿到右端拍平的结果(self.prev),把当前节点接上右端链。遍历每个节点是按右、左、根的方式往上一个个。
Let's see what is happening with this code.
Node(4).right = None
Node(4).left = None
prev = Node(4)Node(3).right = Node(4) (prev)
Node(3).left = None
prev = Node(3)->Node(4)Node(1).right = prev = Node(3) -> Node(4)
Node(1).left = None
prev = Node(1)->Node(3)->Node(4) (Which is the answer)The answer use self.prev to recode the ordered tree of the right part of current node.
Remove the left part of current node
【代码】
def __init__(self):
self.prev = None
def flatten(self, root):
if not root:
return None
self.flatten(root.right)
self.flatten(root.left)
root.right = self.prev
root.left = None
self.prev = root
Given preorder and inorder traversal of a tree, construct the binary tree.
Note:
You may assume that duplicates do not exist in the tree.
For example, given
preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]
Return the following binary tree:
3
/ \
9 20
/ \
15 7
【题意】
根据前序遍历和中序遍历还原树
【代码】
# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
if len(preorder)==0:
return None
root = preorder[0]
mid = inorder.index(root)
root = TreeNode(root)
root.left = self.buildTree(preorder[1:mid+1], inorder[:mid])
root.right = self.buildTree(preorder[mid+1:], inorder[mid+1:])
return root
Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue.
Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.
Follow up:
Could you solve this problem without using the library's sort function?
Could you come up with a one-pass algorithm using only O(1) constant space?
Example 1:
Input: nums = [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
Example 2:
Input: nums = [2,0,1]
Output: [0,1,2]
Example 3:
Input: nums = [0]
Output: [0]
Example 4:
Input: nums = 1
Output: 1Constraints:
n == nums.length
1 <= n <= 300
nums[i] is 0, 1, or 2.
【题意】
一个只有0,1,2的乱序数组 变成000111222的形式
要求in-place,O(1)空间O(N)时间
【思路】
双指针 先把0和2 swap,保证02是完全分开的,中间还会有一些散落的1,变成000101112122这样,然后再swap 0和1、再swap 1和2
class Solution:
def sortColors(self, nums: List[int]) -> None:
def work(l_num, r_num):
i,j = 0, len(nums)-1
while i<j and i<len(nums) and j>=0:
while i<len(nums) and nums[i]!=l_num:
i+=1
while j>=0 and nums[j]!=r_num:
j-=1
if i<j and i<len(nums) and j>=0:
nums[i], nums[j] = nums[j], nums[i]
work(2, 0)
work(1, 0)
work(2, 1)
【discussion】
三指针的做法
- red 指针:指向红色区域的右边界。
- white 指针:当前正在处理的元素。
- blue 指针:指向蓝色区域的左边界。
主要是动中间那个指针,如果中间指到1,说明没问题直接++;如果是0,要和头指针swap,两个都++;如果是2,就和尾指针swap,尾--,中间不动(因为是从前往后遍历 能保证头指针一定是指向对的都是0 但尾指针不能保证是2)
def sortColors(self, nums):
red, white, blue = 0, 0, len(nums)-1
while white <= blue:
if nums[white] == 0:
nums[red], nums[white] = nums[white], nums[red]
white += 1
red += 1
elif nums[white] == 1:
white += 1
else:
nums[white], nums[blue] = nums[blue], nums[white]
blue -= 1
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.
Example 1:
Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.
Example 2:
Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.
【思路】
很容易可以开根一下然后找到所有比n小的平方数 但要怎么从中可重复选取地挑出最少 仿佛像个完全背包的样子(?) 总之老实写记忆搜索能过
【代码】
class Solution:
def numSquares(self, n: int) -> int:
squ = math.sqrt(n)
nums = []
for i in range(int(squ), 0, -1):
nums.append(i*i)
dp = [-1 for i in range(n+1)]
def dfs(n):
if n==0:
return 0
if dp[n] != -1:
return dp[n]
ans = n
for x in nums:
if x<=n:
ans = min(ans, 1+dfs(n-x))
dp[n] = ans
return ans
return dfs(n)
【discussion】
有个神奇的定理 一切数字都是4个平方数的和!(平方数指包括0) 所以答案只有1,2,3,4种可能,所以只要一一判断就好了 [原link] (是拉格朗日定理(四数平方和))
ps: 果然一旦遇到数学题的评论区就跟开了挂一样乐趣无边orz
class Solution:
def numSquares(self, n):
if int(sqrt(n))**2 == n: return 1
for j in range(int(sqrt(n)) + 1):
if int(sqrt(n - j*j))**2 == n - j*j: return 2
while n % 4 == 0:
n >>= 2
if n % 8 == 7: return 4
return 3
Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. Return the answer in any order.
A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.Example 1:
Input: digits = "23"
Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
Example 2:
Input: digits = ""
Output: []
Example 3:
Input: digits = "2"
Output: ["a","b","c"]
【题意】
手机上的数字键盘输入一组数字后可能得到的字符串
【思路】
当遍历到每一个新的数字的时候,就把之前得到的ans拿出来一个个加上它对应的字符
【代码】
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
num_dic = {"2": ["a","b","c"],
"3": ["d","e","f"],
"4": ["g","h","i"],
"5": ["j","k","l"],
"6": ["m","n","o"],
"7": ["p","q","r","s"],
"8": ["t","u","v"],
"9": ["w","x","y","z"]}
ans = []
for x in digits:
if len(ans)==0:
new_ans = num_dic[x]
else:
new_ans = []
for char in num_dic[x]:
new_ans += [pre+char for pre in ans]
ans = new_ans[::]
return ans
Given an m x n 2d grid map of '1's (land) and '0's (water), return the number of islands.
An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.Example 2:
Input: grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
Output: 3
【题意】
上下左右被视作连通 求孤岛数
【代码】
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
m, n = len(grid), len(grid[0])
grid = [["0"]*(n+2)] + [["0"]+g+["0"] for g in grid] + [["0"]*(n+2)]
def dfs(i, j):
grid[i][j]="0"
if grid[i-1][j]=="1": dfs(i-1, j)
if grid[i+1][j]=="1": dfs(i+1, j)
if grid[i][j-1]=="1": dfs(i, j-1)
if grid[i][j+1]=="1": dfs(i, j+1)
ans = 0
for i in range(1,m+1):
for j in range(1,n+1):
if grid[i][j]=="1":
ans+=1
dfs(i,j)
return ans
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
Example:
Input: [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]
【思路】
我还记得这题,就是给everyday记录可能的三个状态下当前拥有的value,状态有:hold、not hold and cooldown、not hold,转移的可能有如下多种
hold -----do nothing----->hold
hold -----sell----->notHold_cooldown
notHold -----do nothing -----> notHold
notHold -----buy-----> hold
notHold_cooldown -----do nothing----->notHold
在discussion里这种考虑转移的思路被tag为状态机(2333没毛病)
初始状态是not hold,价值为0,其他状态都是-MAX,最后返回卖掉股票的两种状态下的max value
【代码】
def maxProfit(self, prices):
notHold, notHold_cooldown, hold = 0, float('-inf'), float('-inf')
for p in prices:
hold, notHold, notHold_cooldown = max(hold, notHold - p), max(notHold, notHold_cooldown), hold + p
return max(notHold, notHold_cooldown)
You are given a binary tree in which each node contains an integer value.
Find the number of paths that sum to a given value.
The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes).
The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.
Example:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
Return 3. The paths that sum to 8 are:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
【题意】
在树上走任意path(只能竖直的往下走),使得value加和等于给定的sum值,问有几种path
【思路】
可以像前缀和那样的思路,先把从root走到当前的和求出来,然后再在竖直方向上的俩node之间对减
但怎么维护node上的value和以及怎么记录是不是在一个branch上都是个问题,看了discussion发现可以这样做:
不用以node作为存值的key,而是以当前拥有的path value作为key,当走到一个node的时候,看看当前path value-target的值是不是存在dict里,dict的value是这个path value出现了几次,所以ans += cache[currPathSum-target]
;然后把当前的path value加入dict,继续左右子树dfs,这个node处理完之后之后就会进入另一个branch,所以要把当前的path value删去 cache[currPathSum] -= 1
BTW,这个dict里get()函数的写法值得学习
【代码】
class Solution(object):
def pathSum(self, root, target):
self.result = 0
cache = {0:1}
self.dfs(root, target, 0, cache)
return self.result
def dfs(self, root, target, currPathSum, cache):
if root is None:
return
currPathSum += root.val
oldPathSum = currPathSum - target
self.result += cache.get(oldPathSum, 0)
cache[currPathSum] = cache.get(currPathSum, 0) + 1
self.dfs(root.left, target, currPathSum, cache)
self.dfs(root.right, target, currPathSum, cache)
# when move to a different branch, the currPathSum is no longer available, hence remove one.
cache[currPathSum] -= 1
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”
Example 1:
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
Output: 3
Explanation: The LCA of nodes 5 and 1 is 3.
Example 2:
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
Output: 5
Explanation: The LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition.
Example 3:
Input: root = [1,2], p = 1, q = 2
Output: 1Constraints:
The number of nodes in the tree is in the range [2, 105].
-109 <= Node.val <= 109
【题意】
找两个节点的最近公共父节点
【思路】
对于每个node,看看这两个节点是不是分别在它的左右子树上(或者这个node就是两个节点之一),如果是,那这个node就是所求的,如果不是,就继续看那棵子树,这样做法似乎是O(N^2)的,主要是要写find()函数有点麻烦
另一个想法是,先遍历一遍,给每个node找到它的父节点,然后就能拿到这两个node走到root的两条path,就可以找交汇点了
然后看了discussion,再次被震撼到了。思路确实还是我的前一种思路,但这5行写完我枯了。这个find函数其实是不用写的,就直接用这个找LCA的函数:
如果left找不到,right也找不到,那就是这棵树里不存在这俩节点返回None;如果left、right里都找到了(因为一个node自己就是自己的LCA),那就是返回遍历到的当前这个节点;如果只找到在一边里,那就是这一边的LCA。
【代码】
def lowestCommonAncestor(self, root, p, q):
if root in (None, p, q): return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left == None and right == None:
return None
if left != None and right != None:
return root
return right if left == None else left
You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.
Find out how many ways to assign symbols to make sum of integers equal to target S.
Example 1:
Input: nums is [1, 1, 1, 1, 1], S is 3.
Output: 5
Explanation:
-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3
There are 5 ways to assign symbols to make the sum of nums be target 3.
【思路】
开始以为是啥神奇题目,但其实正常DP就行了
看当前这个数如果取正,后面能有多少种得到target-nums[idx]
的,如果取负,后面能有多少种能得到target+nums[idx]
的;唯一不同就是dict的key是idx和target都要记
【代码】
class Solution:
def findTargetSumWays(self, nums: List[int], S: int) -> int:
dp = {}
def dfs(target, idx):
if idx>=len(nums):
if target==0:
return 1
else:
return 0
if (idx,target) in dp:
return dp[(idx,target)]
pos = dfs(target-nums[idx], idx+1)
nag = dfs(target+nums[idx], idx+1)
dp[(idx, target)] = pos+nag
return pos+nag
return dfs(S,0)
【discussion】
正常遍历式的dp可以这样写
对于每个遍历到的x,把上一步得到的所有可能的sum值拿出来+x和-x,这个新的 sum±x 出现的次数就+=count[sum]
def findTargetSumWays(self, nums, S):
count = defaultdict(int)
count[0] = 1
for x in nums:
step = defaultdict(int)
for y in count:
step[y + x] += count[y]
step[y - x] += count[y]
count = step
return count[S]
Given the head of a linked list, return the list after sorting it in ascending order.
Follow up: Can you sort the linked list in O(n logn) time and O(1) memory (i.e. constant space)?
Example 1:
Input: head = [-1,5,3,4,0]
Output: [-1,0,3,4,5]
【题意】
给一个链表将其排序,要求O(NlogN)时间 O(1)空间
【思路】
如果正常做应该是O(N^2),就是一个个遍历保证前面的都是有序的然后找地方插;如果NlogN就用个列表存下来前面见过的然后二分找位置,可以直接swap值而不指针插,但这样空间不满足;快排的话不太好双指针往中间找
看了下discussion,居然是归并排序0.0 害,忘了它也是NlogN
先用slow/fast指针找到中间的位置 然后就可以分治了,而且链表的好处就是merge的时候可以不用再开新的空间而是直接用指针指就行了(不过因为需要递归调用似乎空间也并不完全是O(1)? emmm)
BTW,merge的时候当还剩一部分尾巴的时候python是可以直接p.next = l or r
的 阔以学习
【代码】
class Solution:
def sortList(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
fast, slow = head.next, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
start = slow.next
slow.next = None
l, r = self.sortList(head), self.sortList(start)
return self.merge(l,r)
def merge(self, l, r):
head = p = ListNode(0)
while l and r:
if l.val < r.val:
p.next = l
l = l.next
else:
p.next=r
r = r.next
p = p.next
p.next = l or r
return head.next
Given a non-empty array nums containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
Example 1:
Input: nums = [1,5,11,5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and 11.
Example 2:
Input: nums = [1,2,3,5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.
【题意】
判断一组数能不能被拆成求和相同的两组
【思路】
把所有的数加起来得到的target/2然后就是“一组数中能不能挑出一些数求和组成target”的问题
【代码】
class Solution:
def canPartition(self, nums: List[int]) -> bool:
target = sum(nums)
if target%2==1:
return False
target = target//2
return self.canSum(target, nums)
def canSum(self, target, nums):
dp = {}
def dfs(target, idx):
if (target, idx) in dp:
return dp[(target, idx)]
if target==0:
return True
if idx==len(nums)-1:
return target==nums[idx]
dp[(target, idx)] = dfs(target, idx+1) or dfs(target-nums[idx], idx+1)
return dp[(target, idx)]
return dfs(target, 0)
【discussion】
如果会写dp的话能省一维的空间
boolean[] dp=new boolean [sum+1];
dp[0]=true;
for(int k:nums){
for(int j=sum;j>=k;j--)
dp[j]=dp[j]||dp[j-k];
}
return dp[sum];
Given a string s and a non-empty string p, find all the start indices of p's anagrams in s.
Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100.
The order of output does not matter.
Example 1:
Input:
s: "cbaebabacd" p: "abc"
Output:
[0, 6]
Explanation:
The substring with start index = 0 is "cba", which is an anagram of "abc".
The substring with start index = 6 is "bac", which is an anagram of "abc".
Example 2:
Input:
s: "abab" p: "ab"
Output:
[0, 1, 2]
Explanation:
The substring with start index = 0 is "ab", which is an anagram of "ab".
The substring with start index = 1 is "ba", which is an anagram of "ab".
The substring with start index = 2 is "ab", which is an anagram of "ab".
【题意】
找出s中和p相同字母的子串的start idx
【思路】
滑动窗口的思想,用一个dict来存这个窗口里的字母数,并且神奇地发现defaultdict可以直接判“==”,就除了value为0时pop一下就没啥问题了
【代码】
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
from collections import defaultdict
target, now = defaultdict(int), defaultdict(int)
for x in p: target[x]+=1
ans = []
for i, x in enumerate(s):
if i-len(p)>=0:
y = s[i-len(p)]
now[y]-=1
if now[y]==0:
now.pop(y)
now[x]+=1
if target==now:
ans.append(i-len(p)+1)
return ans
There are a total of numCourses courses you have to take, labeled from 0 to numCourses-1.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
Example 1:
Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.
Example 2:
Input: numCourses = 2, prerequisites = [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should
also have finished course 1. So it is impossible.
【题意】
给出课程idx和它必修的前序课程idx,问能不能学完所有课
【思路】
其实就是拓扑序,每次去找没有入度的节点集合,如果不存在这样的节点且又没有遍历完则说明有循环就return false。但因为删掉节点有点麻烦,所以这里用mark来标记有没有被遍历过,为1是没有遍历为0是有遍历,这样方便之后直接用sum(mark[前序节点])==0?就能判断这个点是不是无入度。因为节点数是10^5,每次去找无入度点的时候不能全部搜一遍,而因为新冒出来的点只可能是由于之前删了一部分前序节点导致的,所以只有之前删掉的节点的后继会组成一个candidate集合,从这里面找就不会出事了
【代码】
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
from collections import defaultdict
point_to, tobe_point = defaultdict(list), defaultdict(list)
for pair in prerequisites:
x, y = pair[0], pair[1]
point_to[y].append(x)
tobe_point[x].append(y)
mark, start = [1 for _ in range(numCourses)], []
for x in range(numCourses):
if x not in tobe_point:
start.append(x)
if len(start)==0:
return False
while len(start):
for x in start: mark[x]=0
new_start = []
candis = sum([point_to[x] for x in start], [])
for c in candis:
if sum([mark[i] for i in tobe_point[c]])==0:
new_start.append(c)
start = new_start[::]
if sum(mark)==0:
return True
else:
return False
【discussion】
递归的写法 感觉有一丢丢绕0.0
class Solution(object):
def canFinish(self, numCourses, prerequisites):
graph = [[] for _ in range(numCourses)]
visited = [0 for _ in range(numCourses)]
for pair in prerequisites:
x, y = pair
graph[x].append(y)
for i in range(numCourses):
if not self.dfs(graph, visited, i):
return False
return True
def dfs(self, graph, visited, i):
# if ith node is marked as being visited, then a cycle is found
if visited[i] == -1:
return False
# if it is done visted, then do not visit again
if visited[i] == 1:
return True
# mark as being visited
visited[i] = -1
# visit all the neighbours
for j in graph[i]:
if not self.dfs(graph, visited, j):
return False
# after visit all the neighbours, mark it as done visited
visited[i] = 1
return True
Given an array of integers nums and an integer k, return the total number of continuous subarrays whose sum equals to k.
Example 1:
Input: nums = [1,1,1], k = 2
Output: 2
Example 2:
Input: nums = [1,2,3], k = 3
Output: 2
【题意】
一个列表中 有多少个子串的和是k
【思路】
开始想的是滑动窗口的思想 少了就补多了就扔 但是恰好等于k的情况比较尴尬 比如一个[0,1,0]遍历到1这个位置的时候,在窗口是 [ 0,1 ] 的时候改扔,在 [ 1 ] 的时候该捡()就一直没写出来
后来看了discussion才发现,woc,和之前的一题特别像,就用一个dict来维护之前看到过的窗口的sum值,然后类似前缀和那样求。初始化的时候为了之后求前缀和要先存一个key=0的进去
【代码】
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
count = {0:1}
now, ans = 0,0
for x in nums:
now += x
ans += count.get(now-k, 0)
count[now] = count.get(now, 0)+1
return ans
Write an efficient algorithm that searches for a target value in an m x n integer matrix. The matrix has the following properties:
Integers in each row are sorted in ascending from left to right.
Integers in each column are sorted in ascending from top to bottom.
Example 1:
Input: matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
Output: true
【题意】
在一个矩阵中找target元素是否存在,矩阵是个上下递增左右递增的矩阵
【思路】
小了就往右下找 有找过的话就mark一下
【代码】
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
m, n = len(matrix), len(matrix[0])
mark = [[0 for i in range(n)] for j in range(m)]
i, j = 0, 0
def dfs(i, j):
mark[i][j]=1
if matrix[i][j]==target:
return True
if matrix[i][j]<target:
a, b = False, False
if i+1<m and j<n and not mark[i+1][j]:
a = dfs(i+1, j)
if i<m and j+1<n and not mark[i][j+1]:
b = dfs(i, j+1)
return a or b
if matrix[i][j]>target:
return False
return dfs(0,0)
【discussion】
看了才发现自己思维受限orz 这题是可以从右上角开始找的。大了往左小了往下,这样就可以避免dfs的开销直接一遍搜到位
Given an integer array nums, return the length of the longest strictly increasing subsequence.
A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements. For example, [3,6,2,7] is a subsequence of the array [0,3,1,6,2,2,7].
Example 1:
Input: nums = [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
Example 3:
Input: nums = [7,7,7,7,7,7,7]
Output: 1
【题意】
求最长递增子序列的长度
【思路】
dp,从前面所有比它小的转移过来,而且最后一个数不一定是子串的最后一位,所以返回的是max(dp)。O(N^2)的复杂度,勉勉强强,下面代码就是这个写法
(后来又想 如果长度越长且最后一个数越小的 是不是就可以完全替代掉同样长度但最后一个数越大的 这样如果以片段长度为key的话只对应一个value就好了 这样能节省一定时间 但仍然不是NlogN的
【代码】
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
def find(idx):
for i in range(1, idx+1):
if nums[idx-i]<nums[idx]:
return idx-i
return -1
dp = [1 for _ in range(len(nums))]
for idx,x in enumerate(nums):
for i in range(1, idx+1):
if nums[idx-i]<x:
dp[idx] = max(dp[idx], dp[idx-i]+1)
return max(dp)
【discussion】
看了才发现 思路就是我后来那个思路0.0 解释如下
tails is an array storing the smallest tail of all increasing subsequences with length i+1 in tails[i].
For example, say we have nums =
len = 1 : [4], [5], [6], [3] => tails[0] = 3
len = 2 : [4, 5], [5, 6] => tails[1] = 5
len = 3 : [4, 5, 6] => tails[2] = 6
We can easily prove that tails is a increasing array. Therefore it is possible to do a binary search in tails array to find the one needs update.
Each time we only do one of the two:
(1) if x is larger than all tails, append it, increase the size by 1
(2) if tails[i-1] < x <= tails[i], update tails[i]
但为啥能NlogN呢,是在于找更新的位置,找tails[i-1] < x <= tails[i]
用二分搜这个x要更新在哪
def lengthOfLIS(self, nums):
tails = [0] * len(nums)
size = 0
for x in nums:
i, j = 0, size
while i != j:
m = (i + j) // 2
if tails[m] < x:
i = m + 1
else:
j = m
tails[i] = x
size = max(i + 1, size)
return size
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
Note:
The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
Example 1:
Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
Example 2:
Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.
【题意】
问s字符串能不能用wordDict里的拼接组成
【思路】
dp数组记录从这个idx开始之后的能不能被成功构成;因为考虑到起始的时候需要看长度1的有没有、长度2的有没有...这样复杂度有点高,于是先维护一个dic来存以每个单词开头那个char为key的word list,知道开头要做匹配就在这个word list里面搜就好了
【代码】
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
from collections import defaultdict
dic = defaultdict(list)
for x in wordDict:
dic[x[0]].append(x)
dp = [-1 for _ in range(len(s))]
def dfs(idx):
if idx>=len(s):
return True
if dp[idx]!=-1:
return dp[idx]
ans = False
for x in dic[s[idx]]:
leng = len(x)
if x==s[idx : idx+leng]:
ans = ans or dfs(idx+leng)
dp[idx] = ans
return ans
return dfs(0)
【discussion】
巨 简 洁,很美观√
def word_break(s, words):
d = [False] * len(s)
for i in range(len(s)):
for w in words:
if w == s[i-len(w)+1:i+1] and (d[i-len(w)] or i-len(w) == -1):
d[i] = True
return d[-1]
Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input.
Example 1:
Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].
Example 2:
Input: intervals = [[1,4],[4,5]]
Output: [[1,5]]
Explanation: Intervals [1,4] and [4,5] are considered overlapping.
【题意】
把相互交叠的区间合并
【思路】
先sort一下然后一个个合并就好了
【代码】
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
if not intervals:
return []
intervals.sort(key=lambda x: x[0])
merged = [intervals[0]]
for current in intervals[1:]:
last_merged = merged[-1]
if current[0] <= last_merged[1]:
last_merged[1] = max(last_merged[1], current[1])
else:
merged.append(current)
return merged
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. Internally, pos is used to denote the index of the node that tail's next pointer is connected to. Note that pos is not passed as a parameter.
Notice that you should not modify the linked list.
Example 1:
Input: head = [3,2,0,-4], pos = 1
Output: tail connects to node index 1
Explanation: There is a cycle in the linked list, where tail connects to the second node.
【题意】
找环的入口点。要求不修改链表,返回这个入口节点
【思路】
这玩意 我似乎在之前一个完全不想干的题目见过解法(滑稽.jpg
先slow和fast两个指针找到重合点,再一个finder指针从起始点开始和slow一起往后挪到重合。注意这个解法是一定要有一个从外部指向环的入口,所以起始点需要另开一个节点指向head。
能这么做的原因是:
Consider the following linked list, where E is the cylce entry and X, the crossing point of fast and slow.
H: distance from head to cycle entry E
D: distance from E to X
L: cycle length
_____
/ \
head_____H______E \
\ /
X_____/
If fast and slow both start at head, when fast catches slow, slow has traveled H+D and fast 2(H+D).
Assume fast has traveled n loops in the cycle, we have:
2H + 2D = H + D + nL --> H + D = nL --> H = nL - D
Thus if two pointers start from head and X, respectively, one first reaches E, the other also reaches E.
【代码】
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
if not head or not head.next:
return None
slow = fast = finder = ListNode(0)
slow.next = fast.next = finder.next = head
while True:
if not fast or not fast.next:
return None
slow, fast = slow.next, fast.next.next
if slow == fast:
while slow != finder:
slow, finder = slow.next, finder.next
return finder
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
The Linked List is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:
val: an integer representing Node.val
random_index: the index of the node (range from 0 to n-1) where random pointer points to, or null if it does not point to any node.
Example 1:
Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]
【题意】
一个链表除了有next还有个random指针,要求deep copy这个链表
【思路】
就,扫一遍得到next,再扫一遍得到random. 唯一麻烦的是random这个没法直接now.random=原来的.random 需要拿个dict记录一下原始node对应的新node
【代码】
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
pre_head = Node(0)
ordin_now, now = head, pre_head
dic = {}
while ordin_now:
now.next = Node(ordin_now.val)
dic[ordin_now] = now.next
ordin_now, now = ordin_now.next, now.next
ordin_now, now = head, pre_head.next
while ordin_now:
if ordin_now.random:
now.random = dic[ordin_now.random]
ordin_now, now = ordin_now.next, now.next
return pre_head.next
【discussion】
可以写成一遍遍历提高速度,遇到指向后面random直接开一个扔进dict,之后next到这个位置的时候就在dict里找就行
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
node_dict = {}
new_ptr = dummy = Node(-1)
while head != None:
if head not in node_dict:
node_dict[head] = Node(head.val)
new_ptr.next = node_dict[head]
new_ptr = new_ptr.next
if head.random:
if head.random not in node_dict:
node_dict[head.random] = Node(head.random.val)
new_ptr.random = node_dict[head.random]
head = head.next
return dummy.next
用字典要多出O(N)的空间,这里有个O(1)空间复杂度的解法来了,缺点在于要遍历多遍
- Iterate the original list and duplicate each node. The duplicate
of each node follows its original immediately.- Iterate the new list and assign the random pointer for each
duplicated node.- Restore the original list and extract the duplicated nodes.
需要看原链接评论里的图比较清楚,写法上第三轮指针会有点绕
def copyRandomList(self, head):
# Insert each node's copy right after it, already copy .label
node = head
while node:
copy = RandomListNode(node.label)
copy.next = node.next
node.next = copy
node = copy.next
# Set each copy's .random
node = head
while node:
node.next.random = node.random and node.random.next
node = node.next.next
# Separate the copied list from the original, (re)setting every .next
node = head
copy = head_copy = head and head.next
while node:
node.next = node = copy.next
copy.next = copy = node and node.next
return head_copy
Given an m x n binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.
Example 1:
Input: matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
Output: 4
【题意】
找出矩阵中全是1的正方形的最大面积
【思路】
先前缀和得到所有idx的左上角面积,然后三个for(i,j,正方形长度k)求面积。可以在顶点i,j的地方先预判一下matrix[i][j]是不是1,会快一点
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
matrix = [list(map(int, row)) for row in matrix]
m,n = len(matrix), len(matrix[0])
sum_m = [[0 for i in range(n)] for j in range(m)]
ans = 0
for i in range(m):
row = 0
for j in range(n):
row += matrix[i][j]
sum_m[i][j] = row + (sum_m[i-1][j] if i-1>=0 else 0)
if row>0: ans=1
sum_m = [[0 for i in range(n)]]+sum_m
sum_m = [[0]+row for row in sum_m]
for i in range(1,m+1):
for j in range(1,n+1):
if matrix[i-1][j-1]==0:
continue
K = min(m-i, n-j)
for k in range(K+1):
area = sum_m[i+k][j+k]-sum_m[i+k][j-1]-sum_m[i-1][j+k]+sum_m[i-1][j-1]
if area==(k+1)*(k+1):
ans = max(ans,area)
return ans
【discussion】
上面那个O(N^3)实在有点慢,其实有O(N^2)的解法:不要记面积而是记长度
用一个dp数组记录以当前idx为右下角的全1正方形的最长长度,那么更新的时候就是dp[r+1][c+1] = min(dp[r][c], dp[r+1][c], dp[r][c+1]) + 1
(如果其中有一个是0那就是0+1,如果是三个正方形叠加那就是取它们最小的长度+1,画个图比较好理解)
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
if matrix is None or len(matrix) < 1:
return 0
rows = len(matrix)
cols = len(matrix[0])
dp = [[0]*(cols+1) for _ in range(rows+1)]
max_side = 0
for r in range(rows):
for c in range(cols):
if matrix[r][c] == '1':
dp[r+1][c+1] = min(dp[r][c], dp[r+1][c], dp[r][c+1]) + 1
max_side = max(max_side, dp[r+1][c+1])
return max_side * max_side
Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.
If target is not found in the array, return [-1, -1].
Follow up: Could you write an algorithm with O(log n) runtime complexity?
Example 1:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Example 2:
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
【题意】
一个递增排序的数组 找出target的首个位置和末个位置(如果出现的话),没出现就返回-1,要求时间O(logN)
【思路】
log级别那就只能二分了,找最左边和最右边的唯一区别就是缩减到mid时的边界怎么判;因为我的写法不能处理target在数组边界上的情况,于是要先前后各补一个-1
其实也可以直接用库函数啦orz
python的lower_bound是 bisect.bisect_left(nums, target)
(target的第一个位置),upper_bound是 bisect.bisect(nums, target)
(target末尾位置+1)
【代码】
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def searchL(l,r):
while l<r:
mid = (l+r)//2
if mid==l:
break
if nums[mid]>=target: r=mid
else: l=mid
if nums[r]==target: return r
else: return -1
def searchR(l,r):
while l<r:
mid = (l+r)//2
if mid==l:
break
if nums[mid]<=target: l=mid
else: r=mid
if nums[l]==target: return l
else: return -1
if len(nums)==0:
return [-1, -1]
nums = [-1]+nums+[-1]
l, r = searchL(0,len(nums)-1), searchR(0,len(nums)-1)
return [l-1 if l>0 else -1, r-1 if r>0 else -1]
【discussion】
分治dfs的写法 也是logN的
def searchRange(self, nums, target):
def search(lo, hi):
if nums[lo] == target == nums[hi]:
return [lo, hi]
if nums[lo] <= target <= nums[hi]:
mid = (lo + hi) / 2
l, r = search(lo, mid), search(mid+1, hi)
return max(l, r) if -1 in l+r else [l[0], r[1]]
return [-1, -1]
return search(0, len(nums)-1)
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
You may assume that you have an infinite number of each kind of coin.
Example 1:
Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1
Example 2:
Input: coins = 2, amount = 3
Output: -1
【题意】
有各种面值的硬币,问要组成amount块钱所需的最少个硬币数
【思路】
maya居然是完全背包 然而并不会背 写的记忆搜索
【代码】
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
if amount==0: return 0
from collections import defaultdict
dp = defaultdict(int)
def dfs(amount):
if amount==0: return 0
if amount<=0: return -1
if amount in dp:
return dp[amount]
ans = -1
for x in coins:
tmp = dfs(amount-x)
if tmp>=0:
ans = min(ans,tmp+1) if ans>0 else tmp+1
dp[amount]=ans
return ans
return dfs(amount)
【discussion】
dp的写法 时间差不多会快一些 但内存省了不少
class Solution:
def coinChange(self, coins, amount):
MAX = float('inf')
dp = [0] + [MAX] * amount
for i in range(1, amount + 1):
dp[i] = min([dp[i - c] if i - c >= 0 else MAX for c in coins]) + 1
return [dp[amount], -1][dp[amount] == MAX] ## 这 纯炫技写法...
Given an m x n board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cells, where "adjacent" cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.
Example 1:
Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
Output: true
Example 2:
Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
Output: true
【题意】
在一个char矩阵中找word这个单词是否存在(可以上下左右拐弯)
【思路】
dfs 放置mark之后记得出dfs的之后还原为0
【代码】
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
m, n = len(board), len(board[0])
board = [['-' for i in range(n+2)]]+[['-']+row+['-'] for row in board]+[['-' for i in range(n+2)]]
mark = [[0 for j in range(n+2)] for i in range(m+2)]
def dfs(x, y, word):
if not word:
return True
if mark[x][y]==1:
return False
if board[x][y]==word[0]:
mark[x][y]=1
ans = dfs(x,y-1,word[1:]) or dfs(x,y+1,word[1:]) or dfs(x-1,y,word[1:]) or dfs(x+1,y,word[1:])
mark[x][y]=0
return ans
else:
return False
for i in range(1, m+1):
for j in range(1, n+1):
ans = dfs(i, j, word)
if ans==True:
return ans
return False
【discussion】
用一整个mark矩阵有点费空间 能更节省空间的做法是用dict维护,或者直接修改board矩阵,比如用'#'占位
def exist(board, word):
if not board:
return False
rows, cols = len(board), len(board[0])
def dfs(r, c, index):
if index == len(word):
return True
if r < 0 or r >= rows or c < 0 or c >= cols or board[r][c] != word[index]:
return False
# Temporarily mark the cell as visited
temp = board[r][c]
board[r][c] = '#'
# Explore all possible directions: up, down, left, right
found = (dfs(r + 1, c, index + 1) or
dfs(r - 1, c, index + 1) or
dfs(r, c + 1, index + 1) or
dfs(r, c - 1, index + 1))
# Restore the cell's original value
board[r][c] = temp
return found
for i in range(rows):
for j in range(cols):
if board[i][j] == word[0] and dfs(i, j, 0):
return True
return False
Given the head of a linked list, remove the nth node from the end of the list and return its head.
Follow up: Could you do this in one pass?
Example 1:
Input: head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]
Example 2:
Input: head = 1, n = 1
Output: []
【题意】
去掉链表中的倒数第n个节点
【思路】
最直观的想法就是遍历两遍,第一遍先得到链表总数N,然后再一遍删掉N-n位置
如果要one pass的话,我想的是可以用dict存,value是每个node,key是对应链表中的位置,然后一旦知道N之后就能直接找到dict里的这个node修改它的next
【代码】
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
dic = {}
pre_head = ListNode(next=head)
now, idx = pre_head, 0
while now:
dic[idx]=now
idx, now = idx+1, now.next
dic[idx-n-1].next=dic[idx-n].next
return pre_head.next
【discussion】
又学了三种解法
第一种,从后往前递归地算反过来的index,然后把>n的前面的value往后挪(又是神奇的不动node动value的操作2333)
def removeNthFromEnd(self, head, n):
def index(node):
if not node:
return 0
i = index(node.next) + 1
if i > n:
node.next.val = node.val
return i
index(head)
return head.next
第二种,像前面那样,里头的函数还是在求反过来的index,但是这时候真的remove掉了i+1==n的node。
这个写法非常神奇,相当于是head.next = 下一轮的(head, head.next)[i+1 == n]
就是当i+1==n的时候head.next指向next.next
def removeNthFromEnd(self, head, n):
def remove(head):
if not head:
return 0, head
i, head.next = remove(head.next)
return i+1, (head, head.next)[i+1 == n]
return remove(head)[1]
第三种,先一个fast到正序的n-th位置,这时候再一个slow从头开始,俩个一起往前走,这样一直能维持一个n的宽度,于是fast走到尽头的时候slow就是倒数第n的位置。
def removeNthFromEnd(self, head, n):
fast = slow = head
for _ in range(n):
fast = fast.next
if not fast:
return head.next
while fast.next:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return head
You are given an integer array nums sorted in ascending order, and an integer target.
Suppose that nums is rotated at some pivot unknown to you beforehand (i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).
If target is found in the array return its index, otherwise, return -1.
Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Example 2:
Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1
【题意】
原本有一个单调递增的列表 随机取了个位置左右半边交换 在这个新列表中找值为target的所在位置
【思路】
nums的长度就5000 那岂不是可以直接for一边vans?orz
如果要logN,可以画两张图,一个躺着的直接三角形挪一半尖尖到右边,有两种情况分别讨论,要么mid位置切在大梯形上,要么切在尖尖上
【代码】
毫无任何脑容量的代码(甚至还 faster than 64.07% )
class Solution:
def search(self, nums: List[int], target: int) -> int:
for i, x in enumerate(nums):
if x==target:
return i
return -1
logN的代码(其实也就才快了4ms =。=)
class Solution:
def search(self, nums, target):
if not nums:return -1
low, high = 0, len(nums) - 1
while low <= high:
mid = (low + high) // 2
if target == nums[mid]:
return mid
if nums[low] <= nums[mid]:
if nums[low] <= target <= nums[mid]:
high = mid - 1
else:
low = mid + 1
else:
if nums[mid] <= target <= nums[high]:
low = mid + 1
else:
high = mid - 1
return -1
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.
Example 1:
Input: nums = [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: nums = [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.Constraints:
1 <= nums.length <= 3 * 10^4
0 <= nums[i][j] <= 10^5
【题意】
一个数组 每个位置的value代表着最多能前进几步,问从第一个位置出发能不能走到最后的位置
【思路】
(这 不 是 那 道 程 考 题吗...orz)
直觉是dp 只是要往回转移的有点多;不如正着想,从每个位置出发,往后一段距离都能覆盖到了,再往后遍历,就不断地扩大最右端。用right表示覆盖到的最右端,那么<=right这个遮罩下都是可达的,如果遍历超过了right就直接false
【代码】
class Solution:
def canJump(self, nums: List[int]) -> bool:
right = 0
if len(nums)<=1: return True
for i, x in enumerate(nums):
if i>right:
return False
right = max(right, x+i)
if right>=len(nums)-1:
return True
return False
【discussion】
逻辑更简洁的写法:因为只有遍历超过遮罩最右端才会false 其他情况都是true
def canJump(self, nums):
m = 0
for i, n in enumerate(nums):
if i > m: return False
m = max(m, i+n)
return True
Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.
Implement the LRUCache class:
LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
int get(int key) Return the value of the key if the key exists, otherwise return -1.
void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.
Follow up:
Could you do get and put in O(1) time complexity?
Example 1:
Input
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[2, [1, 1], [2, 2], 1, [3, 3], 2, [4, 4], 1, 3, 4]
Output
[null, null, null, 1, null, -1, null, -1, 3, 4]
Explanation
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1); // return 1
lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2); // returns -1 (not found)
lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
lRUCache.get(1); // return -1 (not found)
lRUCache.get(3); // return 3
lRUCache.get(4); // return 4
【题意】
初始化一个有n容量限制的cache 如果put超过了n个 就抛弃掉the least recently used key。要求get和put都在O(1)完成
【思路】
get函数好办 搞个dict就完事了,主要是put要怎么维护。
这个过程像是一个队列,被先塞进去的被先挤出来,中途有可能把某个捞起来从头塞进去。要维护这样一个链又能做到O(1)时间的话只可能是个链表,同时因为要把最后一个丢弃掉还得要有一个指向最后的指针,所以得是一个双向链表
这时候看了discussion发现一个神器
from collections import OrderedDict
天然就适合做一个LRU cache
popitem(last=True)
移除并返回一个 (key, value) 键值对。 如果 last 值为真,则按 LIFO 后进先出的顺序返回键值对,否则就按 FIFO 先进先出的顺序返回键值对。
move_to_end(key, last=True)
将现有 key 移动到有序字典的任一端。 如果 last 为真值(默认)则将元素移至末尾;如果 last 为假值则将元素移至开头。如果 key 不存在则会触发 KeyError
【代码】
class LRUCache:
from collections import OrderedDict
def __init__(self, capacity: int):
self.size = capacity
self.cache = OrderedDict()
def get(self, key: int) -> int:
if key not in self.cache: return -1
val = self.cache[key]
self.cache.move_to_end(key)
return val
def put(self, key: int, value: int) -> None:
if key in self.cache: del self.cache[key]
self.cache[key] = value
if len(self.cache) > self.size:
self.cache.popitem(last=False)
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example 1:
Input: l1 = [2,4,3], l2 = [5,6,4]
Output: [7,0,8]
Explanation: 342 + 465 = 807.
Example 3:
Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
Output: [8,9,9,9,0,0,0,1]
【题意】
将两个用链表表示的数字加和,链表是从末尾数字开始反过来的
【思路】
不就是大数求和orz 唯一比较麻烦的是求和进位的时候可能需要创建下一个节点,所以遍历的时候得用当前加和的前一个节点pre_now来遍历。我的写法是把l2加到l1上去
【代码】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
if l1==None or l2==None:
return l1 or l2
l1.val += l2.val
c = l1.val//10
l1.val%=10
pre_now, plus = l1, l2.next
while pre_now.next or plus:
if not pre_now.next:
pre_now.next = ListNode()
if not plus:
pre_now.next.val += c
else:
pre_now.next.val += plus.val+c
plus = plus.next
c = pre_now.next.val//10
pre_now.next.val%=10
pre_now = pre_now.next
if c:
pre_now.next = ListNode(c)
return l1
【discussion】
开一个新链表会比较统一好写一点
def addTwoNumbers(self, l1, l2):
carry = 0
res = n = ListNode(0)
while l1 or l2 or carry:
if l1:
carry += l1.val
l1 = l1.next
if l2:
carry += l2.val
l2 = l2.next
carry, val = divmod(carry, 10)
n.next = n = ListNode(val)
return res.next
这里有两个trick 一个是divmod
python divmod() 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。
另一个是神奇写法n.next = n = ListNode(val)
python里这样连等号的意思是,首先把第三个赋值给第一个,然后第二个指向第一个的地址
n.next = n = ListNode(val) means first n.next = ListNode(val) then n point to the same address
n = n.next = ListNode(val) means first n = ListNode(val) , now the n is ListNode(val), then n.next point to the address ListNode(val) which means point to itself!!!
(感受到了这个老哥的怒气
Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.
Example 1:
Input: [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.
Example 2:
Input: [-2,0,-1]
Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.
【题意】
问一个列表的连续子区间的最大乘积
【思路】
乘法和加法最大的不同就是没办法一旦和小于0就可以抛弃掉,如果为负数还有可能之后再遇到负数使乘积变大。所以既然没法O(1)空间那只能开个dp转移了,同时把此时最大的正数和最大的负数都存下来
【代码】
class Solution:
def maxProduct(self, nums: List[int]) -> int:
pos, nag = {}, {}
flag = False
for i, x in enumerate(nums):
if x>0:
if i-1 in pos:
pos[i] = pos[i-1]*x
else: pos[i]=x
if i-1 in nag:
nag[i] = nag[i-1]*x
elif x<0:
if i-1 in nag:
pos[i] = nag[i-1]*x
if i-1 in pos:
nag[i] = pos[i-1]*x
else: nag[i]=x
else:
flag = True
if pos:
return max(pos.values())
else:
return 0 if flag else max(nag.values())
【discussion】
算出前缀积和后缀积(如果前缀值是0就从当前位置开始重新算A[i] *= A[i - 1] or 1
),然后求前缀积列表和后缀积列表的最大值
如果最大子串发生在列表的最前一段或最后一段那确实是没错(包括中间有0的情况,相当于就是被切开的一样的子问题);
如果发生在中间(比如 奇数个负数+正数+奇数个负数)那其实还是要从正数往外扩连续吃前后的负数直到一端顶到头,事实上再仔细想想要不是有0分开就不会有发生在中间的情况orz
所以的确就是求前缀积列表和后缀积列表的最大值即为答案
def maxProduct(self, A):
B = A[::-1]
for i in range(1, len(A)):
A[i] *= A[i - 1] or 1
B[i] *= B[i - 1] or 1
return max(A + B)
Given an integer array nums, you need to find one continuous subarray that if you only sort this subarray in ascending order, then the whole array will be sorted in ascending order.
Return the shortest such subarray and output its length.
Example 1:
Input: nums = [2,6,4,8,10,9,15]
Output: 5
Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order to make the whole array sorted in ascending order.
Example 2:
Input: nums = [1,2,3,4]
Output: 0
【题意】
在一个列表中 只要sort其中一小段的连续子段就可以让整个列表是排序好的状态 问这个需要sort的子段长度
【思路】
只要知道整个sort的结果 就能比对哪些在正确位置上哪些不在 那就是第一个错误位置和最后一个错误位置的中间部分需要重排
【代码】
class Solution:
def findUnsortedSubarray(self, nums: List[int]) -> int:
res = sorted(nums)
l,r = 0,-1
for i in range(len(nums)):
if nums[i]!=res[i]:
l = i
break
for i in range(len(nums)-1, -1, -1):
if nums[i]!=res[i]:
r = i
break
return r-l+1
【discussion】
压行炫技时间到
def findUnsortedSubarray(self, nums):
res = [i for (i, (a, b)) in enumerate(zip(nums, sorted(nums))) if a != b]
return 0 if not res else res[-1] - res[0] + 1
另一种写法。这里用了个all
函数判断给定的可迭代参数中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。
def findUnsortedSubarray(self, nums):
is_same = [a == b for a, b in zip(nums, sorted(nums))]
return 0 if all(is_same) else len(nums) - is_same.index(False) - is_same[::-1].index(False)
更快的做法,不用sort。思路其实也很好想:从头往后找找到一个不递增的位置(同理从后往前找也找到一个不对劲的位置),然后拿着这个位置往前看,直到找到一个比它小的数,那么就说明前面位置切分处的idx找到了(同理后面也一样)
(代码有点长不放了orz)
Given a string s, find the length of the longest substring without repeating characters.
Example 1:
Input: s = "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.
Example 2:
Input: s = "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.Constraints:
0 <= s.length <= 5 * 104
s consists of English letters, digits, symbols and spaces.
【题意】
给一个字符串 找出没有重复字符的最长子串
【思路】
用滑动窗口的思路 每次吃一个字符 如果是不重复的就扩张 如果和之前的重复了就找到之前这个字符的位置把前面的都抛弃掉
难的是怎么维护这个窗口,当然可以用一个dict来存有哪些字符,value是idx,只是遇到旧字符需要抛弃前面一段的时候需要遍历dict又会变成O(N^2)
看了discussion才发现,其实根本不需要做抛弃操作,只要存下当前可行的start点在哪就好了,当遇见了本窗口内没有的char就能更新ans,这就有两种情况:1.遇见了新char;2.遇见了旧char,但它的位置是在窗口外;否则的话就是遇到和窗口中重复的字符了,需要更新start的位置
【代码】
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s: return 0
dic = {}
start, ans = 0, 0
for i in range(len(s)):
x = s[i]
if x not in dic or dic[x]<start:
ans = max(ans, i-start+1)
else:
start = dic[x]+1
dic[x]=i
return ans
Given a string s, return the longest palindromic substring in s.
Example 1:
Input: s = "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: s = "cbbd"
Output: "bb"
Example 3:
Input: s = "a"
Output: "a"Constraints:
1 <= s.length <= 1000
s consist of only digits and English letters (lower-case and/or upper-case),
【题意】
找出字符串中的最长回文子串
【思路】
很容易想到遍历子串中心,然后前后扩张。因为字符串的长度最多才1000所以也还行
代码里用了之前学到的占位符操作这样能避免偶数个char的尴尬情况
【代码】
class Solution:
def longestPalindrome(self, s: str) -> str:
ss = "#"
for x in s:
ss+=x+"#"
cnt, ans = 1, s[0]
for i, x in enumerate(ss):
k=0
while i-k>=0 and i+k<len(ss) and ss[i-k]==ss[i+k]:
k+=1
if (2*k-1)//2 > cnt:
ans = ss[i-k+1:i+k]
cnt = (2*k-1)//2
return "".join(ans.split("#"))
Given the root of a binary tree, determine if it is a valid binary search tree (BST).
A valid BST is defined as follows:
The left subtree of a node contains only nodes with keys less than the node's key.
The right subtree of a node contains only nodes with keys greater than the node's key.
Both the left and right subtrees must also be binary search trees.
Example 1:
Input: root = [2,1,3]
Output: true
Example 2:
Input: root = [5,1,4,null,null,3,6]
Output: false
Explanation: The root node's value is 5 but its right child's value is 4.
【题意】
判断二叉树是否是二叉搜索树
【思路】
只要左子树最大的node小于根节点且右子树最小的node大于根节点,并且左右子树都是BST
【代码】
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
def work(root):
ans = True
max_val = min_val = root.val
if root.left:
l = work(root.left)
ans = ans and (l[0] < root.val) and l[2]
max_val, min_val = max(max_val, l[0]), min(min_val, l[1])
if root.right:
r = work(root.right)
ans = ans and (r[1] > root.val) and r[2]
max_val, min_val = max(max_val, r[0]), min(min_val, r[1])
return max_val, min_val, ans
return work(root)[2]
【discussion】
和我递归往上的思路不一样,这个是递归往下的
维护一个lessThan和largerThan值,代表了当前这个子树必须要小于和大于的阈值。这个做法的运行时间会快上一倍
class Solution(object):
def isValidBST(self, root, lessThan = float('inf'), largerThan = float('-inf')):
if not root:
return True
if root.val <= largerThan or root.val >= lessThan:
return False
return self.isValidBST(root.left, min(lessThan, root.val), largerThan) and \
self.isValidBST(root.right, lessThan, max(root.val, largerThan))
Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Notice that the solution set must not contain duplicate triplets.
Example 1:
Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]Constraints:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
【题意】
从一个列表中挑出三个使其求和为0 求出所有的不重复三元组
【思路】
遍历前两个元素 sort一下然后logn找第三个 这样是NNlogN
然后就想到 可以先NN一下把所有的两两元素的和求出来 扔进dict 就可以直接找到第三个 麻烦的就是判断下idx不能和前两个元素重复(这里又有个O(N) 可以通过提前break不T 但还是一言难尽)
【代码】
非常非常勉强的过了 faster than 5.05% orzzzz
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
from collections import defaultdict
dic = defaultdict(list)
for i,x in enumerate(nums):
dic[x].append(i)
ans = set()
for i in range(len(nums)):
for j in range(i+1, len(nums)):
now = nums[i]+nums[j]
if -now in dic:
for idx in dic[-now]:
if idx!=i and idx!=j:
ans.add(tuple(sorted([nums[i], nums[j], -now])))
break
return ans
【discussion】
O(N^2)的解法 几乎快上十倍0.0
先把list排个序 先固定一个元素 然后在后面找另两个 因为已经排好序 所以可以根据和的大小来挪l/r指针;每个判断"后一个和前一个的值是不是相等"的操作都是为了去重
def threeSum(self, nums):
res = []
nums.sort()
for i in range(len(nums)-2):
if i > 0 and nums[i] == nums[i-1]:
continue
l, r = i+1, len(nums)-1
while l < r:
s = nums[i] + nums[l] + nums[r]
if s < 0:
l +=1
elif s > 0:
r -= 1
else:
res.append((nums[i], nums[l], nums[r]))
while l < r and nums[l] == nums[l+1]:
l += 1
while l < r and nums[r] == nums[r-1]:
r -= 1
l += 1; r -= 1
return res
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.
Example 1:
Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped.
【题意】
一个整数列表 每个整数代表了该位置上的柱子高度 问这一组柱子在下雨的时候能接住多少雨水(需要看题面的图比较清楚)
【思路】
先按照高度排个序,每次在最高的俩柱子之间算雨水(柱子间体积-前缀和差),不断往外扩,维护当前包围的区间的最左端和最右端,如果落入区间内就不管,如果在区间外就拿区间的边界和它包围雨水。
【代码】
class Solution:
def trap(self, height: List[int]) -> int:
if len(height)<=1:
return 0
S = [height[0]]
for x in height[1:]:
S.append(S[-1]+x)
bars = [(x,i) for i,x in enumerate(height)]
bars.sort(reverse=True)
l,r = bars[0], bars[1]
if l[1]>r[1]: l, r = r, l
ans = min(l[0],r[0])*(r[1]-l[1]-1) - (S[r[1]-1]-S[l[1]])
for x in bars[2:]:
if l[1]<x[1]<r[1]:
continue
elif x[1]<l[1]:
ans += x[0]*(l[1]-x[1]-1) - (S[l[1]-1]-S[x[1]])
l=x
elif r[1]<x[1]:
ans += x[0]*(x[1]-r[1]-1) - (S[x[1]-1]-S[r[1]])
r=x
return ans
【discussion】
这道题其实挺像之前的一道题的 区别就在于之前那个是可覆盖的最大矩形 这里是所有包围住柱子大大小小的矩形的和 所以其实也可以用前后双指针的做法
开始的时候俩指针在最头和最尾位置,然后哪边指针的柱子更矮就往中间挪。记住左边和右边已扫过的最高的柱子高度l_max, r_max
,当往中间挪的时候如果是下降了,雨水就填补上这个差额高度volume += l_max - bars[left]
这个差额可以直接是全填补满而不需要担心右边兜不住,是因为有l_max <= r_max
的前提保证。
def trap(self, bars):
if not bars or len(bars) < 3:
return 0
volume = 0
left, right = 0, len(bars) - 1
l_max, r_max = bars[left], bars[right]
while left < right:
l_max, r_max = max(bars[left], l_max), max(bars[right], r_max)
if l_max <= r_max:
volume += l_max - bars[left]
left += 1
else:
volume += r_max - bars[right]
right -= 1
return volume
Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment.
Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure.
Clarification: The input/output format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself.
Example 1:
Input: root = [1,2,3,null,null,4,5]
Output: [1,2,3,null,null,4,5]
【题意】
实现对一棵二叉树序列化和反序列化
【思路】
前中后序遍历只要有两个就能还原完整的树 反序列化可以直接用之前写过的前序中序还原树的代码(这样写完就遇到问题了 因为这样做要求树上不能有重复的数字orz 因为在中序里面找根的位置需要用到.index)
然后在知乎在找到:
一棵二叉树能够被重建,如果满足下面三个条件之一:
a1. 已知先序遍历;或
a2. 已知后序遍历;或
a3. 已知层序遍历;
且满足下面三个条件之一:
b1. 前面已知的那种遍历包含了空指针;或
b2. 已知中序遍历,且树中不含重复元素;或
b3. 树是二叉搜索树,且不含重复元素。
所以既不是BST又有重复元素,那就只能求个前/后序遍历(或者按层遍历)把叶子的null节点也加进来才能分辨,这样时间也是最少的O(N)。下面代码里用的是按层遍历
【代码】
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
que, ans = [root], []
while que:
x = que.pop(0)
if not x:
ans.append("#")
else:
ans.append(str(x.val))
que += [x.left, x.right]
return " ".join(ans)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
def getNode(val):
if val=="#":
return None
return TreeNode(val)
data = data.split()
head = getNode(data[0])
que, now_idx = [head], 1
while que:
x = que.pop(0)
if not x:
continue
x.left = getNode(data[now_idx])
now_idx += 1
x.right = getNode(data[now_idx])
now_idx += 1
que += [x.left, x.right]
return head
# Your Codec object will be instantiated and called as such:
# ser = Codec()
# deser = Codec()
# ans = deser.deserialize(ser.serialize(root))
【discussion】
同样的思路,用前序遍历的递归写法
学到了一个写法 vals = iter(list); val = next(vals)
这样直接往后挪就不用搞个idx指针了
class Codec:
def serialize(self, root):
def doit(node):
if node:
vals.append(str(node.val))
doit(node.left)
doit(node.right)
else:
vals.append('#')
vals = []
doit(root)
return ' '.join(vals)
def deserialize(self, data):
def doit():
val = next(vals)
if val == '#':
return None
node = TreeNode(int(val))
node.left = doit()
node.right = doit()
return node
vals = iter(data.split())
return doit()
Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2.
You have the following three operations permitted on a word:
Insert a character
Delete a character
Replace a character
Example 1:
Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')
【题意】
字符串的最小编辑距离,共有三种操作可以做:插入、删除、替换
(woc这不是字节面试题嘛我枯了)
【思路】
只看第一个位置的char 如果不一样 就做增删改,如果一样 就直接挪下一个位置
特殊情况是 如果word1是空的 就只能做增,如果word2是空的 就删掉整个word1
【代码】
class Solution:
def __init__(self):
self.dic = {}
def minDistance(self, word1: str, word2: str) -> int:
if (word1,word2) in self.dic:
return self.dic[(word1,word2)]
if not word2:
return len(word1)
if not word1:
ans = 1+self.minDistance(word1, word2[1:])
else:
if word1[0]==word2[0]:
ans = self.minDistance(word1[1:], word2[1:])
else:
ans = 1+min(self.minDistance(word1, word2[1:]), self.minDistance(word1[1:], word2), self.minDistance(word1[1:], word2[1:]))
self.dic[(word1,word2)] = ans
return ans
【discussion】
整个word存dict有点费,可以只存当前看的idx
class Solution:
def minDistance(self, word1, word2, i, j, memo):
if i == len(word1) and j == len(word2):
return 0
if i == len(word1):
return len(word2) - j
if j == len(word2):
return len(word1) - i
if (i, j) not in memo:
if word1[i] == word2[j]:
ans = self.minDistance2(word1, word2, i + 1, j + 1, memo)
else:
insert = 1 + self.minDistance2(word1, word2, i, j + 1, memo)
delete = 1 + self.minDistance2(word1, word2, i + 1, j, memo)
replace = 1 + self.minDistance2(word1, word2, i + 1, j + 1, memo)
ans = min(insert, delete, replace)
memo[(i, j)] = ans
return memo[(i, j)]
DP的写法:
class Solution:
def minDistance(self, word1, word2):
m = len(word1)
n = len(word2)
table = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
table[i][0] = i
for j in range(n + 1):
table[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
table[i][j] = table[i - 1][j - 1]
else:
table[i][j] = 1 + min(table[i - 1][j], table[i][j - 1], table[i - 1][j - 1])
return table[-1][-1]
Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
For example,
[2,3,4], the median is 3
[2,3], the median is (2 + 3) / 2 = 2.5
Design a data structure that supports the following two operations:
void addNum(int num) - Add a integer number from the data stream to the data structure.
double findMedian() - Return the median of all elements so far.
Example:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
【题意】
不断地可能有新的数字添加进来,找这个列表的中位数
【思路】
找中位数那就要保证这个列表是有序的,每次来一个新数可以logN地找到它该在的位置然后插进去,然后find的时候就拿出中间那个
(非常非常勉强的没T过了orz又是faster than 5%的那种,主要是慢在插入的时候列表拷贝emm,除非能做成链表=。=
【代码】
class MedianFinder:
def __init__(self):
self.nums = []
def addNum(self, num: int) -> None:
idx = bisect.bisect(self.nums, num)
self.nums = self.nums[:idx]+[num]+self.nums[idx:]
def findMedian(self) -> float:
length = len(self.nums)
if length%2==0:
return (self.nums[length//2-1]+self.nums[length//2])/2
else:
return self.nums[length//2]
# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()
【discussion】
用堆(优先队列)的做法(本来也想过优先队列,但问题是没想到怎么直接拿到中位数orz)
维护两个堆,一个最大堆来存列表中小的那一半small
(加负号是为了能直接变成最大堆),一个最小堆来存大的那一半large
,这样就能直接O(1)的拿到中位数了;添加新数的时候,把num扔进large然后弹出堆顶丢进small,维持两边的个数均衡,这样是O(logN)的
heapq.heappushpop (heap, item):将item 放入堆中,然后弹出并返回heap 的最小元素
heapq.heappush(heap, item):将 item 的值加入 heap 中,保持堆的不变性
heapq.heappop(heap):弹出并返回 heap 的最小的元素,保持堆的不变性
from heapq import *
class MedianFinder:
def __init__(self):
self.heaps = [], []
def addNum(self, num):
small, large = self.heaps
heappush(small, -heappushpop(large, num))
if len(large) < len(small):
heappush(large, -heappop(small))
def findMedian(self):
small, large = self.heaps
if len(large) > len(small): #奇数个
return float(large[0])
return (large[0] - small[0]) / 2.0 #偶数个
Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence.
Follow up: Could you implement the O(n) solution?
Example 1:
Input: nums = [100,4,200,1,3,2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.
【题意】
一个无序列表中的连续元素最多有多少个,要求O(N)
【思路】
最直观的想法就是sort一下然后遍历找,这样O(Nlogn)
然后想到可以维护若干个连续元素区间的左右端点,遍历到一个新元素就看它能不能扩充已有的某个区间,如果能同时扩充两个说明这两个区间需要合并,但这样至少也是O(NlogN)..emm
【discussion】
您瞧瞧我看到了啥=。= 十行代码vans了
评论区笑死我了:“I really hate you. Every time your solution makes mine look like shit. EVERY TIME.”
def longestConsecutive(self, nums):
nums = set(nums)
best = 0
for x in nums:
if x - 1 not in nums:
y = x + 1
while y in nums:
y += 1
best = max(best, y - x)
return best
You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
Return the max sliding window.
Example 1:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position Max
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7Constraints:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
【题意】
滑动长度为K的滑窗,输出滑窗中的最大值
【思路】
优先队列(最大堆),把value和idx都存进去,如果弹出的是在窗口之外的就扔掉. 每个元素进一次出一次,时间O(NlogN)
【代码】
faster than 5% orz
from queue import PriorityQueue
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
pq = PriorityQueue()
for i in range(k-1):
pq.put((-nums[i], i))
ans = []
for i in range(k-1, len(nums)):
pq.put((-nums[i], i))
val,idx = pq.get()
while idx<=i-k:
val,idx = pq.get()
ans.append(-val)
pq.put((val,idx))
return ans
【discussion】
用双端队列(其实是栈的思想)保证这个队列内是个从大到小排序的窗口内元素
对于遍历到一个新元素,在之前窗口右端比它小的就没用了,可以直接pop掉,然后再把左端在窗口范围外的popleft掉,这时候队列首的就是当前窗口的最大值
from collections import deque
def maxSlidingWindow(self, nums, k):
d = collections.deque()
out = []
for i, n in enumerate(nums):
while d and nums[d[-1]] < n:
d.pop()
d += i,
if d[0] == i - k:
d.popleft()
out += nums[d[0]],
return out[k-1:]
You are given an array of k linked-lists lists, each linked-list is sorted in ascending order.
Merge all the linked-lists into one sorted linked-list and return it.
Example 1:
Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
1->4->5,
1->3->4,
2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6Constraints:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] is sorted in ascending order.
【思路】
把每个列表的第一个元素丢进优先队列 然后每次从队列里取
【代码】
from queue import PriorityQueue
class Solution(object):
def mergeKLists(self, lists):
head = ListNode(None)
now = head
q = PriorityQueue()
for i in range(len(lists)):
node = lists[i]
if node: q.put((node.val, i))
while q.qsize():
idx = q.get()[1]
now.next = lists[idx]
now = now.next
if lists[idx].next:
lists[idx] = lists[idx].next
q.put((lists[idx].val, idx))
return head.next
Given a string s, partition s such that every substring of the partition is a palindrome.
Return the minimum cuts needed for a palindrome partitioning of s.
Example 1:
Input: s = "aab"
Output: 1
Explanation: The palindrome partitioning ["aa","b"] could be produced using 1 cut.
Example 2:
Input: s = "a"
Output: 0
Example 3:
Input: s = "ab"
Output: 1
Constraints:
1 <= s.length <= 2000
s consists of lower-case English letters only.
【题意】
把一个字符串切成一堆回文串 问最小切几次
【思路】
先把每个回文串预处理出来,变成一段段的[begin, end]对
然后遍历一遍,存下以pos为结尾的这段里最少的回文串数,转移就是以pos作为end的所有段的前一个位置的值+1
【代码】
class Solution:
def minCut(self, s: str) -> int:
dic = defaultdict(list)
n = len(s)
for pos in range(n):
i, j = pos, pos
while i>=0 and j<n and s[i]==s[j]:
dic[j].append(i)
i, j = i-1, j+1
i, j = pos, pos+1
while i>=0 and j<n and s[i]==s[j]:
dic[j].append(i)
i, j = i-1, j+1
ans = [0]
for pos in range(n):
res = pos+1
if dic[pos]:
for beg in dic[pos]:
res = min(res, ans[beg]+1)
ans.append(res)
return ans[-1]-1
【思路】
将val从小到大排序去assign值,ans相当于此行此列的最大值+1,如果最大值是同样的val就等于最大值
交了一版然后发现 当同行同列出现同样val的时候 assign的顺序会影响后续的值 比如先赋了个1 后来同一列的赋了个2 那就得更改之前同val的值了
这里想的办法是,把每个val所在的所有位置都记下来,然后当需要assign这个val的时候,按照所有这些位置行列上最大值倒序排的顺序遍历
但这个行列最大值是要动态维护的,就很难办
【WA的代码】(只sort一次是不行的)
class Solution:
def matrixRankTransform(self, matrix: List[List[int]]) -> List[List[int]]:
n = len(matrix)
m = len(matrix[0])
val_pos = defaultdict(list)
for i in range(n):
for j in range(m):
val_pos[matrix[i][j]].append((i,j))
val_pos = dict(sorted(val_pos.items(), key=lambda x: x[0]))
# print(val_pos) #val:[(i1,j1), (i2,j2)...]
rows = [[0,0] for i in range(n)] #ans, val
cols = [[0,0] for i in range(m)] #ans, val
ans = [[0 for i in range(m)] for j in range(n)]
for x in val_pos:
val, pos = x, val_pos[x]
rank = []
for (r, c) in pos:
a = max(rows[r][0], cols[c][0])
rank.append([r, c, a])
rank = sorted(rank, key = lambda x: -x[2]) #这里有问题,除非每次都更新a然后再sort
for now in rank:
r, c = now[0], now[1]
if rows[r][0] < cols[c][0]:
if val == cols[c][16]:
ans[r][c] = cols[c][0]
else: ans[r][c] = cols[c][0]+1
else:
if val == rows[r][17]:
ans[r][c] = rows[r][0]
else: ans[r][c] = rows[r][0]+1
rows[r][0], rows[r][18] = ans[r][c], val
cols[c][0], cols[c][19] = ans[r][c], val
return ans
【discussion】
解决的思路就是一次把所有同行同列的相同val都更新掉,就不会有先后顺序导致的问题了
整体来说 还是像之前那样先val从小到大排序,对于一个val存下它出现在的所有地方,当开始处理这个val的时候,对这些position的行号、列号做一个并查集的join操作,把行列绑定,每个position都做了行列绑定之后,更新一个地方就能把和它有影响的一圈都同时更新
def matrixRankTransform(self, A):
n, m = len(A), len(A[0])
rank = [0] * (m + n)
d = collections.defaultdict(list)
for i in xrange(n):
for j in xrange(m):
d[A[i][j]].append([i, j])
def find(i):
if p[i] != i:
p[i] = find(p[i])
return p[i]
for a in sorted(d):
p = range(m + n)
rank2 = rank[:]
for i, j in d[a]:
i, j = find(i), find(j + n)
p[i] = j
rank2[j] = max(rank2[i], rank2[j])
for i, j in d[a]:
rank[i] = rank[j + n] = A[i][j] = rank2[find(i)] + 1
return A
Given two strings s and t, return the number of distinct subsequences of s which equals t.
A string's subsequence is a new string formed from the original string by deleting some (can be none) of the characters without disturbing the remaining characters' relative positions. (i.e., "ACE" is a subsequence of "ABCDE" while "AEC" is not).
It is guaranteed the answer fits on a 32-bit signed integer.
Example 1:
Input: s = "rabbbit", t = "rabbit"
Output: 3
Explanation:
As shown below, there are 3 ways you can generate "rabbit" from S.
rabbbit
rabbbit
rabbbit
Constraints:
1 <= s.length, t.length <= 1000
s and t consist of English letters.
【题意】
s的所有子序列里可以整出几个t
【思路】
把所有字母出现的所有位置list记下来,找到所有t[0]的位置之后的那些里有几个t[1:],然后都加起来
找某个位置之后有几个char的时候可以用upper_bound(位置list,begin pos)
考虑到如果输入是一堆“aaaaa”会T 所以用dic存记忆化搜索(然而这样还是T了 orz)
【T的代码】
class Solution:
def numDistinct(self, s: str, t: str) -> int:
c_pos = defaultdict(list)
dic = {}
for i, x in enumerate(s):
c_pos[x].append(i)
def work(beg, t):
if (beg, t) in dic:
return dic[(beg, t)]
if len(t)==0:
return 1
c = t[0]
lis = c_pos[c]
pos = bisect.bisect(lis, beg)
ans = 0
for i in range(pos, len(lis)):
ans += work(lis[i], t[1:])
dic[(beg, t)] = ans
return ans
ans = 0
for x in c_pos[t[0]]:
ans += work(x, t[1:])
return ans
又想到 一个个加也太慢了 如果把t拆成两半 知道前半段有几个 后半段有几个 一乘就好了
但是想不懂s怎么切分怎么去重 或者另判beg,end有没有交叉
【discussion】
就是纯DP思维了...
dp[i][j]代表前i个s和前j个t这两串的值。
j为0的时候表达t空串,ans是1,所以第一行都是1。i为0的时候代表s空串,ans是0,所以第一列都是0。
对于每个i,j,只要看s[i]==t[j]是否成立,如果不成立,那就只能从i-1里去找这串jdp[i][j] = dp[i-1][j]
,如果成立,那就除了i-1里去找的ok之外,还能加上i-1里找到的这串j-1有多少个dp[i][j] = dp[i-1][j] + dp[i-1][j-1]
# O(m*n) space
def numDistinct1(self, s, t):
l1, l2 = len(s)+1, len(t)+1
dp = [[1] * l2 for _ in xrange(l1)]
for j in xrange(1, l2):
dp[0][j] = 0
for i in xrange(1, l1):
for j in xrange(1, l2):
dp[i][j] = dp[i-1][j] + dp[i-1][j-1]*(s[i-1] == t[j-1])
return dp[-1][-1]
# O(n) space
def numDistinct(self, s, t):
l1, l2 = len(s)+1, len(t)+1
cur = [0] * l2
cur[0] = 1
for i in xrange(1, l1):
pre = cur[:]
for j in xrange(1, l2):
cur[j] = pre[j] + pre[j-1]*(s[i-1] == t[j-1])
return cur[-1]