3sum
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.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路1:
(a + b + c) = 0 ⇒ -(a + b) = c
步骤
- 先把nums遍历一遍存到字典里面,记录它出现的次数 ⇒ 时间复杂度:O(n)
- 以 o(n^2) 的复杂度找到所有 (a, b) 对,然后在字典里找有没有-(a + b) ,且要防止重复 ⇒ 时间复杂度O(n ^ 2)
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
dic = {}
for num in nums:
if num in dic:
dic[num] += 1
else:
dic[num] = 1
ans = []
visited = set()
for i in range(len(nums)):
for j in range(i+1, len(nums)):
a, b, c = order(nums[i], nums[j], -(nums[i]+nums[j]))
if (a, b) in visited:
continue
else:
visited.add((a, b))
dic[nums[i]] -= 1
dic[nums[j]] -= 1
if (-(nums[i]+nums[j])) in dic and dic[(-(nums[i]+nums[j]))] > 0:
ans.append([a, b, c])
dic[nums[i]] += 1
dic[nums[j]] += 1
return ans
def order(x, y, z):
arr = [x, y, z]
arr.sort()
return arr[0], arr[1], arr[2]
然鹅杯具出现了,这种方法并不能100% accepted by leetcode
由此引到了最优方法:
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
nums.sort()
ans = []
for i in range(len(nums)):
if (i != 0 and nums[i] == nums[i-1]):
continue
k = len(nums) - 1
j = i + 1
while (j < k):
if (nums[i] + nums[j] + nums[k]) == 0:
ans.append([nums[i], nums[j], nums[k]])
j += 1
while (j < k and nums[j] == nums[j-1]):
j += 1
elif (nums[i] + nums[j] + nums[k]) < 0:
j += 1
else:
k -= 1
return ans
其思路就是利用3个指针,而且要将数据排号序,时间复杂度是O(n^2)
为什么更优?
- 防止重复的步骤精简了好多
- 利用了有序数组的性质
3sum相关的题目
2sum
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
其思路就和3sum中的第一个naive思路比较像
python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dic = {}
for i in range(len(nums)):
if target - nums[i] in dic:
return [dic[target - nums[i]], i]
else:
dic[nums[i]] = i
3sum closet
Given an array nums of n integers and an integer target, find three integers in nums such that the sum is closest to target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
Example:
Given array nums = [-1, 2, 1, -4], and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
这道题目比3sum更简单一点,因为它只需要返回最接近target的值
基本思路比较像 都是利用了三个指针
时间复杂度是 O(n^2)
class Solution(object):
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
nums.sort()
minVal = 2**31 - 1
for i in range(len(nums)):
j = i + 1
k = len(nums) - 1
while (j < k):
val = nums[i] + nums[k] + nums[j] - target
if (val == 0):
minVal = val
break
if abs(val) < abs(minVal):
minVal = val
if val > 0:
k -= 1
else:
j += 1
return (minVal + target)
Group Anagrams
Given an array of strings, group anagrams together.
Example:
Input: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
Output:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]
Note:
All inputs will be in lowercase.
The order of your output does not matter.
class Solution(object):
def groupAnagrams(self, strs):
# letterMatrix
# record the occuring times of letter from a to z
# [0, 0, .... , 0, 0]
# [0, 0, .... , 0, 1]
# [0, 0, .... , 0, 2]
# .....
# [0, 0, .... , 0, n]
letterMatrix = []
for i in range(len(strs)):
letterMatrix.append([0] * 26 + [i])
for i in range(len(strs)):
for ch in strs[i]:
letterMatrix[i][ord(ch) - ord('a')] += 1
letterMatrix.sort() # 排序
ans = []
ans.append([strs[letterMatrix[0][-1]]])
for i in range(1, len(letterMatrix)):
curStrIdx = letterMatrix[i][-1]
if equal(letterMatrix[i], letterMatrix[i - 1]): # 如果这两个str包含的letter和个数都相同,它们归为一组
ans[-1].append(strs[curStrIdx])
else: # 否则分到新的一组
ans.append([strs[curStrIdx]])
return ans
def equal(arr1, arr2):
for i in range(25, 0, -1):
if arr1[i] != arr2[i]:
return False
return True
上面的解法时间复杂度是O(NK + NlogN),N, K分别是strs的元素个数和strs里面最长的字符串长度,但是可以优化成O(NK),方法就是利用字典来存字母出现的个数,而不是用列表。
class Solution(object):
def groupAnagrams(self, strs):
ans = []
dic = {}
for str in strs:
chars = [0] * 26
for ch in str:
chars[ord(ch) - ord('a')] += 1
chars = tuple(chars)
if chars in dic: # 如果str的anagram已经被访问过,那么这个str将会和之前的分到一组
ans[dic[chars]].append(str)
else: # 否则它自己新成立一组
dic[chars] = len(ans)
ans.append([str])
return ans
Set Matrix Zeroes
Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in-place.
Example 1:
Input:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
Output:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
Example 2:
Input:
[
[0,1,2,0],
[3,4,5,2],
[1,3,1,5]
]
Output:
[
[0,0,0,0],
[0,4,5,0],
[0,3,1,0]
]
Follow up:
A straight forward solution using O(mn) space is probably a bad idea.
A simple improvement uses O(m + n) space, but still not the best solution.
Could you devise a constant space solution?
class Solution(object):
def setZeroes(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
Questions:
- Are there more than one element equals 0?
- What are the requirements for space complexity?
- What if the matrix is Null?
- data type? always int?
"""
m = len(matrix)
n = len(matrix[0])
rows = [False] * m
cols = [False] * n
for i in range(m):
for j in range(n):
if matrix[i][j] == 0:
rows[i] = True
cols[j] = True
for i in range(len(rows)):
if rows[i]:
for j in range(n):
matrix[i][j] = 0
for j in range(len(cols)):
if cols[j]:
for i in range(m):
matrix[i][j] = 0
空间复杂度为O(m + n),空间复杂度的优化方法我没想出来,参见leetcode上的原题答,这个方法有点巧妙。
class Solution(object):
def setZeroes(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
Questions:
- Are there more than one element equals 0?
- What are the requirements for space complexity?
- What if the matrix is Null?
- data type? always int?
"""
m = len(matrix)
n = len(matrix[0])
row_flag = False
col_flag = False
print(matrix)
for i in range(m):
for j in range(n):
if matrix[i][j] == 0:
matrix[i][0] = 0 # 这一行全是0
matrix[0][j] = 0 # 这一列全是0
if (i == 0):
row_flag = True
if (j == 0):
col_flag = True
for i in range(1, m):
if matrix[i][0] == 0:
matrix[i] = [0] * n
for j in range(1, n):
if matrix[0][j] == 0:
for i in range(m):
matrix[i][j] = 0
#print(matrix)
if row_flag:
matrix[0] = [0] * n
if col_flag:
for i in range(m):
matrix[i][0] = 0
#print(matrix)
Longest Substring Without Repeating Characters
Given a string, find the length of the longest substring without repeating characters.
Example 1:
Input: “abcabcbb”
Output: 3
Explanation: The answer is “abc”, with the length of 3.
Example 2:
Input: “bbbbb”
Output: 1
Explanation: The answer is “b”, with the length of 1.
Example 3:
Input: “pwwkew”
Output: 3
Explanation: The answer is “wke”, with the length of 3.
Note that the answer must be a substring, “pwke” is a subsequence and not a substring.
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
Questions:
- all lowercase letters? if upper, how to compare?
- if the lest is none?
"""
if not s:
return 0
dic = {}
#for char in "abcdefghijklmnopqrstuvwxyz":
#dic[char] = -1
# 初始化
left = 0
dic[s[0]] = 0
right = 1
maxInterval = 1
while (right < len(s)):
if s[right] in dic:
if dic[s[right]] == -1: # 如果当前字母不重复,用字典记录此字母出现的位置
dic[s[right]] = right
else: # 如果当前字母重复
if right - left > maxInterval:# 判断目前已经visit的这个区间是不是更大
maxInterval = right - left
tmp = dic[s[right]]
for i in range(left, dic[s[right]] + 1): # 从[left, s[right]重复出现的位置]都要舍去
dic[s[i]] = -1
left = tmp + 1 # left移动到重复的字母后一位
dic[s[right]] = right # 字典更新重复的字母的位置
else:
dic[s[right]] = right
right += 1
if right - left > maxInterval:
maxInterval = right - left
return maxInterval
Missing Ranges
Given a sorted integer array nums, where the range of elements are in the inclusive range [lower, upper], return its missing ranges.
Example:
Input: nums = [0, 1, 3, 50, 75], lower = 0 and upper = 99,
Output: [“2”, “4->49”, “51->74”, “76->99”]
这个题目乍一看挺简单的,但是题目里面是有坑的
在写代码前需要考虑:
- 如果nums为空怎么办?此时就直接输出[lower, upper]
- 如果给的lower和upper是unvalid怎么办? 比如, [1,2,3] upper = 2, lower = 2;在leetcode里面这题目是没有这种unvalid输入的;但是在思考问题的时候要全面一点
class Solution(object):
def findMissingRanges(self, nums, lower, upper):
ans = []
try:
for i in range(len(nums)):
if nums[i] > lower:
ans.append([lower, nums[i]-1])
lower = nums[i] + 1
if nums[len(nums)-1] < upper:
ans.append([nums[len(nums)-1] + 1, upper])
except IndexError: # nums为空的情况怎么办
ans.append([lower, upper])
# 虽然分了两种情况,但是对字符串数组的输出做统一处理
output = []
for a in ans:
if a[0] == a[1]:
output.append(str(a[0]))
else:
output.append(str(a[0]) + "->" + str(a[1]))
return output
Longest Palindromic Substring
Solution
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: “babad”
Output: “bab”
Note: “aba” is also a valid answer.
Example 2:
Input: “cbbd”
Output: “bb”
一开始的理解:
“abcda”
Output:
“aba”
Expected:
“a”
并不是的 和CSCI 570老师上课讲的完全不是一个题目 但我还是努力撸出了代码!
class Solution(object):
"""
Question:
- 如果没有回文呢?
- s为空呢
"""
def longestPalindrome(self, s):
if not s:
return ""
n = len(s)
dp = [[0] * n for _ in range(n)]
# 初始化
for i in range(n):
dp[i][i] = 1
for j in range(1, n):
i = j - 1
if (s[j] == s[i]):
dp[i][j] = 2
else:
dp[i][j] = 1
# 动态规划 Bottom up
for k in range(2, n):
for j in range(k, n):
i = j - k
if s[i] == s[j]:
dp[i][j] = dp[i+1][j-1] + 2
else:
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
#return dp[0][n-1]
# top down
i = 0
j = n - 1
left = 0
right = dp[0][n-1]-1
substring = [''] * (dp[0][n-1])
while (i <= j):
if (s[i] == s[j]):
substring[left] = s[i]
substring[right] = s[j]
left += 1
right -= 1
i += 1
j -= 1
else:
if dp[i+1][j] > dp[i][j-1]:
i += 1
else:
j -= 1
return "".join(substring
max = lambda x, y: x if x > y else y
当我满心欢喜的又编了一个分治法的时候,时间又超了,因为你这个时间复杂度其实是O(n^3)呀傻孩子:
class Solution(object):
def longestPalindrome(self, s):
if not s:
return ""
self.s = s
return self.dc(0, len(s)-1)
def dc(self, left, right):
if (left == right):
return self.s[left]
mid = (left + right) / 2
leftStr = self.dc(left, mid)
rightStr = self.dc(mid+1, right)
midStr = ""
for i in range(left, mid + 1):
for j in range(right, mid, -1):
if self.isPalindrome(i, j):
midStr = self.s[i:j+1]
break
if (midStr != ""):
break
return max(max(leftStr, rightStr), midStr)
def isPalindrome(self, lower, upper):
for i in range((upper-lower)/2+1):
if self.s[lower+i] != self.s[upper-i]:
return False
return True
max = lambda s1, s2: s1 if len(s1) > len(s2) else s2
最后还是动态规划方法靠谱
class Solution(object):
def longestPalindrome(self, s):
n = len(s)
if not s:
return ""
# 初始化
dp = [[False] * n for _ in range(n)]
for i in range(n):
dp[i][i] = True
for i in range(n-1):
j = i + 1
if s[i] == s[j]:
dp[i][j] = True
# 状态转换函数:
# dp[i][j] = dp[i+1][j-1] == True && s[i] == s[j]
for k in range(2, n):
for i in range(n-k):
j = i + k
if dp[i+1][j-1] == True and s[i] == s[j]:
dp[i][j] = True
max_j = 0
max_i = 0
for i in range(n):
for j in range(n-1, i, -1):
if dp[i][j] and (j-i) > (max_j-max_i):
max_j = j
max_i = i
return s[max_i:max_j+1]
Increasing Triplet Subsequence
Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the array.
Formally the function should:
Return true if there exists i, j, k
such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false.
Note: Your algorithm should run in O(n) time complexity and O(1) space complexity.
Example 1:
Input: [1,2,3,4,5]
Output: true
Example 2:
Input: [5,4,3,2,1]
Output: false
太神奇了,这个解法
这个解法为什么可以work呢?
- min1之前(包括它本身)一定存在一个比他更小或者相等的数
- min2之前一定存在一个比他小的数
- 那么当后面的数既大于min2, 且min2前面有比他小的数,不就是返回true么?
class Solution(object):
def increasingTriplet(self, nums):
min1 = 2**31 - 1
min2 = 2**31 - 1
for num in nums:
if num < min1:
min1 = num
if num > min1:
min2 = min(num, min2)
if num > min2:
return True
min = lambda x, y: x if x < y else y