目录
剑指48:最长不含重复字符的子字符串(题解参考) /Leetcode03
剑指48:最长不含重复字符的子字符串(题解参考) /Leetcode03
基本思路:双指针+hashmap
# 如果j不在dict中,增加该元素进入dict;
# 如果j已经在dict中,更新i的位置;同时保证i是向右移动的,所以要加和原来的i比较的逻辑;同时更新重复元素的最新位置
class Solution03():
"""
方法1:滑动窗口+遍历
时间复杂度为O(n)--勘误:这个时间复杂度应该不为O(n),每次在子字符串中要查找一下正在迭代的元素是否在里面
空间复杂度为O(1)
"""
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
if not s: return 0
i, j, l, ans = 0, 0, 1, 1
for j in range(len(s) - 1):
if s.find(s[j+1], i, j+1) == -1:
j += 1
l += 1
ans = max(ans, l)
print('ans = %d and l = %d' % (ans, l))
else:
i = s.find(s[j+1], i, j+1) + 1
l = j + 1 - i + 1
print('---ans = %d and l = %d' % (ans, l))
return ans
"""
方法2:滑动窗口+hashmap
时间复杂度O(n)+空间复杂度O(M):M为最长的子字符串长度
"""
def lengthOfLongestSubstring_02(self, s):
i, ans = 0, 0
dict = {}
for j in range(len(s)):
if s[j] in dict:
# if dict.get(s[j]): # ??? 为啥用这种写法就不行???
i = max(dict[s[j]] + 1, i)
dict[s[j]] = j
ans = max(ans, j-i+1)
return ans
剑指57-I:和为s的两个数字
题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
示例:输入:nums = [2,7,11,15], target = 9 输出:[2,7] 或者 [7,2]
基本思路:左右双指针
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
i = 0
j = len(nums) - 1
while i < j:
if nums[i] + nums[j] > target:
j -= 1
elif nums[i] + nums[j] < target:
i += 1
else:
return [nums[i], nums[j]]
return []
剑指57-II:和为s的连续正数序列
题目:输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
基本思路:双指针,sum<target: 右边界+;sum>target:左边界-;
def findContinuousSequence03(self, target):
"""
方法三:采用滑动窗口,这个思路很牛逼!
if 窗口中的sum < target, 那么右边界往右移动一格;
if 窗口中的sum > target, 那么左边界往右移动一格;
if 窗口中的sum = target, 那么记录下来之后, 左和右边界同时往右移动一格
总结:
只要是递增求和满足条件的情景 都可以用这种滑动窗口求解
!巨坑!:开始写了i for i in(...), 这里算内层循环了,使用了外层循环算子,给搞乱套了
"""
ans = []
i = 1 # 左边界
j = 2 # 右边界
sum = i + j
while(i <= target/2):
if sum < target:
j += 1
sum += j
elif sum > target:
i += 1
sum -= (i-1)
else: # sum == target
ans.append([m for m in (range(i, j+1))]) # !巨坑!:这里开始写了i for i in(...), 这里算内层循环了,使用了外层循环算子,给搞乱套了
i += 1
j += 1
sum = sum - (i-1) + j
return ans
剑指59-I:滑动窗口的最大值/Leetcode239
基本思路:暴力即可
class Solution59(object):
"""
方法1:
小优化:如果新进来的element>前一个窗口的max_value,直接赋值,不用再搜索一遍。
"""
def maxSlidingWindow(self, nums, k):
if not nums: return []
ans = []
i = 0
while i <= len(nums) - k:
ans.append(max(nums[i: i + k]))
i += 1
return ans
"""方法2"""
def maxSlidingWindow(self, nums, k):
deque = collections.deque()
res = []
for i, num in enumerate(nums):
if deque and deque[0] <= i - k: deque.popleft()
while deque and num > nums[deque[-1]]: deque.pop()
deque.append(i)
if i >= k - 1:
res.append(nums[deque[0]])
return res
剑指59-II:队列的最大值(题解参考)
-->同类型题目:How在O(1)能取到栈stack里面的最大值-->维护一个递增的栈,辅助stack中只有新元素大于栈顶元素时,才会push进来;在pop时,如果主stack pop掉元素与辅助stack栈顶元素相等,则辅助站也执行pop操作;
题目:请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。若队列为空,pop_front 和 max_value 需要返回 -1
基本思路:引入队列结构collections.deque();要在O(1)找到max_value, 需要一个辅助队列,保存max_value这个值;维护一个递减的队列;
"""
方法2:
引入队列结构collections.deque()
要在O(1)找到max_value, 需要一个辅助队列,保存max_value这个值
"""
from collections import deque
class MaxQueue_02(object):
def __init__(self):
from collections import deque
self.que = deque()
self.sort_que = deque()
def max_value(self):
"""
时间复杂度为O(1)
"""
return -1 if not self.sort_que else self.sort_que[0]
def push_back(self, value):
"""
均摊时间复杂度为O(1)???如何分析呢???
总共只有n个元素,某一次pop()的元素多了, 那么下一次就是常数级别的时间复杂度, 均摊到每一次也就是O(1)
"""
self.que.append(value)
while self.sort_que and self.sort_que[-1] < value:
self.sort_que.pop()
self.sort_que.append(value)
def pop_front(self):
"""
时间复杂度为O(1)
"""
pop = -1 if not self.que else self.que.popleft()
if pop != -1 and self.sort_que[0] == pop:
self.sort_que.popleft()
return pop
class MaxQueue(object):
"""
方法1:用list实现
求最大值的时间复杂度是O(n)
push_back的时间复杂度是O(1)
pop_front的空间复杂度为O(n)
"""
def __init__(self):
self.val = []
def __str__(self):
return str([i for i in self.val])
def max_value(self):
"""
:rtype: int
"""
if not self.val: return -1
max_val = self.val[0]
for i in range(1, len(self.val)):
if self.val[i] > max_val:
max_val = self.val[i]
return max_val
def push_back(self, value):
"""
:type value: int
:rtype: None
"""
self.val.append(value)
def pop_front(self):
"""
:rtype: int
"""
pop = -1 if not self.val else self.val[0]
self.val = [] if not self.val else self.val[1: ]
return pop
Leetcode76:最小覆盖子串(参考题解)
题目:给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:输入: S = "ADOBECODEBANC", T = "ABC" 输出: "BANC"
基本思路:见代码逻辑
class Solution(object):
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
import collections
need = collections.Counter(t) # 记录需要的t中的元素的个数
needCnt = len(t) # 记录需要的t中元素的总数,避免每次都要check need
# start = end = 0
ans = (0, float('inf')) # 记录最短字符串位置
i = 0 # 指针首部
for j, char in enumerate(s):
if need[char] > 0: # 如果need中没有char这个元素,不会进入到这个分支中;而且need[char] > 0只可能是在t中的元素,非相关元素在need中的val只会为非正数;
needCnt -= 1
need[char] -= 1 # 即使s[j]这个元素不在t中,也需要-1(自动添加元素,然后-1)
if needCnt == 0: # 满足t中的元素已经全部在滑动窗口中
while True:
if need[s[i]] == 0: # 缩小窗口直到找到第一个t中的元素且为需要次数为0跳出
break
need[s[i]] += 1 # 否则更新s[i]
i += 1
if ans[1] - ans[0] > j - i: # 保留符合条件的最小长度
ans = (i, j)
need[s[i]] += 1 # 跳出循环后,更新窗口
needCnt += 1
i += 1
return '' if ans[1] > len(s) else s[ans[0] : ans[1]+1] # 考虑ans没有被更新过的情况
Leetcode15:三数之和
题目:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
示例:给定数组 nums = [-1, 0, 1, 2, -1, -4],满足要求的三元组集合为:[[-1, 0, 1], [-1, -1, 2]]
基本思路:先排序,滑动窗口,遇到重复的值跳过;
class Solution(object):
def threeSum(self, nums):
size = len(nums)
if size < 3: return []
nums.sort()
ans = []
for k in range(size-2):
if k > 0 and nums[k] == nums[k-1]: continue
i, j = k + 1, size - 1
while i < j:
if nums[k] + nums[i] + nums[j] == 0:
ans.append([nums[k], nums[i], nums[j]])
i += 1
j -= 1
while i < j and nums[i] == nums[i-1]: i += 1
while i < j and nums[j] == nums[j+1]: j -= 1
elif nums[k] + nums[i] + nums[j] < 0:
i += 1
while i < j and nums[i] == nums[i-1]: i += 1
else:
j -= 1
while i < j and nums[j] == nums[j+1]: j -= 1
return ans