剑指66.构建乘积数组
给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
示例:
输入: [1,2,3,4,5]
输出: [120,60,40,30,24]
1.因为不能用乘法,所以考虑前后遍历,前遍历,第n位就是前n-1位的乘积,后遍历,临时变量存储乘积,然后乘到数组上。
class Solution:
def constructArr(self, a: List[int]) -> List[int]:
result = [1]*len(a)
for i in range(1,len(a)):
result[i] = result[i-1]*a[i-1]
temp = 1
for i in range(len(a)-2, -1, -1):
temp *= a[i+1]
result[i] *= temp
return result
**自我反思:**思路清晰
剑指67.把字符串转换成整数
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: "42"
输出: 42
1.这里涉及一个溢出问题
遍历字符,判断是否越界,然后每次乘10+,就可以得到结果。最后的int(s) 也可以用 ord(s) - ord(‘0’)来表示,ord返回ASCII数值。
class Solution:
def strToInt(self, str: str) -> int:
result, sign, i, length = 0, 1, 0, (len(str))
int_max = 2**31-1
int_min = -2**31
border = 2**31//10
if str == '': return 0
while str[i] == ' ':
i += 1
if i == length: return 0
if str[i] == '-': sign = -1
if str[i] in '+-': i += 1
for s in str[i:]:
if not '0'<=s<='9': break
if result > border or result == border and s>'7':
return int_max if sign == 1 else int_min
result = result*10 + int(s)
return result*sign
**自我反思:**注意数字溢出问题。
剑指68-1. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
1.写了一个特麻烦的办法,递归遍历所有的结点,每个节点都判断是不是两个结点的祖先,是的话就放进result里,最后return result的最后一个元素。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
self.result = []
self.p = p
self.q = q
def isparent(node1,node2):
if not node1:
return False
if node1 == node2:
return True
return isparent(node1.left,node2) or isparent(node1.right,node2)
def recursion(node):
if not node:
return
if isparent(node,self.p) and isparent(node,self.q):
self.result.append(node)
if node==self.p or node==self.q:
return
recursion(node.left)
recursion(node.right)
recursion(root)
return self.result[-1]
2.这道题的关键点,我的办法没有用上,那就是二叉搜索树。如果当前节点的值大于p和q,就往左子树上查,如果小于p和q,就往右子树上查。当前节点值在p和q之间,则p和q分别在左右子树上,当前节点就是最近公共祖先,return。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
# 迭代
# while root:
# if root.val > p.val and root.val > q.val:
# root = root.left
# elif root.val < p.val and root.val < q.val:
# root = root.right
# else:
# break
# return root
# 递归
if root.val > p.val and root.val > q.val:
return self.lowestCommonAncestor(root.left, p, q)
if root.val < p.val and root.val < q.val:
return self.lowestCommonAncestor(root.right, p, q)
return root
**自我反思:**二叉搜索树的性质要利用好。
剑指68-2. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
1.递归解析:
一.终止条件:
1.当越过叶节点,则直接返回 null ;
2.当 root 等于 p,q ,则直接返回 root ;
二.递推工作:
1.开启递归左子节点,返回值记为 left ;
2.开启递归右子节点,返回值记为 right ;
三.返回值: 根据 left 和 right ,可展开为四种情况;
当 left 和 right 同时为空 :说明 root 的左 / 右子树中都不包含 p,q,返回 null ;
当 left 和 right 同时不为空 :说明 p, q 分列在 root 的 异侧 (分别在 左 / 右子树),因此 root 为最近公共祖先,返回 root ;
当 left 为空 ,right 不为空 :p,q 都不在 root 的左子树中,直接返回 right 。具体可分为两种情况:
p,q 其中一个在 root 的 右子树 中,此时 right 指向 p(假设为 p );
p,q 两节点都在 root 的 右子树 中,此时的 right 指向 最近公共祖先节点 ;
当 left 不为空 , right 为空 :与情况 3. 同理;
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
if not root or root == p or root == q: return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if not left: return right
if not right: return left
return root
**自我反思:**再理一遍思路
面试题30.包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.min(); --> 返回 -2.
- 定义两个stack,第二个stack[-1]存放当前的最小值
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
self.min_stack = []
def push(self, x: int) -> None:
self.stack.append(x)
if self.min_stack == [] or x<=self.min_stack[-1]:
self.min_stack.append(x)
def pop(self) -> None:
temp = self.stack.pop()
if self.min_stack[-1] == temp:
self.min_stack.pop()
def top(self) -> int:
return self.stack[-1]
def min(self) -> int:
return self.min_stack[-1]
自我反思: 这种O(1)复杂度返回最小值或者最大值的题,一定要熟练。
面试题34. 二叉树中和为某一值的路径
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
示例:
给定如下二叉树,以及目标和 target = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
1.递归,一路记录path,当到达叶子节点,并且和与target相同时,将该条path放入result。
class Solution:
def pathSum(self, root: TreeNode, target: int) -> List[List[int]]:
result = []
path = []
def recursion(node,tar):
if not node:return
path.append(node.val)
tar += node.val
if tar == target and not node.left and not node.right:
result.append(path[:])
recursion(node.left,tar)
recursion(node.right,tar)
path.pop()
recursion(root,0)
return result
自我反思: 熟练熟练熟练
面试题59-1. 滑动窗口的最大值
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[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] 7
- 跟O(1)维护最大值有点像,定义双端队列,利用滑动窗口维护最大值。
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
deque = collections.deque()
res, n = [], len(nums)
for i, j in zip(range(1 - k, n + 1 - k), range(n)):
# 删除 deque 中对应的 nums[i-1]
if i > 0 and deque[0] == nums[i - 1]:
deque.popleft()
# 保持 deque 递减
while deque and deque[-1] < nums[j]:
deque.pop()
deque.append(nums[j])
# 记录窗口最大值
if i >= 0:
res.append(deque[0])
return res
自我反思: 剑指二刷完了,但是很多方面依然感觉掌握的不太好,有的思想还是很难想到,还是需要常回顾。