作者 | Howard Wonanut
目录:
双指针
LeetCode15题 https://leetcode-cn.com/problems/3sum/
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
首先对数组进行排序,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),然后从左往右遍历排序数组。对于nums[i],设置初始指针j=0,k=len(nums)-1,如果sum = nums[i]+nums[j]+nums[k]=0则直接将其添加到结果中,i++结束当前循环;如果sum<0则j++;如果sum>0,则k–;直至i=j或者i=k,结束当前循环。整体时间复杂度 O ( n 2 ) O(n^2) O(n2)。按照上面的思路写出的代码如下
def threeSum(nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
ans = []
nums = sorted(nums)
for i in range(len(nums)):
j, k = 0, len(nums)-1
while j < i and k > i:
cur_sum = nums[i] + nums[j] + nums[k]
if cur_sum == 0:
ans.append([nums[i], nums[j], nums[k]])
j += 1
k -= 1
elif cur_sum < 0:
j += 1
else:
k -= 1
return ans
threeSum([-1,0,1,2,-1,-4])
>>> [[-1, -1, 2], [0, -1, 1]]
threeSum([-2,-1,0,1,2,3])
>>> [[-1, -2, 3], [0, -2, 2], [0, -1, 1]]
threeSum([0,0,0,0,0])
>>> [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
不过上面的答案不能AC,因为没有去除重复结果,如数组中的元素全为0的情况,会有多个[0,0,0]结果,如何解决重复的问题呢?把ans改成set就通过了~
def threeSum(nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
ans = set()
nums = sorted(nums)
for i in range(len(nums)):
j, k = 0, len(nums)-1
while j < i and k > i:
cur_sum = nums[i] + nums[j] + nums[k]
if cur_sum == 0:
ans.add((nums[i], nums[j], nums[k]))
j += 1
k -= 1
elif cur_sum < 0:
j += 1
else:
k -= 1
return list(ans)
双指针
LeetCode1169题 https://leetcode-cn.com/problems/invalid-transactions/
如果出现下述两种情况,交易 可能无效:
每个交易字符串 transactions[i] 由一些用逗号分隔的值组成,这些值分别表示交易的名称,时间(以分钟计),金额以及城市。
给你一份交易清单 transactions,返回可能无效的交易列表。你可以按任何顺序返回答案。
直接暴力解就好了,AC
def invalidTransactions(transactions):
"""
:type transactions: List[str]
:rtype: List[str]
"""
ret = []
t = [x.split(',') for x in transactions]
for idx1, tran1 in enumerate(transactions):
for idx2, tran2 in enumerate(transactions):
if idx2 == idx1:
continue
if int(t[idx1][2]) > 1000:
ret.append(tran1)
break
if t[idx1][0] == t[idx2][0] and t[idx1][3] != t[idx2][3] and abs(int(t[idx1][1])-int(t[idx2][1])) <= 60:
ret.append(tran1)
break
return ret
invalidTransactions(["alice,20,800,mtv","alice,50,100,beijing"])
>>> ['alice,20,800,mtv', 'alice,50,100,beijing']
双指针
LeetCode 16题 https://leetcode-cn.com/problems/3sum-closest/
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
下面的答案和0x02题目思路一样,使用双指针,时间复杂度 O ( n 2 ) O(n^2) O(n2)
def threeSumClosest(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
ans = 999999
nums = sorted(nums)
for i in range(len(nums)):
j, k = 0, len(nums) - 1
while j < i and k > i:
cur_sum = nums[i] + nums[j] + nums[k]
if cur_sum == target:
return target
elif abs(cur_sum - target) < abs(ans - target):
ans = cur_sum
elif cur_sum < target:
j += 1
else:
k -= 1
return ans
LeetCode 17题 https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
def letterCombinations(digits):
"""
:type digits: str
:rtype: List[str]
"""
if len(digits) == 0:
return []
char_dict = {"2":"abc", "3":"def", "4":"ghi", "5":"jkl", "6":"mno", "7":"pqrs", "8":"tuv", "9":"wxyz"}
ans = [char for char in char_dict[digits[0]]]
digits = digits[1:]
for digit in digits:
cur_list, ans = ans, []
for seq in cur_list:
for char in char_dict[digit]:
ans.append(seq+char)
return ans
## 0x05 四数之和
### 题目描述
LeetCode 18题 https://leetcode-cn.com/problems/4sum/
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
```python
# TODO
LeetCode 98题 https://leetcode-cn.com/problems/validate-binary-search-tree/
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
class Solution(object):
def init(self):
self.prev = None
def helper(self, root):
if not root:
return True
left = self.helper(root.left)
if self.prev == None:
self.prev = root.val
elif root.val <= self.prev:
return False
else:
self.prev = root.val
right = self.helper(root.right)
return right and left
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
return self.helper(root)
日期
2019-12-12
## 0x07 树中距离之和
`困难`
### 题目描述
LeetCode 834题 https://leetcode-cn.com/problems/sum-of-distances-in-tree/
给定一个无向、连通的树。树中有 N 个标记为 0...N-1 的节点以及 N-1 条边 。
第 i 条边连接节点 edges[i][0] 和 edges[i][1] 。
返回一个表示节点 i 与其他所有节点距离之和的列表 ans。
```python
# TODO
简单
LeetCode 513题 https://leetcode-cn.com/problems/find-bottom-left-tree-value/
给定一个二叉树,在树的最后一行找到最左边的值。
最简单的层序遍历即可
def findBottomLeftValue(root):
que1, que2 = [], []
ans = root.val
que1.append(root)
while len(que1) != 0 or len(que2) != 0:
if len(que1) != 0:
ans = que1[0].val
while len(que1)!= 0:
cur_node = que1[0]
que1.pop(0)
if cur_node.left:
que2.append(cur_node.left)
if cur_node.right:
que2.append(cur_node.right)
if len(que2) != 0:
ans = que2[0].val
while len(que2)!= 0:
cur_node = que2[0]
que2.pop(0)
if cur_node.left:
que1.append(cur_node.left)
if cur_node.right:
que1.append(cur_node.right)
return ans
使用DFS
简单
LeetCode 693题 https://leetcode-cn.com/problems/binary-number-with-alternating-bits/
给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等。
逐位右移检查
def hasAlternatingBits(n):
"""
:type n: int
:rtype: bool
"""
flag = n & 0x1
while n != 0:
if n & 0x1 != flag:
return False
n = n >> 1
flag = not flag
return True
将n和右移一位的n做异或,检查所有位是否都为1(异或:不同为1)
class Solution(object):
def hasAlternatingBits(self, n):
"""
:type n: int
:rtype: bool
"""
temp = n^(n>>1)
return temp&(temp+1) == 0
中等
LeetCode 430题 https://leetcode-cn.com/problems/flatten-a-multilevel-doubly-linked-list/submissions/
您将获得一个双向链表,除了下一个和前一个指针之外,它还有一个子指针,可能指向单独的双向链表。这些子列表可能有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。
扁平化列表,使所有结点出现在单级双链表中。您将获得列表第一级的头部。
"""
# Definition for a Node.
class Node(object):
def __init__(self, val, prev, next, child):
self.val = val
self.prev = prev
self.next = next
self.child = child
"""
class Solution(object):
def dfs(self, head):
if head == None:
return None
head_copy = head
while head:
if head.child != None:
temp_next = head.next
temp_flat = self.dfs(head.child)
head.child = None
temp_flat.prev = head
head.next = temp_flat
while temp_flat.next:
temp_flat = temp_flat.next
if temp_next:
temp_flat.next = temp_next
temp_next.prev = temp_flat
head = temp_next
else:
head = head.next
return head_copy
def flatten(self, head):
"""
:type head: Node
:rtype: Node
"""
return self.dfs(head)
2019-12-17
简单
LeetCode 104题 https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
像此类二叉树的问题,大部分都可以直接使用递归解决,但是递归一定要写的漂亮,简洁!
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
return 0 if root == None else max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
简单
LeetCode 111题 https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
这道题和上一题很像,很容易进坑写成这样:
return 0 if root == None else min(self.minDepth(root.left), self.minDepth(root.right)) + 1
但是最小深度定义为:根节点到最近叶子节点,也就是说只有到达叶子节点(左右子树均为空)才算到底!
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def minDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if root == None:
return 0
if root.left and root.right:
return min(self.minDepth(root.left), self.minDepth(root.right)) + 1
elif root.left and not root.right:
return self.minDepth(root.left) + 1
elif root.right and not root.left:
return self.minDepth(root.right) + 1
else:
return 1
简单
LeetCode 14题 https://leetcode-cn.com/problems/longest-common-prefix/
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
class Solution(object):
def longestCommonPrefix(self, strs):
"""
:type strs: List[str]
:rtype: str
"""
ans = ""
if len(strs) == 0:
return ans
min_len = 9999
for st in strs:
min_len = min(min_len, len(st))
for i in range(min_len):
ch = strs[0][i]
for j in range(1,len(strs)):
if strs[j][i] != ch:
return ans
ans += ch
return ans
利用python的max()和min(),在Python里字符串是可以比较的,按照ascII值排,举例abb, aba,abac,最大为abb,最小为aba。所以只需要比较最大最小的公共前缀就是整个数组的公共前缀
class Solution(object):
def longestCommonPrefix(self, strs):
"""
:type strs: List[str]
:rtype: str
"""
if not strs:
return ""
s1 = min(strs)
s2 = max(strs)
for i,x in enumerate(s1):
if x != s2[i]:
return s2[:i]
return s1
https://leetcode-cn.com/problems/longest-common-prefix/solution/zui-chang-gong-gong-qian-zhui-by-leetcode/
LeetCode 模拟题
给出 n 个数对。 在每一个数对中,第一个数字总是比第二个数字小。
现在,我们定义一种跟随关系,当且仅当 b < c 时,数对(c, d) 才可以跟在 (a, b) 后面。我们用这种形式来构造一个数对链。
给定一个对数集合,找出能够形成的最长数对链的长度。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
示例 :
输入: [[1,2], [2,3], [3,4]]
输出: 2
解释: 最长的数对链是 [1,2] -> [3,4]
注意:
给出数对的个数在 [1, 1000] 范围内。
class Solution(object):
def findLongestChain(self, pairs):
"""
:type pairs: List[List[int]]
:rtype: int
"""
pairs.sort(key=lambda pair: pair[0])
dp = [1 for i in range(len(pairs) + 1)]
for i in range(1, len(pairs) + 1):
cur_max = 0
for j in range(1, i+1):
if pairs[i-1][0] > pairs[j-1][1]:
cur_max = max(cur_max, dp[j] + 1)
else:
cur_max = max(cur_max, dp[j])
dp[i] = cur_max
return dp[len(pairs)]
对pair[1]从小到大排序
class Solution(object):
def findLongestChain(self, pairs):
"""
:type pairs: List[List[int]]
:rtype: int
"""
pairs.sort(key=lambda pair: pair[1])
end, cnt = -sys.maxsize, 0
for pair in pairs:
if pair[0] > end:
cnt += 1
end = pair[1]
return cnt
质数判断
LeetCode204: https://leetcode-cn.com/problems/count-primes/
统计所有小于非负整数 n 的质数的数量。
class Solution(object):
def countPrimes(self, n):
"""
:type n: int
:rtype: int
"""
arr = [True for i in range(n)]
ans = 0
for i in range(2, n):
if arr[i]:
ans += 1
for j in range(i*i, n, i):
arr[j] = False
return ans
LeetCode234: https://leetcode-cn.com/problems/palindrome-linked-list/
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
复杂度要求: O(n) 时间复杂度和 O(1) 空间复杂度
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def isPalindrome(self, head):
"""
:type head: ListNode
:rtype: bool
"""
arr = []
while head:
arr.append(head.val)
head = head.next
i, j = 0, len(arr) - 1
while i < j:
if arr[i] != arr[j]:
return False
i += 1
j -= 1
return True
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
class ListNode(object):
def __init__(self, x):
self.val = x
self.next = None
def print_list(p, c):
arr = []
prev, cur = p, c
while prev:
arr.insert(0, prev.val)
prev = prev.next
arr.append('prev | cur')
while cur:
arr.append(cur.val)
cur = cur.next
print(arr)
def reverse_linklist(head):
prev, cur = None, head
print_list(prev, cur)
while cur:
cur.next, cur, prev = prev, cur.next, cur
print_list(prev, cur)
return prev
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node5 = ListNode(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
reverse_linklist(node1)
['prev | cur', 1, 2, 3, 4, 5]
[1, 'prev | cur', 2, 3, 4, 5]
[1, 2, 'prev | cur', 3, 4, 5]
[1, 2, 3, 'prev | cur', 4, 5]
[1, 2, 3, 4, 'prev | cur', 5]
[1, 2, 3, 4, 5, 'prev | cur']
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def isPalindrome(self, head):
if not head or not head.next:
return True
fast, cur, prev = head.next, head, None
while fast and fast.next:
fast = fast.next.next
cur.next, cur, prev = prev, cur.next, cur
cur.next, cur, prev = prev, cur.next, cur
if fast:
left, right = prev, cur
else:
left, right = prev.next, cur
while left and right:
if left.val != right.val:
return False
left = left.next
right = right.next
return True
class Solution(object):
def isPalindrome(self, head):
arr = []
while head:
arr.append(head.val)
head = head.next
return arr == arr[::-1]
困难
LeetCode1074: https://leetcode-cn.com/problems/number-of-submatrices-that-sum-to-target/
给出矩阵 matrix 和目标值 target,返回元素总和等于目标值的非空子矩阵的数量。
子矩阵 x1, y1, x2, y2 是满足 x1 <= x <= x2 且 y1 <= y <= y2 的所有单元 matrix[x][y] 的集合。
如果 (x1, y1, x2, y2) 和 (x1’, y1’, x2’, y2’) 两个子矩阵中部分坐标不同(如:x1 != x1’),那么这两个子矩阵也不同。
# TODO
中等
LeetCode1138: https://leetcode-cn.com/problems/alphabet-board-path/
我们从一块字母板上的位置 (0, 0) 出发,该坐标对应的字符为 board[0][0]。
在本题里,字母板为board = [“abcde”, “fghij”, “klmno”, “pqrst”, “uvwxy”, “z”].
我们可以按下面的指令规则行动:
如果方格存在,‘U’ 意味着将我们的位置上移一行;
如果方格存在,‘D’ 意味着将我们的位置下移一行;
如果方格存在,‘L’ 意味着将我们的位置左移一列;
如果方格存在,‘R’ 意味着将我们的位置右移一列;
‘!’ 会把在我们当前位置 (r, c) 的字符 board[r][c] 添加到答案中。
返回指令序列,用最小的行动次数让答案和目标 target 相同。你可以返回任何达成目标的路径。
注意:
字符转ASCII码:ord(‘a’)
ord('a')
>>> 97
class Solution(object):
def alphabetBoardPath(self, target):
"""
:type target: str
:rtype: str
"""
i, j = 0, 0
ans = ""
for ch in list(target):
row, col = int(ord(ch) - ord('a')) // 5, int(ord(ch) - ord('a')) % 5
while j > col:
j -= 1
ans += "L"
while i > row:
i -= 1
ans += "U"
while j < col:
j += 1
ans += "R"
while i < row:
i += 1
ans += "D"
ans += '!'
return ans
简单
异或
LeetCode136: https://leetcode-cn.com/problems/single-number/
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = 0
for num in nums:
ans ^= num
return ans
中等
数学
LeetCode754: https://leetcode-cn.com/problems/reach-a-number/
在一根无限长的数轴上,你站在0的位置。终点在target的位置。
每次你可以选择向左或向右移动。第 n 次移动(从 1 开始),可以走 n 步。
返回到达终点需要的最小移动次数。
这道题可以说是极其恶心了,好好学习
字符翻转
LeetCode151: https://leetcode-cn.com/problems/reverse-words-in-a-string/
给定一个字符串,逐个翻转字符串中的每个单词。
class Solution(object):
def reverseWords(self, s):
"""
:type s: str
:rtype: str
"""
return ' '.join([x.strip() for x in s.split()][::-1])
简单
LeetCode459 https://leetcode-cn.com/problems/repeated-substring-pattern/
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
假设母串S是由子串s重复N次而成, 则 S+S则有子串s重复2N次, 现在S=Ns, S+S=2Ns 因此S在(S+S)[1:-1]中必出现一次以上。
class Solution(object):
def repeatedSubstringPattern(self, s):
return s in (s+s)[1:-1]
类似的操作还有:
中等
数学
LeetCode423: https://leetcode-cn.com/problems/reconstruct-original-digits-from-english/
给定一个非空字符串,其中包含字母顺序打乱的英文单词表示的数字0-9。按升序输出原始的数字。
注意:
输入只包含小写英文字母。
输入保证合法并可以转换为原始的数字,这意味着像 “abc” 或 “zerone” 的输入是不允许的。
输入字符串的长度小于 50,000。
找到每个数字的英文单词中的独特字母,即可
class Solution(object):
def originalDigits(self, s):
n0 = s.count('z')
n2 = s.count('w')
n8 = s.count('g')
n6 = s.count('x')
n3 = s.count('t') - n2 - n8
n4 = s.count('r') - n3 - n0
n7 = s.count('s') - n6
n1 = s.count('o') - n4 - n2 - n0
n5 = s.count('v') - n7
n9 = s.count('i') - n8 - n6 - n5
ns = (n0,n1,n2,n3,n4,n5,n6,n7,n8,n9)
return "".join((str(i)*n for i, n in enumerate(ns)))
简单
回文数
LeetCode9: https://leetcode-cn.com/problems/palindrome-number/
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
虽然这是道简单题,可以用一行代码结束战斗,但是如果不把整数看作字符串如何解决这个问题呢?不简单了
class Solution(object):
def isPalindrome(self, x):
"""
:type x: int
:rtype: bool
"""
return str(x) == str(x)[::-1]
你能不将整数转为字符串来解决这个问题吗?
中等
LeetCode1292: https://leetcode-cn.com/problems/maximum-side-length-of-a-square-with-sum-less-than-or-equal-to-threshold/
给你一个大小为 m x n 的矩阵 mat 和一个整数阈值 threshold。
请你返回元素总和小于或等于阈值的正方形区域的最大边长;如果没有这样的正方形区域,则返回 0 。
简单
异或
LeetCode268: https://leetcode-cn.com/problems/missing-number/submissions/
给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
class Solution(object):
def missingNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = len(nums)
for idx, num in enumerate(nums):
ans ^= idx
ans ^= num
return ans
中等
动态规划
LeetCode221: https://leetcode-cn.com/problems/maximal-square/
在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
dp[i][j]代表从(0,0)到(i,j)的最大正方形大小,
初始 dp[i][j]= matrix[i][j] == ‘1’?1:0
一般情况:dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1也就是当前位置是1且上边,左边,左上位置都是1,就能构成更大的矩形
class Solution(object):
def maximalSquare(self, matrix):
"""
:type matrix: List[List[str]]
:rtype: int
"""
if len(matrix) == 0 or len(matrix[0]) == 0:
return 0
ans = 0
m, n = len(matrix), len(matrix[0])
dp = [[int(matrix[i][j]) for j in range(n)] for i in range(m)]
for i in range(m):
for j in range(n):
if i > 0 and j > 0 and dp[i][j]:
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
ans = max(ans, dp[i][j])
return ans * ans
困难
动态规划
LeetCode691: https://leetcode-cn.com/problems/stickers-to-spell-word/
我们给出了 N 种不同类型的贴纸。每个贴纸上都有一个小写的英文单词。
你希望从自己的贴纸集合中裁剪单个字母并重新排列它们,从而拼写出给定的目标字符串 target。
如果你愿意的话,你可以不止一次地使用每一张贴纸,而且每一张贴纸的数量都是无限的。
拼出目标 target 所需的最小贴纸数量是多少?如果任务不可能,则返回 -1。
def minStickers(stickers, target):
ch_dict = {}
for index, sticker in enumerate(stickers):
for ch in sticker:
if ch not in ch_dict:
ch_dict[ch] = []
ch_dict[ch].append(index)
for ch in ch_dict:
if len(ch_dict[ch]) == 1:
idx = ch_dict[ch][0]
sticker = stickers[idx]
for s in sticker:
ch_dict[s]
minStickers(["with", "example", "science"],"target")
简单
LeetCode1207: https://leetcode-cn.com/problems/unique-number-of-occurrences/submissions/
给你一个整数数组 arr,请你帮忙统计数组中每个数的出现次数。
如果每个数的出现次数都是独一无二的,就返回 true;否则返回 false。
class Solution(object):
def uniqueOccurrences(self, arr):
"""
:type arr: List[int]
:rtype: bool
"""
a = [arr.count(num) for num in set(arr)]
return len(set(a)) == len(set(arr))
中等
递归
LeetCode655: https://leetcode-cn.com/problems/print-binary-tree/
在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则:
行数 m 应当等于给定二叉树的高度。
列数 n 应当总是奇数。
根节点的值(以字符串格式给出)应当放在可放置的第一行正中间。根节点所在的行与列会将剩余空间划分为两部分(左下部分和右下部分)。你应该将左子树输出在左下部分,右子树输出在右下部分。左下和右下部分应当有相同的大小。即使一个子树为空而另一个非空,你不需要为空的子树输出任何东西,但仍需要为另一个子树留出足够的空间。然而,如果两个子树都为空则不需要为它们留出任何空间。
每个未使用的空间应包含一个空的字符串""。
使用相同的规则输出子树。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def getHeight(self, root):
if not root:
return 0
return max(self.getHeight(root.left), self.getHeight(root.right)) + 1
def fillTree(self, root, ans, row, left, right):
if root:
mid = left + (right - left) / 2
ans[row][mid] = str(root.val)
self.fillTree(root.left, ans, row + 1, left, mid - 1)
self.fillTree(root.right, ans, row + 1, mid + 1, right)
def printTree(self, root):
"""
:type root: TreeNode
:rtype: List[List[str]]
"""
tree_height = self.getHeight(root)
tree_width = 2**tree_height - 1
ans = [["" for j in range(tree_width)] for i in range(tree_height)]
self.fillTree(root, ans, 0, 0, tree_width - 1)
return ans
困难
快慢指针
LeetCode76: https://leetcode-cn.com/problems/minimum-window-substring/
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
class Solution(object):
def minWindow(self, s, t):
from collections import defaultdict
lookup = defaultdict(int)
for c in t:
lookup[c] += 1
start = 0
end = 0
min_len = float("inf")
counter = len(t)
res = ""
while end < len(s):
if lookup[s[end]] > 0:
counter -= 1
lookup[s[end]] -= 1
end += 1
while counter == 0:
if min_len > end - start:
min_len = end - start
res = s[start:end]
if lookup[s[start]] == 0:
counter += 1
lookup[s[start]] += 1
start += 1
return res
中等
蓄水池抽样
LeetCode382: https://leetcode-cn.com/problems/linked-list-random-node/
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
进阶:
如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?
乍一看这道题很简单:首先把单链表遍历一遍存起来,然后调用getRandom函数的时候随机生成一个索引返回即可,其解法如下:
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def __init__(self, head):
"""
@param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node.
:type head: ListNodeQ
"""
self.arr = []
while head:
self.arr.append(head.val)
head = head.next
def getRandom(self):
"""
Returns a random node's value.
:rtype: int
"""
return random.choice(self.arr)
# Your Solution object will be instantiated and called as such:
# obj = Solution(head)
# param_1 = obj.getRandom()
但是如果用上面的方法不能满足进阶的要求:首先,链表长度未知,链表很大,可能无法一次全部装入内存,因此上面的方法无效了。其次要求使用常数级空间复杂度实现,也是直接否决了上面的方法。我百思不得其解,最终发现需要使用到蓄水池抽样算法
,并且被这个算法的巧妙深深折服了。本题算得上12月题目里面很有意思的一道题了,解法如下:
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def __init__(self, head):
"""
@param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node.
:type head: ListNodeQ
"""
self.head = head
def getRandom(self):
"""
Returns a random node's value.
:rtype: int
"""
ans, node, index = self.head, self.head.next, 1
while node:
if random.randint(0, index) == 0:
ans = node
node = node.next
index += 1
return ans.val
# Your Solution object will be instantiated and called as such:
# obj = Solution(head)
# param_1 = obj.getRandom()
# 问题背景:假如现在有海量数据,无法一次将其读入内存,
# 数据长度也不可得知,暂且用N表示其长度。
# 现在需要从中抽样k个数据,要求抽样得到的k个数据,每个数据被选中的概率均为k/N.
def sample(Data, k):
# 先将Data中的前k个数据放入蓄水池pool
pool = [Data[i] for i in range(k)]
# 从第k+1个数据开始遍历(由于我们是从0开始的,因此索引为k的数就是第k+1个数了)
for i in range(k, len(Data)):
# 生成一个随机数,范围为[0,i]
r = random.randint(0, i)
# 如果生成的随机数落在[0,k)之间,则将当前位置的数据替换掉pool中索引为r+1的数
if r < k:
pool[r] = Data[i]
# 返回pool即为所求
return pool
中等
逆波兰表达式
栈
LeetCode150: https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
根据逆波兰表示法,求表达式的值。
有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
1.逆波兰表达式求解,定义一个栈辅助计算;
2.当遇到运算符"+"、"-"、"*"、"/"时,从栈中pop出两个数字计算,否则将数字入栈;
class Solution(object):
def evalRPN(self, tokens):
"""
:type tokens: List[str]
:rtype: int
"""
stack = []
for token in tokens:
if token in "+-*/":
b = stack.pop()
a = stack.pop()
if token == '+':
stack.append(a+b)
elif token == '-':
stack.append(a-b)
elif token == '*':
stack.append(a*b)
else:
if a*b < 0:
stack.append(abs(a)//abs(b)*(-1))
else:
stack.append(a//b)
else:
stack.append(int(token))
return stack[-1]
1、首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
2、读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。
3、从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
4、如果不是数字,该字符则是运算符,此时需比较优先关系。
具体做法是:将该字符与运算符栈顶的运算符的优先关系相比较。如果该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。若不是的话,则将栈顶的运算符从栈中弹出,直到栈项运算符的优先级低于当前运算符,将该字符入栈。
5、重复步骤1~2,直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
中等
动态规划
LeetCode1035: https://leetcode-cn.com/problems/uncrossed-lines/
我们在两条独立的水平线上按给定的顺序写下 A 和 B 中的整数。
现在,我们可以绘制一些连接两个数字 A[i] 和 B[j] 的直线,只要 A[i] == B[j],且我们绘制的直线不与任何其他连线(非水平线)相交。
以这种方法绘制线条,并返回我们可以绘制的最大连线数。
当我自己从头到尾把这道题轻松的写出来,且一次提交直接通过了之后,我发现:这一个多月的学习真的没有白费!!!
动态规划即可解决,状态转移方程:
class Solution(object):
def maxUncrossedLines(self, A, B):
"""
:type A: List[int]
:type B: List[int]
:rtype: int
"""
m, n = len(A), len(B)
dp = [[0 for j in range(n+1)] for i in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
if A[i - 1] == B[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j])
return dp[m][n]
中等
DFS
LeetCode967: https://leetcode-cn.com/problems/numbers-with-same-consecutive-differences/
返回所有长度为 N 且满足其每两个连续位上的数字之间的差的绝对值为 K 的非负整数。
请注意,除了数字 0 本身之外,答案中的每个数字都不能有前导零。例如,01 因为有一个前导零,所以是无效的;但 0 是有效的。
你可以按任何顺序返回答案。
我是用深度优先搜索dfs首先,代码写的有写丑陋,发现有人使用迭代的方法实现也挺好的。
class Solution(object):
def __init__(self):
self.ans = []
def helper(self, N, k, cur):
if len(cur) == N:
self.ans.append(int(cur))
return
if N == 1:
self.ans = [i for i in range(10)]
return
if len(cur) == 0:
for i in range(1, 10):
self.helper(N, k, str(i))
else:
prev = cur[-1]
for i in range(0, 10):
if abs(i - int(prev)) == k:
self.helper(N, k, cur+str(i))
def numsSameConsecDiff(self, N, k):
"""
:type N: int
:type K: int
:rtype: List[int]
"""
self.helper(N, k, "")
return self.ans
# TODO 使用dfs的方法实现
# 参考:https://leetcode-cn.com/problems/numbers-with-same-consecutive-differences/solution/zhi-jie-sheng-cheng-fa-by-amchor-3/
中等
栈
LeetCode739: https://leetcode-cn.com/problems/daily-temperatures/
根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
这道题暴力无法通过,如何使用栈来完成还是挺难想到的,反正我没想到。
参考题解:https://leetcode-cn.com/problems/daily-temperatures/solution/cheng-xu-yuan-de-zi-wo-xiu-yang-739-daily-temperat/
class Solution(object):
def dailyTemperatures(self, T):
"""
:type T: List[int]
:rtype: List[int]
"""
res = [0 for i in range(len(T))]
stack = []
for i in range(len(T)):
while len(stack) and T[i] > T[stack[-1]]:
res[stack[-1]] = i - stack[-1]
stack.pop()
stack.append(i)
return res
简单
LeetCode1128: https://leetcode-cn.com/problems/number-of-equivalent-domino-pairs/
给你一个由一些多米诺骨牌组成的列表 dominoes。
如果其中某一张多米诺骨牌可以通过旋转 0 度或 180 度得到另一张多米诺骨牌,我们就认为这两张牌是等价的。
形式上,dominoes[i] = [a, b] 和 dominoes[j] = [c, d] 等价的前提是 ac 且 bd,或是 ad 且 bc。
在 0 <= i < j < dominoes.length 的前提下,找出满足 dominoes[i] 和 dominoes[j] 等价的骨牌对 (i, j) 的数量。
很容易想到建立哈希索引
class Solution(object):
def numEquivDominoPairs(self, dominoes):
"""
:type dominoes: List[List[int]]
:rtype: int
"""
dic = {i:[] for i in range(1,10)}
for domino in dominoes:
mini, maxi = min(domino), max(domino)
dic[mini].append(maxi)
print(dic)
ans = 0
for d in dic:
for i in range(1,10):
count = dic[d].count(i)
count = count * (count - 1) // 2
ans += count
return ans
将每个多米诺加密成一个两位数后建立hash,不过不知道为什么这个运行时间比上面的还要长
class Solution(object):
def numEquivDominoPairs(self, dominoes):
"""
:type dominoes: List[List[int]]
:rtype: int
"""
dic = collections.defaultdict(int)
for i,j in dominoes:
cur = 10 * i + j if i < j else 10 * j+ i
dic[cur] += 1
ans = 0
for d in dic.values():
ans += d * (d - 1) / 2
return ans