KK's blog

每天积累多一些

0%

LeetCode



There is an undirected graph with n nodes, where each node is numbered between 0 and n - 1. You are given a 2D array graph, where graph[u] is an array of nodes that node u is adjacent to. More formally, for each v in graph[u], there is an undirected edge between node u and node v. The graph has the following properties:

There are no self-edges (graph[u] does not contain u). There are no parallel edges (graph[u] does not contain duplicate values).
If v is in graph[u], then u is in graph[v] (the graph is undirected). The graph may not be connected, meaning there may be two nodes u and v such that there is no path between them.

A graph is bipartite if the nodes can be partitioned into two independent sets A and B such that every edge in the graph connects a node in set A and a node in set B.

Return true if and only if it is bipartite.

Example 1:



Input: graph = [[1,2,3],[0,2],[0,1,3],[0,2]]
Output: false
Explanation: There is no way to partition the nodes into two independent sets such that every edge connects a node in one and a node in the other.


Example 2:



Input: graph = [[1,3],[0,2],[1,3],[0,2]]
Output: true
Explanation: We can partition the nodes into two sets: {0, 2} and {1, 3}.


Constraints:

graph.length == n 1 <= n <= 100
0 <= graph[u].length < n 0 <= graph[u][i] <= n - 1
graph[u] does not contain u. All the values of graph[u] are unique.
* If graph[u] contains v, then graph[v] contains u.

题目大意:

无向图中是否存在一个划分,将节点分为两集合,任何一条边都连接着两个集合,也就是不存在一条边在单一集合内。

解题思路:

图上色法。两种颜色,将节点上色0,儿子上色1,若某个节点已经上的色和将要上的色矛盾(来自的路径不同),即不合法

解题步骤:

N/A

注意事项:

  1. 图上色法。两种颜色,将节点上色0,儿子上色1,若某个节点已经上的色和将要上的色矛盾(来自的路径不同),即不合法
  2. 题意表示,图可能是有几个连通图,所以要从每个节点做BFS,除非节点已访问过, Line 4. node_to_color作为visited的功能
  3. return True在两个函数中要写,否则返回None

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def isBipartite(self, graph: List[List[int]]) -> bool:
node_to_color = collections.defaultdict(int)
for i in range(len(graph)):
if i in node_to_color: # disconnected nodes
continue
node_to_color[i] = 0
if not self.bfs(graph, i, node_to_color):
return False
return True

def bfs(self, graph, n, node_to_color):
queue = collections.deque([n])
while queue:
node = queue.popleft()
for neighbor in graph[node]:
if neighbor in node_to_color and node_to_color[neighbor] != 1 - node_to_color[node]:
return False
if neighbor in node_to_color:
continue
queue.append(neighbor)
node_to_color[neighbor] = 1 - node_to_color[node]
return True

算法分析:

时间复杂度为O(V + E),空间复杂度O(V + E)

LeetCode



Given an expression such as expression = "e + 8 - a + 5" and an evaluation map such as {"e": 1} (given in terms of evalvars = ["e"] and evalints = [1]), return a list of tokens representing the simplified expression, such as ["-1*a","14"]

An expression alternates chunks and symbols, with a space separating each chunk and symbol. A chunk is either an expression in parentheses, a variable, or a non-negative integer.
A variable is a string of lowercase letters (not including digits.) Note that variables can be multiple letters, and note that variables never have a leading coefficient or unary operator like "2x" or "-x".

Expressions are evaluated in the usual order: brackets first, then multiplication, then addition and subtraction.
For example, expression = "1 + 2 * 3" has an answer of ["7"].

The format of the output is as follows:

For each term of free variables with a non-zero coefficient, we write the free variables within a term in sorted order lexicographically. For example, we would never write a term like "b*a*c", only "a*b*c".
Terms have degrees equal to the number of free variables being multiplied, counting multiplicity. We write the largest degree terms of our answer first, breaking ties by lexicographic order ignoring the leading coefficient of the term. For example, "a*a*b*c" has degree 4.
The leading coefficient of the term is placed directly to the left with an asterisk separating it from the variables (if they exist.) A leading coefficient of 1 is still printed. An example of a well-formatted answer is ["-2*a*a*a", "3*a*a*b", "3*b*b", "4*a", "5*c", "-6"].
Terms (including constant terms) with coefficient 0 are not included. For example, an expression of "0" has an output of [].

Example 1:

Input: expression = “e + 8 - a + 5”, evalvars = [“e”], evalints = [1]
Output: [“-1a”,”14”]


Example 2:

Input: expression = “e - 8 + temperature - pressure”, evalvars = [“e”, “temperature”], evalints = [1, 12]
Output: [“-1pressure”,”5”]


Example 3:

Input: expression = “(e + 8)  (e - 8)”, evalvars = [], evalints = []
Output: [“1
ee”,”-64”]


Constraints: 1 <= expression.length <= 250
expression consists of lowercase English letters, digits, '+', '-', `’,‘(‘,‘)’,‘ ‘. *expressiondoes not contain any leading or trailing spaces. * All the tokens inexpressionare separated by a single space. *0 <= evalvars.length <= 100*1 <= evalvars[i].length <= 20*evalvars[i]consists of lowercase English letters. *evalints.length == evalvars.length*-100 <= evalints[i] <= 100`

题目大意:

表达式含有若干变量evalvars及其对应值evalints,且含加减乘和括号,求结果。若变量不在evalvars就简化表达式

解题思路:

此题不需要掌握,若考到就认命好了。之前的LeetCode 224 Basic Calculator含有括号和加法已经是Hard,此题不但有括号和加减乘,还有变量,难度不止提高一个数量级。不过不可以用eval函数的条件去掉了。所以就是考察eval。
如果不含变量,直接调用eval即可求解

Python代码:

1
2
def basicCalculatorIV(self, expression: str, evalvars: List[str], evalints: List[int]) -> List[str]:
return eval(expression)

含变量且变量有值,就调用字典将变量替代掉,这里考到了regex替代函数re.sub

Python代码:

1
2
3
4
5
6
7
8
9
10
11
def basicCalculatorIV(self, expression: str, evalvars: List[str], evalints: List[int]) -> List[str]:
var_to_val = dict(zip(evalvars, evalints))

def f(s):
token = s.group()
s = str(var_to_val[token] if token in var_to_val else token)
return s

converted_expr = re.sub(r'\w+', f, expression)
res = eval(converted_expr)
return res

由于变量可能没有值,所以核心思路是用dict进行计算,如x + 2,用集合求和{(x,): 1} + {(): 2}得到{(‘x’,): 1, (): -2},用dict来计算及保存结果

解题步骤:

  1. regex替代变量
  2. 将表达式用f包装,如(f(“x”) + f(“8”)) * (f(“x”) - f(“8”))
  3. 实现dict的加减乘
  4. dict的计算结果转成题目所求

注意事项:

  1. Python代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    def basicCalculatorIV(self, expression: str, evalvars: List[str], evalints: List[int]) -> List[str]:
    class MyCounter(Counter):
    def __add__(self, other):
    self.update(other)
    return self

    def __sub__(self, other):
    self.subtract(other)
    return self

    def __mul__(self, other):
    product = MyCounter()
    for x in self:
    for y in other:
    xy = tuple(sorted(x + y))
    product[xy] += self[x] * other[y]
    return product

    var_to_val = dict(zip(evalvars, evalints))

    def f(s):
    token = s
    s = str(var_to_val[token] if token in var_to_val else token)
    return MyCounter({(s, ): 1}) if s.isalpha() else MyCounter({(): int(s)})

    converted_expr = re.sub(r'(\w+)', r'f("\1")', expression)
    # (f("x") + f("8")) * (f("x") - f("8"))
    res = eval(converted_expr) #
    # C({('x', 'x'): 1, ('x',): 0, (): -64})
    return ['*'.join((str(res[x]), ) + x)
    for x in sorted(res, key=lambda x: (-len(x), x))
    if res[x]]

算法分析:

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

[
[“3234.html”, “xys.html”, “7hsaa.html”], // user1
[“3234.html”, “sdhsfjdsh.html”, “xys.html”, “7hsaa.html”] // user2
]

输出两个user的最长连续且相同的访问记录。

题目大意:

求连续最长子数组

解题思路:

类似于LeetCode 1143先求最长公共子字符串。

LeetCode 1143 Longest Common Subsequence, 求最长公共子字符串
Karat 002 Longest Common Continuous Subarray 一样的题目,结果类型不同:最长长度和结果

不同之处在于:

  1. 由于是连续,所以递归只有相同的情况,其他情况为0。
  2. 答案不是最后一位,而是全局最值

递归式为

1
2
dp[i][j] = dp[i - 1][j - 1] + 1 if text1[i - 1] == text2[j - 1]
= 0

解题步骤:

N/A

注意事项:

  1. 递归只有一种情况
  2. 答案需求全局

Python代码:

1
2
3
4
5
6
7
8
9
10
11
# dp[i][j] = dp[i - 1][j - 1] + 1 if text1[i - 1] == text2[j - 1]
# = 0
def longestCommonContinuous(self, text1: str, text2: str) -> int:
dp = [[0 for _ in range(len(text2) + 1)] for _ in range(len(text1) + 1)]
res = 0
for i in range(1, len(dp)):
for j in range(1, len(dp[0])):
if text1[i - 1] == text2[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
res = max(res, dp[i][j])
return res

优化空间:

1
2
3
4
5
6
7
8
9
def longestCommonContinuous(self, text1: str, text2: str) -> int:
dp = [[0 for _ in range(len(text2) + 1)] for _ in range(2)]
res = 0
for i in range(1, len(text1) + 1):
for j in range(1, len(dp[0])):
if text1[i - 1] == text2[j - 1]:
dp[i % 2][j] = dp[(i - 1) % 2][j - 1] + 1
res = max(res, dp[i % 2][j])
return res

回到原题,输入是列表而不是字符串,但原理一样。还有需要输出公共结果,而不是数字

Python代码:

1
2
3
4
5
6
7
8
9
10
11
def longestCommonContinuousSubarray(self, history1, history2):
dp = [[0 for _ in range(len(history2) + 1)] for _ in range(len(history1) + 1)]
max_len, res = 0, []
for i in range(1, len(dp)):
for j in range(1, len(dp[0])):
if history1[i - 1] == history2[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
if dp[i][j] > max_len:
max_len = dp[i][j]
res = history1[i - dp[i][j]:i]
return res

算法分析:

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

LeetCode



You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

Return the number of combinations that make up that amount. If that amount of money cannot be made up by any combination of the coins, return 0.

You may assume that you have an infinite number of each kind of coin.

The answer is guaranteed to fit into a signed 32-bit integer.

Example 1:

Input: amount = 5, coins = [1,2,5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1


Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.


Example 3:

Input: amount = 10, coins = [10]
Output: 1


Constraints:

1 <= coins.length <= 300 1 <= coins[i] <= 5000
All the values of coins are unique. 0 <= amount <= 5000

题目大意:

求兑换硬币的种数

解题思路:

类似于LeetCode 322 Coin Change,那题求最小个数,此题求总数,也是用DP。
递归式:

1
dp[i] = sum(dp[j]), i = j + coins[i]

LeetCode 377 Combination Sum IV 题目基本一样,唯一区别是结果元素有序,属于排列
LeetCode 518 Coin Change 2 题目基本一样,唯一区别是结果元素无序,属于组合

解题步骤:

递归5部曲

注意事项:

  1. for循环顺序不能错,先coin再dp,否则会有重复计算,如dp[3] = 2 + 1和1 + 2. 字面上理解也是可以知道重复。但如果coin先的话,就只能用1的硬币,第二轮是只能用2的硬币,如此类推,显然不会重复,dp[3] = dp[2] + 1(只用硬币1), dp[1] + 2(只用硬币2)

Python代码:

1
2
3
4
5
6
7
8
9
# dp[i] = dp[j], i = j + coins[i]
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount + 1)
dp[0] = 1
for coin in coins:
for i in range(len(dp)): # [0, 0]
if i + coin <= amount:
dp[i + coin] += dp[i]
return dp[-1]

算法分析:

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

LeetCode



Given an array of n integers nums and an integer target, find the number of index triplets i, j, k with 0 <= i < j < k < n that satisfy the condition nums[i] + nums[j] + nums[k] < target.

Example 1:

Input: nums = [-2,0,1,3], target = 2
Output: 2
Explanation: Because there are two triplets which sums are less than 2:
[-2,0,1]
[-2,0,3]


Example 2:

Input: nums = [], target = 0
Output: 0


Example 3:

Input: nums = [0], target = 0
Output: 0


Constraints:

n == nums.length 0 <= n <= 3500
-100 <= nums[i] <= 100 -100 <= target <= 100

题目大意:

找三数和小于target的组合个数

解题思路:

类似于三数和等于target,但当小于target时,直接求个数,类似于LeetCode 315 Count of Smaller Numbers After Self。

解题步骤:

N/A

注意事项:

  1. res不是+1而是right - left

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def threeSumSmaller(self, nums: List[int], target: int) -> int:
if len(nums) < 3:
return 0
nums.sort()
res = 0
for i in range(len(nums) - 2):
left, right = i + 1, len(nums) - 1
while left < right:
if nums[i] + nums[left] + nums[right] < target:
res += right - left # remember
left += 1
else:
right -= 1
return res

算法分析:

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

Free mock interview