KK's blog

每天积累多一些

0%

LeetCode 138 Copy List with Random Pointer

A linked list of length n is given such that each node contains an additional random pointer, which could point to any node in the list, or null.

Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes should point to new nodes in the copied list such that the pointers in the original list and copied list represent the same list state. None of the pointers in the new list should point to nodes in the original list.

For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding two nodes x and y in the copied list, x.random --> y.

Return the head of the copied linked list.

The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:

  • val: an integer representing Node.val
  • random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not point to any node.

Your code will only be given the head of the original linked list.

Example 1:

<pre>Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]] Output: [[7,null],[13,0],[11,4],[10,2],[1,0]] </pre>

Example 2:

<pre>Input: head = [[1,1],[2,1]] Output: [[1,1],[2,1]] </pre>

Example 3:

<pre>Input: head = [[3,null],[3,0],[3,null]] Output: [[3,null],[3,0],[3,null]] </pre>

Example 4:

<pre>Input: head = [] Output: [] Explanation: The given linked list is empty (null pointer), so return null. </pre>

Constraints:

  • 0 <= n <= 1000
  • -10000 <= Node.val <= 10000
  • Node.random is null or is pointing to some node in the linked list.

题目大意:

复制含next和random的链表。

解题思路(推荐):

此法较容易实现。先复制next指针,然后利用HashMap存储旧新节点,来复制random指针。

注意事项:

  1. 复制next指针和Map中。clone题均用此法。
  2. Random指针不空才copy
  3. 加it = it.next,否则死循环
  4. 如果创建新Node用while it.next表示用它的父节点,否则某个field赋值如random用while it

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def copyRandomList(self, head: 'Node') -> 'Node':
node_map = {}
fake_head, fake_head_copy = Node(0), Node(0)
fake_head.next = head
it, it_copy = fake_head, fake_head_copy
while it.next:
it_copy.next = Node(it.next.val)
node_map[it.next] = it_copy.next
it, it_copy = it.next, it_copy.next

it, it_copy = fake_head.next, fake_head_copy.next
while it:
if it.random:
node_map[it].random = node_map[it.random]
it, it_copy = it.next, it_copy.next
return fake_head_copy.next

梅花间竹解题思路:

第二种方法,梅花间竹,分3部走。

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
def copyRandomList(self, head: 'Node') -> 'Node':
fake_head, fake_head_copy = Node(0), Node(0)
fake_head.next = head

# insert
it = fake_head.next
while it:
temp = it.next
it.next = Node(it.val)
it.next.next = temp
it = it.next.next

# copy random
it = fake_head.next
while it:
if it.random is not None:
it.next.random = it.random.next
it = it.next.next

# delete
it, it_copy = fake_head.next, fake_head_copy
while it:
temp = it.next
it.next = it.next.next
it_copy.next = temp
temp.next = None
it, it_copy = it.next, it_copy.next
return fake_head_copy.next

算法分析:

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

LeetCode

<div>

You are assigned to put some amount of boxes onto one truck. You are given a 2D array boxTypes, where boxTypes[i] = [numberOfBoxes<sub>i</sub>, numberOfUnitsPerBox<sub>i</sub>]:

  • numberOfBoxes<sub>i</sub> is the number of boxes of type i.
  • numberOfUnitsPerBox<sub>i</sub>is the number of units in each box of the type i.

You are also given an integer truckSize, which is the maximum number of boxes that can be put on the truck. You can choose any boxes to put on the truck as long as the number of boxes does not exceed truckSize.

Return the maximum total number of units that can be put on the truck.

Example 1:

<pre>Input: boxTypes = [[1,3],[2,2],[3,1]], truckSize = 4 Output: 8 Explanation: There are:

  • 1 box of the first type that contains 3 units.
  • 2 boxes of the second type that contain 2 units each.
  • 3 boxes of the third type that contain 1 unit each. You can take all the boxes of the first and second types, and one box of the third type. The total number of units will be = (1 * 3) + (2 * 2) + (1 * 1) = 8. </pre>

Example 2:

<pre>Input: boxTypes = [[5,10],[2,5],[4,7],[3,9]], truckSize = 10 Output: 91 </pre>

Constraints:

  • 1 <= boxTypes.length <= 1000
  • 1 <= numberOfBoxes<sub>i</sub>, numberOfUnitsPerBox<sub>i</sub> <= 1000
  • 1 <= truckSize <= 10<sup>6</sup>

</div>

题目大意:

货车能装的最大unit数,每种类型的盒都能装一定数量的units,而每种盒子占地方一样。

解题思路:

由于每种盒子占地一样,所以当然是先放unit大的。贪婪法。

解题步骤:

N/A

注意事项:

  1. 按unit数倒序排序
  2. pair是一个数组,要加pair[i][0]

Python代码:

1
2
3
4
5
6
7
8
9
10
def maximumUnits(self, boxTypes: List[List[int]], truckSize: int) -> int:
pairs = [(li[1], li[0]) for li in boxTypes]
pairs.sort(reverse=True)
res, i = 0, 0
for pair in pairs:
res += pair[0] * min(pair[1], truckSize)
truckSize -= pair[1]
if truckSize <= 0:
break
return res

算法分析:

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

LeetCode

<div>

Given an integer x, return true if x is palindrome integer.

An integer is a palindrome when it reads the same backward as forward.

  • For example, 121 is a palindrome while 123 is not.

Example 1:

<pre>Input: x = 121 Output: true Explanation: 121 reads as 121 from left to right and from right to left. </pre>

Example 2:

<pre>Input: x = -121 Output: false Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome. </pre>

Example 3:

<pre>Input: x = 10 Output: false Explanation: Reads 01 from right to left. Therefore it is not a palindrome. </pre>

Constraints:

  • -2<sup>31</sup> <= x <= 2<sup>31</sup> - 1

Follow up: Could you solve it without converting the integer to a string?</div>

题目大意:

判断是否回文数字

解题思路:

N/A

解题步骤:

N/A

注意事项:

Python代码:

1
2
def isPalindrome(self, x: int) -> bool:
return str(x) == str(x)[::-1]

算法分析:

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


算法II解题思路:

Follow-up 不能用str,就求它的reversed数

注意事项:

  1. x是用于计算过程,所以不断变化,最后一行不能用它来与reversed的结果比较

Python代码:

1
2
3
4
5
6
7
8
def isPalindrome2(self, x: int) -> bool:
if x < 0:
return False
rev, original = 0, x
while x > 0:
rev = rev * 10 + x % 10 # 121
x = x // 10 # 1
return rev == original

算法分析:

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

LeetCode

<div>

You are given the heads of two sorted linked lists list1 and list2.

Merge the two lists in a one sorted list. The list should be made by splicing together the nodes of the first two lists.

Return the head of the merged linked list.

Example 1:

<pre>Input: list1 = [1,2,4], list2 = [1,3,4] Output: [1,1,2,3,4,4] </pre>

Example 2:

<pre>Input: list1 = [], list2 = [] Output: [] </pre>

Example 3:

<pre>Input: list1 = [], list2 = [0] Output: [0] </pre>

Constraints:

  • The number of nodes in both lists is in the range [0, 50].
  • -100 <= Node.val <= 100
  • Both list1 and list2 are sorted in non-decreasing order.

</div>

题目大意:

合并两个LL

解题思路:

N/A

解题步骤:

N/A

注意事项:

  1. Fake Node的引入

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def mergeTwoLists(self, list1: ListNode, list2: ListNode) -> ListNode:
fake_head = ListNode(0)
it, it2, it_res = list1, list2, fake_head
while it and it2:
if it.val <= it2.val:
it_res.next = it
it = it.next
it_res = it_res.next
else:
it_res.next = it2
it2 = it2.next
it_res = it_res.next
if it:
it_res.next = it
if it2:
it_res.next = it2
return fake_head.next

算法分析:

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

LeetCode 139 Word Break

<div>

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

Note:

  • The same word in the dictionary may be reused multiple times in the segmentation.
  • You may assume the dictionary does not contain duplicate words.

Example 1:

<pre>Input: s = "leetcode", wordDict = ["leet", "code"] Output: true Explanation: Return true because "leetcode" can be segmented as "leet code". </pre>

Example 2:

<pre>Input: s = "applepenapple", wordDict = ["apple", "pen"] Output: true Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".   Note that you are allowed to reuse a dictionary word. </pre>

Example 3:

<pre>Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] Output: false </pre>

</div>

题目大意:

一个字符串s,是否能够被“字典集合”(wordDict)中的单词拼接而成。

解题思路:

这是经典题。如果知道s[0:n-1)很容易知道s[0:n)是否有解,既然和子问题有关,就用DP。

  1. 定义dp[i]为字符串s[0,i)是否可以合法分解。
  2. 判断一个字符串是否可以合法分解,方案是尝试在每一位进行分解,若其中一个可分解,即有解。
    递归式为dp[i] |= dp[k] && isWord(s[k:i)), 0 <= k < i.
  3. 方向为从左到右i=0..n, 初始值为dp[0] = true.

注意事项:

  1. 初始值dp[0] = True。
  2. 递归中dp[i]用或操作符。
  3. s[j:i] in word_set不要忘记上限为i

Python代码:

1
2
3
4
5
6
7
8
9
10
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
if not s:
return False
word_set = set(wordDict)
dp = [False] * (len(s) + 1)
dp[0] = True
for i in range(1, len(dp)):
for j in range(0, i):
dp[i] |= dp[j] and s[j:i] in word_set
return dp[len(dp) - 1]

注意事项:

  1. 将两个输入都转换成小写。
  2. 递归中dp[i]用或操作符。

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public boolean wordBreak(String s, List<String> wordDict) {
if(s == null || s.isEmpty() || wordDict == null || wordDict.size() == 0)
return false;
Set<String> wordDictLower = new HashSet<>();
for(String c : wordDict)
wordDictLower.add(c.toLowerCase());
s = s.toLowerCase();

boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for(int i = 1; i < dp.length; i++)
for(int k = 0; k < i; k++) {//"a"
dp[i] |= dp[k] && wordDictLower.contains(s.substring(k, i));
}
return dp[dp.length - 1];
}

算法分析:

时间复杂度为<code>O(n<sup>2</sup>)</code>,空间复杂度为O(n)


算法II解题思路:

这题也可以额用DFS来解。如果可以用DP就尽量用DP,只有求所有可能性才只能用DFS而不能用DP。
这道题递归子问题dfs[:n]为子串[0:n)是否可合法拆解。对于子问题而言,需要对其范围内i=[0:st)的每个可能位置分解 dfs[:i) + word[i:st)从而求出dfs(st)的解,只有任一分解成功,dfs(st)=true,否则false。

Cache的应用场景: 如果子问题重复就要用Cache。
例如dfs(10)=dfs(9)+s[9:9] = (dfs(8) + s[8:8]) + s[9:9]
=dfs(8)+s[8:9]
dfs(8)由第一层递归第二个循环s[8:9]和第二层递归s[9:9]达到,这是重复的子问题dfs(8)。如果不cache,dfs(8)的求解
是重复的。

Cache模板:

  1. key为子问题索引st,value为子问题的解。
  2. 紧跟终结条件,若在cache中,返回子问题的解。
  3. 循环结束,将子问题的结果存于cache。

注意事项:

  1. 将两个输入都转换成小写。
  2. 递归中先查询词是否在字典中再递归。如果顺序调转就会LTE,因为这些子问题是白费的。
  3. 递归终结条件为st==0而不是st==s.length()因为子问题递归从右到左。

Java代码:

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
public boolean wordBreakDFS(String s, List<String> wordDict) {
if(s == null || s.isEmpty() || wordDict == null || wordDict.size() == 0)
return false;
Set<String> wordDictLower = new HashSet<>();
for(String c : wordDict)
wordDictLower.add(c.toLowerCase());
s = s.toLowerCase();
Map<Integer, Boolean> cache = new HashMap<>();
return dfs(s, wordDictLower, s.length(), cache);
}

// subproblem's answer dfs[:st)
boolean dfs(String s, Set<String> wordDict, int st, Map<Integer, Boolean> cache) {
if(st == 0)
return true;
if(cache.containsKey(st))
return cache.get(st);
boolean re = false;
for(int i = 0; i < st; i++) {
if(wordDict.contains(s.substring(i, st)) && dfs(s, wordDict, i, cache))
return true;
}
cache.put(st, re);
return false;
}

算法分析:

时间复杂度为<code>O(n<sup>2</sup>)</code>,空间复杂度为O(n)

Free mock interview