[剑指Offer]57~59
[剑指Offer]57~59
学习使用工具
剑指Offer http://itmyhome.com/sword-means-offer/sword-means-offer.pdf
LeetCode的剑指Offer题库 https://leetcode.cn/problemset/all/
剑指 Offer 57 - II. 和为s的连续正数序列
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
示例 1:
输入一个正整数 target
,输出所有和为 target
的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
限制:
1 <= target <= 10^5
解法:
双指针滑动窗口,如果当前窗口内元素和小于target,右指针前移;如果当前窗口内元素和大于target,左指针前移;如果当前窗口内元素和等于target,将窗口加入到结果数组,左指针前移。直到左指针等于右指针为止。
def findContinuousSequence(self, target: int) -> List[List[int]]:
ans = []
l = 1
r = 2
while l < r:
sum = (l + r) * (r - l + 1) / 2
if sum < target:
r += 1
elif sum > target:
l += 1
else:
if not l == r:
ans.append([i for i in range(l, r + 1)])
l += 1
return ans
剑指 Offer 58 - I. 翻转单词顺序
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。
示例 1:
输入: "the sky is blue"
输出: "blue is sky the"
示例 2:
输入: " hello world! "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:
输入: "a good example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
说明:
- 无空格字符构成一个单词。
- 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
- 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
解法:
新的一天,新的Offer消失术!
def reverseWords(self, s: str) -> str:
return " ".join(s.split()[::-1])
……本以为这是Offer消失术,但这竟然是正确答案,好吧。
不使用API的话,双指针倒序遍历:
def reverseWords(self, s: str) -> str:
s = s.strip()
i = j = len(s) - 1
ans = []
while i >= 0:
while i >= 0 and s[i] != ' ':
i -= 1
ans.append(s[i + 1: j + 1])
while s[i] == ' ':
i -= 1
j = i
return " ".join(ans)
剑指 Offer 58 - II. 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = "abcdefg", k = 2
输出: "cdefgab"
示例 2:
输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"
限制:
-
1 <= k < s.length <= 10000
解法:
不要告诉我这个也是正确答案。
def reverseLeftWords(self, s: str, n: int) -> str:
return s[n:] + s[:n]
还真是,我无语了。不用切片的话:
def reverseLeftWords(self, s: str, n: int) -> str:
res = []
for i in range(n, len(s)):
res.append(s[i])
for i in range(n):
res.append(s[i])
return ''.join(res)
不用列表的话:
def reverseLeftWords(self, s: str, n: int) -> str:
res = ""
for i in range(n, len(s)):
res += s[i]
for i in range(n):
res += s[i]
return res
剑指 Offer 59 - I. 滑动窗口的最大值
给定一个数组 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
提示:
你可以假设 k 总是有效的,在输入数组 **不为空 **的情况下,1 ≤ k ≤ nums.length
。
解法:
先写个遍历,哎呀果然超时了。
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if not nums:
return []
i = 0
j = k - 1
ans = []
while j < len(nums):
ans.append(max(nums[i: j + 1]))
i += 1
j += 1
return ans
维护一个单调双端队列\(deque\),队列永远保持降序。
滑动窗口初始化:左边界\(i∈[1-k, n-k]\),右边界\(j∈[0,n-1]\)
- 若\(i>0\)且\(deque[0]=nums[i-1]\):队首元素出队;删除\(deque\)内所有小于\(nums[j]\)的元素,保证\(deque\)递减。
- \(nums[j]\)从右端入队。
- 若\(i≥0\),将\(deque[0]\)加入\(res\)
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)):
if i > 0 and deque[0] == nums[i - 1]:
deque.popleft()
while deque and deque[-1] < nums[j]:
deque.pop()
deque.append(nums[j])
if i >= 0:
res.append(deque[0])
return res