KK's blog

每天积累多一些

0%

LeetCode 540 Single Element in a Sorted Array

Given a sorted array consisting of only integers where every element appears twice except for one element which appears once. Find this single element that appears only once.

Example 1:

<pre>Input: [1,1,2,3,3,4,4,8,8] Output: 2 </pre>

Example 2:

<pre>Input: [3,3,7,7,10,11,11] Output: 10 </pre>

Note: Your solution should run in O(log n) time and O(1) space.

题目大意:

一个有序数组中,每个数字都出现了两次,只有一个数字出现了一次,求出现一次的数字。

解题思路:

这是A公司Problem solving的题目。类似于L136。此题数组有序且要求O(logn)时间,所以考虑用二分法。由于没有输入tgt,有点似
算法文档中用二分法求峰值,就是比较相邻两个数做二分法。考虑一个结论,若数组为偶数个数,就一定不存在只出现一次的元素。
所以必须考虑奇偶位,若下标mid为偶数,其后一位与其相等,就一定在右半边搜索left=mid+2(不会是mid和mid+1),如第二个
例子,因为mid左边个数为偶数,利用结论可知不会在左边。同理与后一位不等,搜左边right=mid(可能为mid)。注意边界。
若mid为奇数,mid前面有奇数个,mid包括自己的后面有偶数个,所以mid和mid+1上的数相等,就应在左半搜,所以与偶数位的
情况正好相反,但是边界不同,产生了4个if语句。
法二:改进一下,若mid为奇数位,就mid--归结为偶数位的情况,这样if变成两个。

注意事项:

  1. 类似于Leetcode 033,四种情况,前两种中的第二种全包第一种。
  2. for循环后,答案一定在start和end其中一个。end前面有偶数个与start不同就肯定在start上。

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def singleNonDuplicate(self, nums: List[int]) -> int:
start, end = 0, len(nums) - 1
while start + 1 < end:
mid = start + (end - start) //2
if mid % 2 == 1 and mid >= 1 and nums[mid - 1] == nums[mid]:
start = mid
elif mid % 2 == 1:
end = mid
elif mid % 2 == 0 and mid >= 1 and nums[mid - 1] != nums[mid]:
start = mid
else:
end = mid
if end % 2 == 1 and nums[start] != nums[end]:
return nums[start] # remember
else:
return nums[end]

注意事项:

  1. 边界也就是mid的赋值,写出例子来理解。

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public int singleNonDuplicate(int[] nums) {
int N = nums.length;
int left = 0, right = N - 1;
while (left < right) {
int mid = (right - left) / 2 + left;
boolean isEven = true;
if (mid % 2 == 1) isEven = false;
if ((isEven && nums[mid] != nums[mid + 1]) )
right = mid;
else if (isEven && nums[mid] == nums[mid + 1])
left = mid + 2;
else if (!isEven && nums[mid] == nums[mid + 1])
right = mid-1;
else
left = mid + 1;
}
return nums[left];
}

1
2
3
4
5
6
7
8
9
10
11
12
13
public int singleNonDuplicate2(int[] nums) {
int N = nums.length;
int left = 0, right = N - 1;
while (left < right) {
int mid = (right - left) / 2 + left;
if (mid % 2 == 1) mid--;
if (nums[mid] != nums[mid + 1])
right = mid;
else
left = mid + 2;
}
return nums[left];
}

算法分析:

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

Follow-up:

首先问L316 Given a non-empty array of integers, every element appears twice except for one. Find that single one. XOR解法,不用实现。 Follow up问题是L260 Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once. 分三步。若只有一个数出现1次,只要把所有数异或^即可(相同数异或=0)。如果有两个此数,异或结果是这两数不同的位。只要选为1且最低位(或任意为1的位)lowBit=a-(a&(a-1))。再扫所有数,根据它们在lowBit上=0和=1分组异或num1, num2,最后分组异或后它们为所求

LeetCode

<div>

Let's play the minesweeper game (Wikipedia, online game)!

You are given an m x n char matrix board representing the game board where:

  • 'M' represents an unrevealed mine,
  • 'E' represents an unrevealed empty square,
  • 'B' represents a revealed blank square that has no adjacent mines (i.e., above, below, left, right, and all 4 diagonals),
  • digit ('1' to '8') represents how many mines are adjacent to this revealed square, and
  • 'X' represents a revealed mine.

You are also given an integer array click where click = [click<sub>r</sub>, click<sub>c</sub>] represents the next click position among all the unrevealed squares ('M' or 'E').

Return the board after revealing this position according to the following rules:

  1. If a mine 'M' is revealed, then the game is over. You should change it to 'X'.
  2. If an empty square 'E' with no adjacent mines is revealed, then change it to a revealed blank 'B' and all of its adjacent unrevealed squares should be revealed recursively.
  3. If an empty square 'E' with at least one adjacent mine is revealed, then change it to a digit ('1' to '8') representing the number of adjacent mines.
  4. Return the board when no more squares will be revealed.

Example 1:

<pre>Input: board = [["E","E","E","E","E"],["E","E","M","E","E"],["E","E","E","E","E"],["E","E","E","E","E"]], click = [3,0] Output: [["B","1","E","1","B"],["B","1","M","1","B"],["B","1","1","1","B"],["B","B","B","B","B"]] </pre>

Example 2:

<pre>Input: board = [["B","1","E","1","B"],["B","1","M","1","B"],["B","1","1","1","B"],["B","B","B","B","B"]], click = [1,2] Output: [["B","1","E","1","B"],["B","1","X","1","B"],["B","1","1","1","B"],["B","B","B","B","B"]] </pre>

Constraints:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 50
  • board[i][j] is either 'M', 'E', 'B', or a digit from '1' to '8'.
  • click.length == 2
  • 0 <= click<sub>r</sub> < m
  • 0 <= click<sub>c</sub> < n
  • board[click<sub>r</sub>][click<sub>c</sub>] is either 'M' or 'E'.

</div>

题目大意:

给定扫雷版上的某一个状态,计算扫雷版上的下一个状态

解题思路:

N/A

解题步骤:

N/A

注意事项:

  1. 三种情况: 1. 踩雷,只需更改此格。 2. 踩到数字格也就是雷相邻的格,计算此格的临近雷数,更改此格。 3. 从此格开始BFS访问全版,节点出列后如果是数字格不加入到queue中,否则继续BFS访问,更改此格。
  2. x, y = node[0] + _dx, node[1] + _dy用node[0], node[1]不要用i, j

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
33
34
35
36
37
38
39
def updateBoard(self, board: List[List[str]], click: List[int]) -> List[List[str]]:

if board[click[0]][click[1]] == 'M':
board[click[0]][click[1]] = 'X'
return board
else:
mine_num = self.get_neighboring_mine_num(board, click[0], click[1])
if mine_num > 0:
board[click[0]][click[1]] = str(mine_num)
return board
else:
return self.bfs(board, click[0], click[1])

def bfs(self, board, i, j):
queue = collections.deque([(i, j)])
visited = set([(i, j)])
while queue:
node = queue.popleft()
mine_num = self.get_neighboring_mine_num(board, node[0], node[1])
if mine_num > 0:
board[node[0]][node[1]] = str(mine_num)
continue
else:
board[node[0]][node[1]] = 'B'
for _dx, _dy in OFFSETS:
x, y = node[0] + _dx, node[1] + _dy # remember not to use i, j
if x < 0 or x >= len(board) or y < 0 or y >= len(board[0]) or (x, y) in visited:
continue
queue.append((x, y))
visited.add((x, y))
return board

def get_neighboring_mine_num(self, board, i, j):
num = 0
for _dx, _dy in OFFSETS:
x, y = i + _dx, j + _dy
if 0 <= x < len(board) and 0 <= y < len(board[0]) and board[x][y] == 'M':
num += 1
return num

算法分析:

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

LeetCode

<div>

We are given an array asteroids of integers representing asteroids in a row.

For each asteroid, the absolute value represents its size, and the sign represents its direction (positive meaning right, negative meaning left). Each asteroid moves at the same speed.

Find out the state of the asteroids after all collisions. If two asteroids meet, the smaller one will explode. If both are the same size, both will explode. Two asteroids moving in the same direction will never meet.

Example 1:

<pre>Input: asteroids = [5,10,-5] Output: [5,10] Explanation: The 10 and -5 collide resulting in 10. The 5 and 10 never collide. </pre>

Example 2:

<pre>Input: asteroids = [8,-8] Output: [] Explanation: The 8 and -8 collide exploding each other. </pre>

Example 3:

<pre>Input: asteroids = [10,2,-5] Output: [10] Explanation: The 2 and -5 collide resulting in -5. The 10 and -5 collide resulting in 10. </pre>

Constraints:

  • 2 <= asteroids.length <= 10<sup>4</sup>
  • -1000 <= asteroids[i] <= 1000
  • asteroids[i] != 0

</div>

题目大意:

星体向左向右同速运动,符号表示方向,数值表示星体大小。若相撞,同大小想消,否则较小的消失。

解题思路:

保持原有顺序且相邻元素大小关系,考虑用Stack

解题步骤:

N/A

注意事项:

  1. 两星体可以正负,所以有四种可能:同左,同右,向左向右,向右向左。**只有最后一种向右向左才会相撞。**所以出栈条件为栈顶为正,遍历元素为负。
  2. 同大小要特别处理,记录到is_same_size变量中。入栈条件为出栈条件的非以及不是is_same_size

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
def asteroidCollision(self, asteroids: List[int]) -> List[int]:
stack = []
for i in range(len(asteroids)):
is_same_size = False
while stack and stack[-1] > 0 and asteroids[i] < 0 and -asteroids[i] >= stack[-1]:
stack_top = stack.pop()
if stack_top == -asteroids[i]:
is_same_size = True
break
if not (stack and stack[-1] > 0 and asteroids[i] < 0) and not is_same_size:
stack.append(asteroids[i])
return stack

算法分析:

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

更简洁的写法,不要要掌握while, break, else语句,如果没有break,else永远执行,若break,else不执行。若不熟悉该语法,推荐用上法。

Python代码:

1
2
3
4
5
6
7
8
9
def asteroidCollision2(self, asteroids: List[int]) -> List[int]:
stack = []
for i in range(len(asteroids)):
while stack and stack[-1] > 0 and asteroids[i] < 0:
if -asteroids[i] < stack[-1] or stack.pop() == -asteroids[i]:
break
else:
stack.append(asteroids[i])
return stack

LeetCode

<div>

You are given an array of logs. Each log is a space-delimited string of words, where the first word is the identifier.

There are two types of logs:

  • Letter-logs: All words (except the identifier) consist of lowercase English letters.
  • Digit-logs: All words (except the identifier) consist of digits.

Reorder these logs so that:

  1. The letter-logs come before all digit-logs.
  2. The letter-logs are sorted lexicographically by their contents. If their contents are the same, then sort them lexicographically by their identifiers.
  3. The digit-logs maintain their relative ordering.

Return the final order of the logs.

Example 1:

<pre>Input: logs = ["dig1 8 1 5 1","let1 art can","dig2 3 6","let2 own kit dig","let3 art zero"] Output: ["let1 art can","let3 art zero","let2 own kit dig","dig1 8 1 5 1","dig2 3 6"] Explanation: The letter-log contents are all different, so their ordering is "art can", "art zero", "own kit dig". The digit-logs have a relative order of "dig1 8 1 5 1", "dig2 3 6". </pre>

Example 2:

<pre>Input: logs = ["a1 9 2 3 1","g1 act car","zo4 4 7","ab1 off key dog","a8 act zoo"] Output: ["g1 act car","a8 act zoo","ab1 off key dog","a1 9 2 3 1","zo4 4 7"] </pre>

Constraints:

  • 1 <= logs.length <= 100
  • 3 <= logs[i].length <= 100
  • All the tokens of logs[i] are separated by a single space.
  • logs[i] is guaranteed to have an identifier and at least one word after the identifier.

</div>

题目大意:

排序log file,以下顺序:字母log (内容,id), 数字log

解题思路(推荐):

N/A

解题步骤:

N/A

注意事项:

  1. 排序的multi key实现(0, content_str, li[0]) if is_alpha else (1, ). (1, )表示按数组顺序

Python代码:

1
2
3
4
5
6
7
8
9
def reorderLogFiles(self, logs: List[str]) -> List[str]:

def get_key(x):
li = x.split(' ')
content_str = ' '.join(li[1:])
is_alpha = 1 if content_str[0].isalpha() else 0
return (0, content_str, li[0]) if is_alpha else (1, )

return sorted(logs, key=get_key)

算法分析:

时间复杂度为O(nmlogn),空间复杂度O(mn), n为log数量,m为每个log的最长长度。如mergesort中merge复杂度为nm, 每个key比较是O(m)复杂度


算法II解题思路:

我的解法。本质上和上法一致,较繁琐

注意事项:

  1. 字母log排序不能按content_str + ' ' + li[0], 而是(content_str, li[0])作多key排序

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
def reorderLogFiles2(self, logs: List[str]) -> List[str]:
letter_logs, digit_logs = [], []
for i in range(len(logs)):
if logs[i][-1].isdigit():
digit_logs.append(logs[i])
else:
li = logs[i].split(' ')
content_str = ' '.join(li[1:])
letter_logs.append((content_str, li[0], i))
letter_logs.sort()
res = [logs[pair[2]] for pair in letter_logs]
res += digit_logs
return res

LeetCode

<div>

Design a time-based key-value data structure that can store multiple values for the same key at different time stamps and retrieve the key's value at a certain timestamp.

Implement the TimeMap class:

  • TimeMap() Initializes the object of the data structure.
  • void set(String key, String value, int timestamp) Stores the key key with the value value at the given time timestamp.
  • String get(String key, int timestamp) Returns a value such that set was called previously, with timestamp_prev <= timestamp. If there are multiple such values, it returns the value associated with the largest timestamp_prev. If there are no values, it returns "".

Example 1:

<pre>Input ["TimeMap", "set", "get", "get", "set", "get", "get"] [[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]] Output [null, null, "bar", "bar", null, "bar2", "bar2"]

Explanation TimeMap timeMap = new TimeMap(); timeMap.set("foo", "bar", 1); // store the key "foo" and value "bar" along with timestamp = 1. timeMap.get("foo", 1); // return "bar" timeMap.get("foo", 3); // return "bar", since there is no value corresponding to foo at timestamp 3 and timestamp 2, then the only value is at timestamp 1 is "bar". timeMap.set("foo", "bar2", 4); // store the key "foo" and value "bar2" along with timestamp = 4. timeMap.get("foo", 4); // return "bar2" timeMap.get("foo", 5); // return "bar2" </pre>

Constraints:

  • 1 <= key.length, value.length <= 100
  • key and value consist of lowercase English letters and digits.
  • 1 <= timestamp <= 10<sup>7</sup>
  • All the timestamps timestamp of set are strictly increasing.
  • At most 2 * 10<sup>5</sup> calls will be made to set and get.

</div>

题目大意:

实现带历史记录的HashMap。也就是同一个key记录所有赋过值的value

解题思路:

N/A

解题步骤:

N/A

注意事项:

  1. Map to list的思路,list含两个,包括value和timestamp,用binary search搜索timestamp的下标,然后返回对应的value

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class TimeMap(TestCases):

def __init__(self):
self.key_to_val = collections.defaultdict(list)
self.key_to_timestamp = collections.defaultdict(list)

def set(self, key: str, value: str, timestamp: int) -> None:
self.key_to_val[key].append(value)
self.key_to_timestamp[key].append(timestamp)

def get(self, key: str, timestamp: int) -> str:
index = bisect.bisect(self.key_to_timestamp[key], timestamp) - 1
if index == -1:
return ''
else:
return self.key_to_val[key][index]

算法分析:

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

Free mock interview