KK's blog

每天积累多一些

0%

LeetCode



Given a non-empty array of integers nums, every element appears twice except for one. Find that single one.

You must implement a solution with a linear runtime complexity and use only constant extra space.

Example 1:

Input: nums = [2,2,1]
Output: 1


Example 2:

Input: nums = [4,1,2,1,2]
Output: 4


Example 3:

Input: nums = [1]
Output: 1


Constraints:

`1 <= nums.length <= 3 104*-3 104 <= nums[i] <= 3 104`
* Each element in the array appears twice except for one element which appears only once.

题目大意:

数列中,所有数都出现两次除了一个数,求这一个数

异或解题思路(推荐):

Easy题

解题步骤:

N/A

注意事项:

  1. Python代码:

    1
    2
    3
    4
    5
    def singleNumber(self, nums: List[int]) -> int:
    res = 0
    for n in nums:
    res ^= n
    return res

算法分析:

时间复杂度为O(n),空间复杂度O(1)


HashMap算法II解题思路:

记录频数,最直观解法

Python代码:

1
2
3
def singleNumber2(self, nums: List[int]) -> int:
num_to_count = collections.Counter(nums)
return [n for n, count in num_to_count.items() if count == 1][0]

算法分析:

时间复杂度为O(n),空间复杂度O(n)


Math算法III解题思路:

用set求单一元素和乘以2减去原数组的和

Python代码:

1
2
def singleNumber3(self, nums: List[int]) -> int:
return 2 * sum(set(nums)) - sum(nums)

算法分析:

时间复杂度为O(n),空间复杂度O(n)

LeetCode 010 Regular Expression Matching

Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

Note:

  • s could be empty and contains only lowercase letters a-z.
  • p could be empty and contains only lowercase letters a-z, and characters like . or *.

Example 1:

**Input:**
s = "aa"
p = "a"
**Output:** false
**Explanation:** "a" does not match the entire string "aa".

Example 2:

**Input:**
s = "aa"
p = "a*"
**Output:** true
**Explanation:** '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".

Example 3:

**Input:**
s = "ab"
p = ".*"
**Output:** true
**Explanation:** ".*" means "zero or more (*) of any character (.)".

Example 4:

**Input:**
s = "aab"
p = "c*a*b"
**Output:** true
**Explanation:** c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".

Example 5:

**Input:**
s = "mississippi"
p = "mis*is*p*."
**Output:** false

题目大意:

这道求正则表达式匹配的题和那道Leetocde 044 Wildcard Matching很类似,不同点在于*的意义不同,在之前那道题中,
*表示可以代替任意个数的不同字符,而这道题中的*表示之前一个字符(同样字符)可以有0-N个匹配。此题更难一些。

解题思路:

这是经典题。两字符串匹配题基本就是DP而且知道子问题答案可以推导下一个。

  1. 定义dp[i][j]为字符串s[i-1]和p[j-1]是否能匹配。
  2. 递归式为dp[i][j] = dp[i-1][j-1] && (p[j-1] == . || s[i-1] == p[j-1])
    OR ((dp[i-1][j] && (s[i-1] == p[j-2] || p[j-2] == .)) || dp[i][j-2]) && p[j-1] == *
    第一种情况为非*,通配一样字符或.
    第二种情况为*,如果通配(有条件:与p的前一个字符相等或p为.)就是只移动s,dp[i-1][j]。若不通配就只移动p及其前一个字符
  3. 方向为从左到右,从上到下。初始值为dp[0][0] = true。以及若s为空,p为多个*时候,dp[0][j]=true。

与Wildcard Matching不同之处用黑体标注了:

  1. 用.代替?
  2. *情况,不匹配p移动两位而不是一位。
  3. *情况,匹配带条件而不是无条件。
  4. 初始化用dp[0][j-2]而不是j-1。

注意事项:

  1. 递归式含*不匹配情况dp[i][j-2]。
  2. 初始化s为空,p为多个*。此情况其实与递归式符合,因为i=1开始,所以i=0的时候,dp[i-1][j]为负值省去,
    只取dp[i][j-2]。
  3. 循环中j按理应该从2开始,因为递归式含p[j-2], 这样要初始化第1列(第0列已经初始化为False)。由于题目条件保证星号前一定有其他字符,所以p的第0位不能是星号,所以j可以从1开始
  4. Python中任何矩阵初始化都只能用两重for循环而不能用乘号

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
def isMatch(self, s: str, p: str) -> bool:
dp = [[False for _ in range((len(p) + 1))] for _ in range(len(s) + 1)]
dp[0][0] = True
for j in range(2, len(dp[0])):
dp[0][j] = dp[0][j - 2] and p[j - 1] == '*'

for i in range(1, len(dp)):
for j in range(1, len(dp[0])):
if p[j - 1] != '*':
dp[i][j] = dp[i - 1][j - 1] and (s[i - 1] == p[j - 1] or p[j - 1] == '.')
else:
dp[i][j] = (dp[i - 1][j] and (s[i - 1] == p[j - 2] or p[j - 2] == '.')) or (dp[i][j - 2])
return dp[-1][-1]

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public boolean isMatch(String s, String p) {
boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
dp[0][0] = true;
for(int j = 2; j < dp[0].length; j++)
if(p.charAt(j-1) == '*') // remember s="", p="a*"
dp[0][j] = dp[0][j-2];
for(int i = 1; i < dp.length; i++)
for(int j = 1; j < dp[0].length; j++) {
if(p.charAt(j-1) == '*')
dp[i][j] = (dp[i][j-2] || ((s.charAt(i-1) == p.charAt(j-2)
|| p.charAt(j-2) == '.') && dp[i-1][j]));
else
dp[i][j] = dp[i-1][j-1] && (s.charAt(i-1) == p.charAt(j-1)
|| p.charAt(j-1) == '.');
}
return dp[dp.length -1][dp[0].length - 1];
}

算法分析:

时间复杂度为O(n*m),空间复杂度为O(n*m)

LeetCode 042 Trapping Rain Water

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.


The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

Example:

**Input:** [0,1,0,2,1,0,1,3,2,1,2,1]
**Output:** 6

题目大意:

给出 n 个非负整数,代表一张X轴上每个区域宽度为 1 的海拔图, 计算这个海拔图最多能接住多少(面积)雨水。

解题思路:

画图解题。
比较直观的方法是找低谷,只有低谷才可以藏水。用一个递减栈来存所有呈递减趋势的下标,而当上升时就计算藏水量。

从图可以看出,栈中有最高的,3,2,1,最矮的已经出栈了。蓝色的bar准备入栈。计算水量是水平计算的。具体而言,
右边界是确定的,左边界以及高度都是由此bar相邻的在栈中的bar确定的。如1的水量由bar2的高度和位置确定。
同理bar2的水量由bar3确定。实质上,是求出栈的元素之间的水量,既然是之间,最后一个出栈就要特殊处理,需要准入栈
元素来确定。特殊之处是计算栈中最后一个将要被新bar踢出栈的bar3时,并没有相邻的bar作参考,
导致它需要用新bar作为参考,所以它不能在while中处理,需要特别处理,主要因为它是最后一个,属于edge case。

解题步骤:

  1. 遍历数组
  2. 若比上一个高度递增,出栈直至栈中下标对应高度大于当前高度(保持递减栈)。每次出栈,用上一轮的高度作为底部计算高度差
    乘以下标距离即为横向藏水增量,更新底部进入下一次出栈。
  3. 出栈完成后,根据新bar计算最后一个bar的水量,用当前高度计算藏水增量。
  4. 加入下标到栈中

公式:

1
水量 = 准入栈下标与相邻栈(栈顶)的下标i - stack[-1] - 1 乘以 (相邻栈高度 - 刚出栈高度)

注意事项:

  1. [公式]水量 = 准入栈下标与相邻栈(栈顶)的下标i - stack[-1] - 1 乘以 (相邻栈高度 - 刚出栈高度)
    水量的宽度并不是用刚出栈的下标,因为如上图,2-3之间可能实际上不相邻(有些高度已出栈),若用当前栈的下标会忽略了2-3之间的水量。
  2. 最后一个出栈的宽度计算要还有相邻栈(栈顶)i - stack[-1] - 1才计算也就是栈不为空if stack。
  3. 最后一个出栈的高度公式要改成相邻栈高度 -> 准入栈高度。

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
def trap(self, height: List[int]) -> int:
stack = []
sum = 0
for i in range(len(height)):
j = -1
while stack and height[i] > height[stack[-1]]:
j = stack.pop()
if stack and height[i] > height[stack[-1]]:
sum += (height[stack[-1]] - height[j]) * (i - stack[-1] - 1) # stack[-1] is the neighboring index
if stack and j > 0:
sum += (height[i] - height[j]) * (i - stack[-1] - 1)
stack.append(i)
return sum

算法分析:

时间复杂度为O(n),空间复杂度O(n)


算法II解题思路(推荐):

算法I主要从面考虑,现在我们从点来考虑。下标4的水量取决于向左最大值(下标0)和向右最大值(下标12)中的较小值。
问题转化为求每个点的向左向右最大值。数组从左到右扫描,把当前最大值存入leftHeight中,这是向左最大值。

同理,数组从又到左扫描,得到向右最大值。对每个点取向左向右最大值的较小者,从而计算水量。此法实现起来简单很多。

Python代码:

1
2
3
4
5
6
7
8
9
10
11
def trap(self, height: List[int]) -> int:
max_height = max(height)
max_index = height.index(max_height)
sum, left_max, right_max = 0, 0, 0
for i in range(max_index):
sum += max(0, left_max - height[i])
left_max = max(left_max, height[i])
for i in range(len(height) - 1, max_index, -1):
sum += max(0, right_max - height[i])
right_max = max(right_max, height[i])
return sum

算法分析:

时间复杂度为O(n),空间复杂度O(1)

LeetCode



Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2.

You have the following three operations permitted on a word:

Insert a character Delete a character
Replace a character

Example 1:

Input: word1 = “horse”, word2 = “ros”
Output: 3
Explanation:
horse -> rorse (replace ‘h’ with ‘r’)
rorse -> rose (remove ‘r’)
rose -> ros (remove ‘e’)


Example 2:

Input: word1 = “intention”, word2 = “execution”
Output: 5
Explanation:
intention -> inention (remove ‘t’)
inention -> enention (replace ‘i’ with ‘e’)
enention -> exention (replace ‘n’ with ‘x’)
exention -> exection (replace ‘n’ with ‘c’)
exection -> execution (insert ‘u’)


Constraints:
0 <= word1.length, word2.length <= 500
* word1 and word2 consist of lowercase English letters.

题目大意:

求编辑两个字符串的最短距离。编辑操作含加删一个字符,替换一个字符。

解题思路:

求最值且涉及到字符串考虑用DP。递归式为

1
2
dp[i][j] = dp[i-1][j-1]                                   if word1[i-1] == word[j-1]  
= min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1, otherwise

解题步骤:

N/A

注意事项:

  1. 初始值先word2长度再word1.
  2. 初始化上和左边界,表示当一个字符串为空时,另一个字符串的编辑距离是其长度。

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# dp[i][j] = dp[i-1][j-1] if word1[i-1] == word[j-1]
# = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1, otherwise
def minDistance(self, word1: str, word2: str) -> int:
dp = [[0 for _ in range(len(word2) + 1)] for _ in range(len(word1) + 1)]
for i in range(1, len(dp)):
dp[i][0] = i
for j in range(1, len(dp[0])):
dp[0][j] = j
for i in range(1, len(dp)):
for j in range(1, len(dp[0])):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
return dp[-1][-1]

算法分析:

时间复杂度为O(nm),空间复杂度O(nm)

LeetCode



Given an m x n integer matrix matrix, if an element is 0, set its entire row and column to 0‘s, and return the matrix.

You must do it in place.

Example 1:



Input: matrix = [[1,1,1],[1,0,1],[1,1,1]]
Output: [[1,0,1],[0,0,0],[1,0,1]]


Example 2:



Input: matrix = [[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]]


Constraints:

m == matrix.length n == matrix[0].length
1 <= m, n <= 200 -2<sup>31</sup> <= matrix[i][j] <= 2<sup>31</sup> - 1

Follow up:

A straightforward 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?

题目大意:

若矩阵某元素为0,设置它所在的行和列所有元素均为0,不能用额外区间

解题思路:

用第0行和第0列作为统计。由于第0行和第0列会被覆盖,所以先查看他们有无0

解题步骤:

N/A

注意事项:

  1. 用第0行和第0列作为统计。由于第0行和第0列会被覆盖,所以先查看他们有无0。两大步骤:先统计,再根据结果设置0
  2. 第二步中,根据第0和和第0列的结果回设,均从1开始,不含左上cell,因为统计结果不保存在它上。它仅在统计第一行和第一列是否有0时用到。

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def setZeroes(self, matrix: List[List[int]]) -> None:
# calculate
is_zero_row_zero = True if len([0 for n in matrix[0] if n == 0]) > 0 else False
is_zero_col_zero = True if len([0 for i in range(len(matrix)) if matrix[i][0] == 0]) > 0 else False
for i in range(1, len(matrix)):
for j in range(1, len(matrix[0])):
if matrix[i][j] == 0:
matrix[i][0], matrix[0][j] = 0, 0
# Set
for i in range(1, len(matrix)): # remember to start with 1
if matrix[i][0] == 0:
for j in range(1, len(matrix[0])):
matrix[i][j] = 0
for j in range(1, len(matrix[0])): # remember to start with 1
if matrix[0][j] == 0:
for i in range(1, len(matrix)):
matrix[i][j] = 0
if is_zero_row_zero:
for j in range(len(matrix[0])):
matrix[0][j] = 0
if is_zero_col_zero:
for i in range(len(matrix)):
matrix[i][0] = 0

算法分析:

时间复杂度为O(n2),空间复杂度O(1)

Free mock interview