from functools import cmp_to_key
from functools import reduce
class Solution:
def largestNumber(self, nums: List[int]) -> str:
# a + b > b + a means (a+b) is larger than (b+a) in dictionary order
dictionaryOrder = cmp_to_key(lambda a, b : 1 if int(a + b) > int(b + a) else -1)
# sort nums in dictionary ascending order
# concat all numbers from the largest to the smallest.
# return 0 if the first digit of final result is '0'
result = reduce(lambda accumulate, n : accumulate + n, \
reversed(sorted(map(str, nums), key = dictionaryOrder)), \
'')
return result if result[0] != '0' else '0'
7/31/2020
[LeetCode] 179. Largest Number
7/30/2020
[LeetCode] 174. Dungeon Game
class Solution:
def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
row = len(dungeon)
col = len(dungeon[0])
@lru_cache(maxsize = None)
def helper(y, x):
if y >= row or x >= col:
return 2 ** 31 - 1
if y == row -1 and x == col - 1:
return max(1, 1 - dungeon[y][x])
# only need to have 1 health point to survive
return max(1, min(helper(y,x+1), helper(y+1, x)) - dungeon[y][x])
return helper(0, 0)
class Solution:
def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
row = len(dungeon)
col = len(dungeon[0])
max_int = 2 ** 31 - 1
dp = [[max_int] * (col+1) for _ in range(row + 1)]
dp[row][col-1] = 1
dp[row-1][col] = 1
for y in reversed(range(row)):
for x in reversed(range(col)):
dp[y][x] = max(1, min(dp[y+1][x], dp[y][x+1]) - dungeon[y][x])
return dp[0][0]
7/29/2020
[LeetCode] 173. Binary Search Tree Iterator
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class BSTIterator:
def __init__(self, root: TreeNode):
def inorder(node):
if node:
yield from inorder(node.right)
yield node.val
yield from inorder(node.left)
self.nodes = list(inorder(root))
def next(self) -> int:
return self.nodes.pop()
def hasNext(self) -> bool:
return self.nodes
# Your BSTIterator object will be instantiated and called as such:
# obj = BSTIterator(root)
# param_1 = obj.next()
# param_2 = obj.hasNext()
A stack based iterative solution
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class BSTIterator:
def __init__(self, root: TreeNode):
self.stack = [root] if root else []
def next(self) -> int:
node = self.stack[-1]
while node.left:
self.stack.append(node.left)
tmp = node.left
node.left = None
node = tmp
# pop the parent node
self.stack.pop()
if node.right:
self.stack.append(node.right)
node.right = None
return node.val
def hasNext(self) -> bool:
return self.stack
# Your BSTIterator object will be instantiated and called as such:
# obj = BSTIterator(root)
# param_1 = obj.next()
# param_2 = obj.hasNext()
Edited on 04/22/2021. Refactor the recursive and iterative solution.
[LeetCode] 172. Factorial Trailing Zeroes
class Solution:
def trailingZeroes(self, n: int) -> int:
return n // 5 + self.trailingZeroes(n // 5) if n > 0 else 0
[LeetCode] 171. Excel Sheet Column Number
from functools import reduce
class Solution:
def titleToNumber(self, s: str) -> int:
return reduce(lambda x, y: x * 26 + ord(y) - ord('A') + 1, s, 0)
[LeetCode] 169. Majority Element
class Solution:
def majorityElement(self, nums: List[int]) -> int:
n = len(nums)
return sorted(nums)[n // 2]
class Solution:
def majorityElement(self, nums: List[int]) -> int:
result = nums[0]
count = 1
for i in range(1, len(nums)):
if nums[i] == result:
count += 1
else:
count -= 1
if count == 0:
result = nums[i]
count = 1
return result
7/28/2020
[LeetCode] 168. Excel Sheet Column Title
class Solution:
def convertToTitle(self, n: int) -> str:
letters = [chr(i) for i in range(ord('A'),ord('Z')+1)]
result = ""
while n > 0:
# use n - 1 because letters is zero-based array
i = (n-1) % 26
result += letters[i]
n = (n-1) // 26
return result[::-1]
[LeetCode] 167. Two Sum II - Input array is sorted
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left, right = 0, len(numbers) - 1
while left < right:
tmp = numbers[left] + numbers[right]
if tmp < target:
left += 1
elif tmp > target:
right -= 1
else:
return [left+1, right+1]
[LeetCode] 166. Fraction to Recurring Decimal
class Solution:
def fractionToDecimal(self, numerator: int, denominator: int) -> str:
negative = (numerator < 0) ^ (denominator < 0)
numerator = abs(numerator)
denominator = abs(denominator)
result = str(numerator // denominator)
if numerator % denominator == 0:
return "-" + result if negative and numerator != 0 else result
numerator = numerator % denominator
numerator *= 10
visited = {}
result += "."
while numerator > 0:
if numerator not in visited:
visited[numerator] = len(result)
else:
prefix = result[:visited[numerator]] + '('
appendix = result[visited[numerator]:] + ')'
result = prefix + appendix
break
if numerator > denominator:
result += str(numerator // denominator)
numerator = numerator % denominator
else:
result += '0'
numerator *= 10
return "-" + result if negative else result
Edited on 03/04/2021. A simpler implementation.
7/27/2020
[LeetCode] 165. Compare Version Numbers
class Solution:
def compareVersion(self, version1: str, version2: str) -> int:
v1 = list(map(int, version1.split('.')))
v2 = list(map(int, version2.split('.')))
def compare(level):
left = v1[level] if level < len(v1) else 0
right = v2[level] if level < len(v2) else 0
if left < right:
return -1
if left > right:
return 1
return 0 if level >= len(v1) and level >= len(v2) else compare(level+1)
return compare(0)
[LeetCode] 164. Maximum Gap
class Solution:
def maximumGap(self, nums: List[int]) -> int:
if len(nums) < 2:
return 0
nums.sort()
result = 0
for i in range(len(nums) - 1):
result = max(result, nums[i+1] - nums[i])
return result
7/26/2020
[LeetCode] 162. Find Peak Element
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1
if len(nums) <= 1:
return 0
if nums[left] > nums[left+1]:
return left
if nums[right] > nums[right-1]:
return right
while left < right:
mid = left + (right - left)
if nums[mid-1] < nums[mid] > nums[mid+1]:
return mid
elif nums[mid-1] < nums[mid] < num[mid+1]:
# peak exists on the end of the ascending sequence
left = mid + 1
else:
# peak exists on the begining of descending sequence
right = mid - 1
return right
[LeetCode] 160. Intersection of Two Linked Lists
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
memo = set()
p1 = headA
p2 = headB
while p1:
memo.add(p1)
p1 = p1.next
while p2:
if p2 in memo:
return p2
p2 = p2.next
return None
[LeetCode] 155. Min Stack
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
def push(self, x: int) -> None:
if len(self.stack) == 0:
self.stack.append({'value': x, 'min': x})
else:
self.stack.append({'value': x, 'min': min(x, self.stack[-1]['min'])})
def pop(self) -> None:
del self.stack[-1]
def top(self) -> int:
return self.stack[-1]['value']
def getMin(self) -> int:
return self.stack[-1]['min']
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()
[LeetCode] 154. Find Minimum in Rotated Sorted Array II
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1
while left < right:
mid = left + (right - left) // 2
if nums[mid] > nums[right]:
left = mid + 1
elif nums[mid] < nums[right]:
right = mid
elif nums[mid] == nums[right]:
while right > mid and nums[mid] == nums[right]:
right -= 1
return nums[right]
[LeetCode] 153. Find Minimum in Rotated Sorted Array
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1
while left < right:
mid = left + (right - left) // 2
if nums[mid] > nums[right]:
left = mid + 1
else:
right = mid
return nums[right]
class Solution:
def findMin(self, nums: List[int]) -> int:
def helper(left, right):
if nums[left] <= nums[right]:
return nums[left]
mid = left + (right - left) // 2
return min(helper(left, mid), helper(mid+1, right))
return helper(0, len(nums) - 1)
7/19/2020
[LeetCode] 152. Maximum Product Subarray
class Solution:
def maxProduct(self, nums: List[int]) -> int:
result = nums[0]
for i in range(len(nums)):
tmp = nums[i]
result = max(result, tmp)
for j in range(i+1, len(nums)):
tmp *= nums[j]
result = max(result, tmp)
return result
class Solution {
public int maxProduct(int[] nums) {
int miSoFar = nums[0];
int mxSoFar = nums[0];
int result = nums[0];
for (int i = 1; i < nums.length; i++) {
int preMiSoFar = miSoFar;
miSoFar = Math.min(mxSoFar * nums[i], Math.min(miSoFar * nums[i], nums[i]));
mxSoFar = Math.max(preMiSoFar * nums[i], Math.max(mxSoFar * nums[i], nums[i]));
result = Math.max(result, mxSoFar);
}
return result;
}
}
Edited on 12/02/2021. Update the DP solution
[LeetCode] 151. Reverse Words in a String
class Solution:
def reverseWords(self, s: str) -> str:
return ' '.join(s.split()[::-1])
[LeetCode] 150. Evaluate Reverse Polish Notation
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
operators = { '+' : lambda a, b: a + b, \
'-' : lambda a, b: a - b, \
'*' : lambda a, b: a * b, \
'/' : lambda a, b: int(a/b) }
for t in tokens:
opt = operators.get(t)
if opt:
b = stack.pop()
a = stack.pop()
c = opt(a, b)
stack.append(c)
else:
stack.append(int(t))
return stack.pop()
Edited on 05/25/2021. Simplify condition checks.
[LeetCode] 149. Max Points on a Line
class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
@lru_cache(maxsize = None)
def gcd(a, b):
return a if b == 0 else gcd(b, a % b)
result = 0
for i in range(len(points)):
# init duplicated = 1 for counting the point itself
duplicated = 1
# count of pointer with same slop
slops = defaultdict(int)
for j in range(i+1, len(points)):
if points[i][0] == points[j][0] and points[i][1] == points[j][1]:
duplicated += 1
else:
dx = points[j][0] - points[i][0]
dy = points[j][1] - points[i][1]
d = gcd(dx, dy)
slops[(dx/d, dy/d)] += 1
result = max(result, duplicated)
for _, count in slops.items():
result = max(result, count + duplicated)
return result
[LeetCode] 148. Sort List
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def sortList(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
# divide into 2 parts
fast, slow = head, head
while fast and fast.next:
fast = fast.next.next
if fast:
slow = slow.next
tmp = slow.next
slow.next = None
left = self.sortList(head)
right = self.sortList(tmp)
return self.merge(left, right)
def merge(self, left, right):
dummy = ListNode(0)
p = dummy
while left and right:
if left.val <= right.val:
p.next = left
p = p.next
left = left.next
else:
p.next = right
p = p.next
right = right.next
while left:
p.next = left
p = p.next
left = left.next
while right:
p.next = right
p = p.next
right = right.next
p.next = None
return dummy.next
[LeetCode] 147. Insertion Sort List
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode insertionSortList(ListNode head) {
ListNode dummy = new ListNode();
ListNode p = head;
ListNode pre;
ListNode cur;
while (p != null) {
pre = dummy;
cur = dummy.next;
while (cur != null && cur.val <= p.val) {
pre = cur;
cur = cur.next;
}
ListNode tmp = p.next;
pre.next = p;
p.next = cur;
p = tmp;
}
return dummy.next;
}
}
Edited on 12/14/2021. Replace with Java implementation.
[LeetCode] 146. LRU Cache
class Node:
def __init__(self, key):
self.pre = None
self.next = None
self.key = key
class LRUCache:
def __init__(self, capacity: int):
self.memo = {}
self.queue = Node(-1)
self.tail = self.queue
self.capacity = capacity
def get(self, key: int) -> int:
if key in self.memo:
val, node = self.memo[key]
self._remove(node)
self._append(node)
return val
return -1
def put(self, key: int, value: int) -> None:
if key in self.memo:
_, node = self.memo[key]
self.memo[key] = (value, node)
self._remove(node)
self._append(node)
elif len(self.memo) < self.capacity:
node = Node(key)
self.memo[key] = (value, node)
self._append(node)
else:
removed = self._popleft()
del self.memo[removed.key]
node = Node(key)
self.memo[key] = (value, node)
self._append(node)
def _remove(self, node):
if node == self.tail:
self.tail = node.pre
pre_node = node.pre
next_node = node.next
pre_node.next = next_node
if next_node:
next_node.pre = pre_node
def _append(self, node):
self.tail.next = node
node.pre = self.tail
node.next = None
self.tail = node
def _popleft(self):
node = self.queue.next
if node:
self.queue.next = node.next
if node.next:
node.next.pre = self.queue
if self.tail == node:
self.tail = node.pre
return node
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
[LeetCode] 145. Binary Tree Postorder Traversal
Recursive solution:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
def postorder(node):
if node:
yield from postorder(node.left)
yield from postorder(node.right)
yield node.val
return list(postorder(root))
Iterative solution:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
result = []
stack = [root] if root else []
while stack:
node = stack[-1]
if not node.left and not node.right:
result.append(node.val)
stack.pop()
else:
if node.right:
stack.append(node.right)
node.right = None
if node.left:
stack.append(node.left)
node.left = None
return result
Edited on 04/20/2021. Add the recursive approach.
Edited on 04/20/2021. Update the stack based iterative approach.
[LeetCode] 144. Binary Tree Preorder Traversal
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
def preorder(node):
if node:
yield node.val
yield from preorder(node.left)
yield from preorder(node.right)
return list(preorder(root))
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
result = []
stack = [root] if root else []
while stack:
node = stack.pop()
result.append(node.val)
for child in filter(None, [node.right, node.left]):
stack.append(child)
return result
Edited on 04/20/2021. Update iterative solution. Same approach can apply to N-array tree too.
Edited on 04/20/2021. Update recursive solution. Use 'yield' and 'yield from'.
[LeetCode] 143. Reorder List
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
self.dummy = ListNode()
self.p = self.dummy
self.preorder = head
self.seen = set()
def postorder(node):
if node:
postorder(node.next)
# add node by preorder traversal
if self.preorder not in self.seen:
self.seen.add(self.preorder)
self.p.next = self.preorder
self.preorder = self.preorder.next
self.p = self.p.next
# add node by postorder traversal
if node not in self.seen:
self.seen.add(node)
self.p.next = node
self.p = self.p.next
postorder(head)
# end the list
self.p.next = None
A stack based solution:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
if not head: return
stack = []
p = head
total = 0
while p:
stack.append(p)
p = p.next
total += 1
total = total // 2
p = head
while total > 0:
tmp = p.next
p.next = stack.pop()
total -= 1
p = p.next
p.next = tmp
p = p.next
p.next = None
An iterative solution:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public void reorderList(ListNode head) {
ListNode p1 = head; // slow
ListNode p2 = head; // fast
// find the middle node
while (p2.next != null && p2.next.next != null) {
p1 = p1.next;
p2 = p2.next.next;
}
ListNode mid = p1.next;
p1.next = null;
// return directly if the second half list is empty
if (mid == null) {
return;
}
// reverse the second half list
ListNode pre = null;
ListNode cur = mid;
while (cur.next != null) {
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
cur.next = pre;
// reorder the list
p1 = head;
p2 = cur;
ListNode dummy = new ListNode();
ListNode p = dummy;
while (p1 != null || p2 != null) {
if (p1 != null) {
p.next = p1;
p1 = p1.next;
p = p.next;
}
if (p2 != null) {
p.next = p2;
p2 = p2.next;
p = p.next;
}
}
}
}
Edited on 12/21/2021. Add the iterative solution.
[LeetCode] 142. Linked List Cycle II
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
memo = set()
while head:
if head in memo:
return head
memo.add(head)
head = head.next
return head
Fast and slow pointer solution:
Space complexity = O ( 1 )
Firstly, pointer P1 moves 2 times faster than P2. The linked list has cycle if P1 meets P2
Assume P1 and P2 meet at node P. Because P1 moves 2 times faster P2, A + B + C + B = 2 * (A + B)
So A = C.
Secondly, move pointer P1 back to the head of linked list. Then move P1 and P2 with same speed. P1 and P2 must meet again at node Q which is the node where the cycle starts.
Q P --A---|---B---| | | | | | | |---C---|
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
p1 = p2 = head
while p1 and p1.next and p2:
p1 = p1.next.next
p2 = p2.next
if p1 == p2:
# find the cycle
p1 = head
while p1 != p2:
p1 = p1.next
p2 = p2.next
return p1
return None
[LeetCode] 141. Linked List Cycle
Hash set based solution:
Time Complexity = O( N )
Space Complexity = O( N )
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
p = head
memo = set()
while p:
if p in memo:
return True
else:
memo.add(p)
p = p.next
return False
Fast and slow pointer solution:
Iterate the list with 'fast' and 'slow' pointers. If 'fast' pointer encounter 'slow' pointer, it means there is a cycle in the linked list.
Time Complexity = O ( N )
Space Complexity = O ( 1 )
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
fast, slow = head, head
while fast and fast.next:
if fast.next == slow:
return True
# 'fast' moves forward 2 steps
fast = fast.next.next
# 'slow' moves forward 1 step
slow = slow.next
return False
[LeetCode] 140. Word Break II
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
wordDictSet = set(wordDict)
result = []
@lru_cache(maxsize = None)
def backtracking(start):
if start == len(s):
return [[]]
result = []
for i in range(start, len(s)):
tmp = s[start:i+1]
if tmp in wordDictSet:
partials = backtracking(i+1)
for p in partials:
result.append([tmp] + p)
return result
result = backtracking(0)
return map(lambda x : " ".join(x), result)
7/18/2020
[LeetCode] 139. Word Break
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
if not wordDict: return False
wordSet = set(wordDict)
maxLen = max([len(w) for w in wordDict])
@lru_cache(maxsize=None)
def dfs(start):
if start >= len(s):
return True
for i in range(start, min(start+maxLen, len(s))):
if s[start:i+1] in wordSet and dfs(i+1):
return True
return False
return dfs(0)
BFS solution:
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
if not wordDict: return False
wordSet = set(wordDict)
maxLen = max([len(w) for w in wordDict])
queue = deque([0])
visited = set([0])
while queue:
start = queue.popleft()
for i in range(start, min(start+maxLen, len(s))):
if s[start:i+1] in wordSet:
if i + 1 == len(s):
return True
if i + 1 not in visited:
visited.add(i+1)
queue.append(i+1)
return False
The bottom-up solution:
class Solution {
public boolean wordBreak(String s, List wordDict) {
HashSet<String> words = new HashSet();
int wordLen = 0;
for (int i = 0; i < wordDict.size(); i++) {
words.add(wordDict.get(i));
wordLen = Math.max(wordLen, wordDict.get(i).length());
}
int N = s.length();
boolean[] dp = new boolean[N];
for (int i = N-1; i >= 0; i--) {
for (int j = i; i - j + 1 <= wordLen && j >= 0; j--) {
if (words.contains(s.substring(j, i+1)) && (i + 1 == N || dp[i+1]) ) {
dp[j] = true;
}
}
}
return dp[0];
}
}
Edited on 11/26/2021. Add the bottom-up solution.
[LeetCode] 138. Copy List with Random Pointer
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
copies = {}
def copy(node):
if not node:
return None
copied = None
if node in copies:
return copies[node]
copied = Node(node.val)
copies[node] = copied
copied.next = copy(node.next)
copied.random = copy(node.random)
return copied
return copy(head)
"""
# Definition for a Node.
class Node:
def __init__(self, x, next=None, random=None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution(object):
def copyRandomList(self, head):
"""
:type head: Node
:rtype: Node
"""
copies = dict()
dummy = Node(0)
p1 = dummy
p2 = head
while p2:
p1.next = Node(p2.val)
copies[p2] = p1.next
p1 = p1.next
p2 = p2.next
p1 = dummy.next
p2 = head
while p1:
if p2.random:
p1.random = copies[p2.random]
p1 = p1.next
p2 = p2.next
return dummy.next
[LeetCode] 137. Single Number II
class Solution:
def singleNumber(self, nums: List[int]) -> int:
for n, count in Counter(nums).items():
if count == 1:
return n
import ctypes
class Solution:
def singleNumber(self, nums: List[int]) -> int:
result = 0
# calculate sum of bit on each position
for shift in range(32):
sum_of_bit = 0
for n in nums:
mask = 1 << shift
if n & mask != 0:
sum_of_bit += 1
# sum_of_bit = 0, if there is no bit from the unique number
# on this position
sum_of_bit = sum_of_bit % 3
result = result | sum_of_bit << shift
# convert to integer
return ctypes.c_int(result).value
[LeetCode] 136. Single Number
from operator import xor
class Solution:
def singleNumber(self, nums: List[int]) -> int:
return reduce(xor, nums)
[LeetCode] 135. Candy
class Solution:
def candy(self, ratings: List[int]) -> int:
candies = [1] * len(ratings)
for i in range(len(ratings) - 1):
if ratings[i+1] > ratings[i]:
candies[i+1] = candies[i] + 1
for i in reversed(range(1, len(ratings))):
if ratings[i-1] > ratings[i]:
candies[i-1] = max(candies[i-1], candies[i] + 1)
return sum(candies)
7/17/2020
[LeetCode] 134. Gas Station
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
def dfs(start, current, remain):
if remain < 0:
return False
if start == current:
return True
return dfs(start, (current + 1) % len(gas), remain + gas[current] - cost[current])
for i in range(len(gas)):
if dfs(i, (i + 1) % len(gas), gas[i] - cost[i]):
return i
return -1
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
# to finish a circle, gained gas must larger or equal to used gas
total = 0
# at each pos, remained gas = last remained gas + gained gas - used gas
# if remained gas < 0, it means from start pos to current pos,
# none of them can be used as start point.
# because the initial remained gas of the start position = 0
remain = 0
start = 0
for i in range(len(gas)):
total += gas[i] - cost[i]
remain += gas[i] - cost[i]
if remain < 0:
start = i + 1
remain = 0
return start if total >= 0 else -1
[LeetCode] 133. Clone Graph
"""
# Definition for a Node.
class Node:
def __init__(self, val = 0, neighbors = None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
"""
class Solution:
def cloneGraph(self, node: 'Node') -> 'Node':
copies = {}
def copy(node):
if not node:
return None
if node.val in copies:
return copies[node.val]
copied = Node(node.val)
copies[node.val] = copied
for n in node.neighbors:
copied.neighbors.append(copy(n))
return copied
return copy(node)
[LeetCode] 132. Palindrome Partitioning II
class Solution:
def minCut(self, s: str) -> int:
if not s:
return 0
# isPalindrome[i][j] = True if s[i:j+1] is palindrome string
isPalindrome = [[False] * len(s) for _ in range(len(s))]
for j in range(len(s)):
for i in range(j+1):
if s[i] == s[j] and (j - i <= 2 or isPalindrome[i+1][j-1] == True):
isPalindrome[i][j] = True
# minCut[i] = min cut of s[0:i+1]
minCut = [i for i in range(len(s))]
for i in range(len(s)):
for j in range(i+1):
if isPalindrome[j][i]:
if j == 0:
# s[0:i+1] is already a palindrome
# no need to cut
minCut[i] = 0
break
minCut[i] = min(minCut[i], minCut[j-1] + 1)
return minCut[-1]
The top-down memorization + backtracking solution:
class Solution:
def minCut(self, s: str) -> int:
@cache
def isPalindrome(left, right):
if left >= right:
return True
if right - left == 1:
return True
if s[left] == s[right-1] and isPalindrome(left+1, right-1):
return True
return False
@cache
def helper(left, right):
if isPalindrome(left, right):
# s[left:right] is already a palindrome. no need to cut.
return 0
result = right - left - 1
for mid in range(left+1, right):
if isPalindrome(left, mid):
# s[left:mid] is palindrome, we can try to cut it at 'mid' and calculate the least cut of rest of string
result = min(result, 1 + helper(mid, right))
return result
return helper(0, len(s))
Edited on 08/07/2021. Add the top-down memorization + backtracking solution.
7/13/2020
[LeetCode] 131. Palindrome Partitioning
class Solution:
def partition(self, s: str) -> List[List[str]]:
result = []
@lru_cache(maxsize = None)
def isPalindrome(start, end):
while start < end:
if s[start] != s[end]:
return False
start += 1
end -= 1
return True
def helper(start, partial):
if start == len(s):
result.append(partial)
return
for i in range(start, len(s)):
if isPalindrome(start, i):
helper(i+1, partial + [s[start:i+1]])
helper(0, [])
return result
class Solution {
int N;
boolean[][] palindrome;
Map<Integer, List<List<String>>> memo = new HashMap<>();
public List<List<String>> partition(String s) {
N = s.length();
palindrome = new boolean[N][N];
for (int left = N-1; left >= 0; left--) {
for (int right = N-1; right >= left; right--) {
palindrome[left][right] = s.charAt(left) == s.charAt(right) && (right - left <= 1 || palindrome[left+1][right-1]);
}
}
return helper(s, 0);
}
List<List<String>> helper(String s, int start) {
if (memo.containsKey(start)) {
return memo.get(start);
}
List<List<String>> result = new ArrayList<>();
if (start >= N) {
List<String> tmp = new ArrayList<>();
result.add(tmp);
return result;
}
for (int i = start; i < N; i++) {
if (palindrome[start][i]) {
for (List<String> suffix : helper(s, i+1)) {
List<String> tmp = new ArrayList<>();
tmp.add(s.substring(start, i+1));
tmp.addAll(suffix);
result.add(tmp);
}
}
}
memo.put(start, result);
return result;
}
}
class Solution {
public List<List<String>> partition(String s) {
int N = s.length();
boolean[][] palindrome = new boolean[N][N];
// memo[i] = palindrome partitions of s[i:]
List<List<List<String>>> memo = new ArrayList<>(N+1);
for (int i = 0; i < N; i++) {
memo.add(new ArrayList<List<String>>());
}
// Add an empty partition list for position 'N'
// memo[N] = [[]]
memo.add(Arrays.asList(Collections.emptyList()));
for (int left = N-1; left >= 0; left--) {
for (int right = N-1; right >= left; right--) {
palindrome[left][right] = s.charAt(left) == s.charAt(right) && (right - left <= 1 || palindrome[left+1][right-1]);
if (palindrome[left][right]) {
// s[left:right+1] is a palindrome
// use it to expand the palindrome partitions of s[right+1:]
for (List<String> suffix : memo.get(right+1)) {
List<String> tmp = new ArrayList<>();
tmp.add(s.substring(left, right+1));
tmp.addAll(suffix);
memo.get(left).add(tmp);
}
}
}
}
// return palindrome partitions of s[0:]
return memo.get(0);
}
}
Edited on 04/13/2021. Add DP solution.
Edited on 01/04/2022. Refactor the DP + DFS solution.
Edited on 01/04/2022. Refactor the DP solution.
7/11/2020
[LeetCode] 130. Surrounded Regions
class Solution:
def solve(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
if not board:
return
row = len(board)
column = len(board[0])
def dfs(y, x):
if board[y][x] == 'X' or board[y][x] == '#':
return
board[y][x] = '#'
for dy in (-1, 1):
if 0 <= y + dy < row:
dfs(y + dy, x)
for dx in (-1, 1):
if 0 <= x + dx < column:
dfs(y, x + dx)
for i in range(row):
for j in (0, column - 1):
dfs(i, j)
for i in (0, row -1):
for j in range(column):
dfs(i, j)
for i in range(row):
for j in range(column):
if board[i][j] == '#':
board[i][j] = 'O'
else:
board[i][j] = 'X'
[LeetCode] 129. Sum Root to Leaf Numbers
A rescurisve approach:
class Solution {
public int sumNumbers(TreeNode root) {
return dfs(root, 0);
}
int dfs(TreeNode node, int tmp) {
tmp = tmp * 10 + node.val;
if (node.left == null && node.right == null) {
return tmp;
}
int result = 0;
result += (node.left != null) ? dfs(node.left, tmp) : 0;
result += (node.right != null) ? dfs(node.right, tmp) : 0;
return result;
}
}
An iterative approach:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sumNumbers(self, root: TreeNode) -> int:
result = 0
stack = [(0, root)]
while stack:
num, node = stack.pop()
num = num * 10 + node.val
if not node.left and not node.right:
result += num
else:
if node.right:
stack.append((num, node.right))
if node.left:
stack.append((num, node.left))
return result
Edited on 03/13/2023. Replace the recursive solution with a Java based solution.
Edited on 04/24/2021. Refactor the recursive solution.
Edited on 04/24/2021. Add the iterative solution.
[LeetCode] 128. Longest Consecutive Sequence
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
memo = set(nums)
result = 0
for n in nums:
if n - 1 not in memo:
tmp = 1
while n + 1 in memo:
tmp += 1
n += 1
result = max(result, tmp)
return result
Union-Find based solution:
Put the contiguous numbers in one group. Then find the maximum number of items in one group.
class UnionFind:
def __init__(self, nums):
self.memo = {}
for n in nums:
self.memo[n] = n
def union(self, x, y):
parent = y
while parent != self.memo[parent]:
tmp = self.memo[parent]
self.memo[parent] = self.memo[x]
parent = tmp
self.memo[y] = self.memo[x]
def find(self, x):
if x not in self.memo:
return None
parent = self.memo[x]
while self.memo[parent] != parent:
parent = self.memo[parent]
while self.memo[x] != parent:
tmp = self.memo[x]
self.memo[x] = parent
x = tmp
return parent
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
uf = UnionFind(nums)
for n in nums:
if uf.find(n-1) != None:
uf.union(n-1, n)
if uf.find(n+1) != None:
uf.union(n, n+1)
maxLength = defaultdict(int)
# count number of items in each group
for k in uf.memo.keys():
p = uf.find(k)
maxLength[p] += 1
return max(maxLength.values()) if maxLength.values() else 0
7/04/2020
[LeetCode] 127. Word Ladder
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
INT_MAX = 2 ** 31 - 1
N = len(beginWord)
wordDict = {word: INT_MAX for word in wordList}
intermediates = defaultdict(list)
if endWord not in wordDict: return 0
for word in wordList:
for i in range(N):
wordMask = word[:i] + '*' + word[i+1:]
intermediates[wordMask].append(word)
queue = deque([beginWord])
step = 1
while queue:
for _ in range(len(queue)):
word = queue.popleft()
for i in range(N):
wordMask = word[:i] + '*' + word[i+1:]
for newWord in intermediates[wordMask]:
if newWord == endWord:
return step + 1
if newWord == word or wordDict[newWord] < step + 1:
continue
wordDict[newWord] = step + 1
queue.append(newWord)
step += 1
return 0
Bidirectional BFS
Time complexity = O ( N * W * N + N * W * N ) = O ( W * N ** 2), N = length of each word, W = total number of word.
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
INT_MAX = 2 ** 31 - 1
N = len(beginWord)
wordDict = {word: INT_MAX for word in wordList}
intermediates = defaultdict(list)
if endWord not in wordDict: return 0
for word in wordList:
for i in range(N):
wordMask = word[:i] + '*' + word[i+1:]
intermediates[wordMask].append(word)
queue = set([beginWord])
opposite = set([endWord])
step = 1
while queue:
tmp = []
for word in queue:
for i in range(N):
wordMask = word[:i] + '*' + word[i+1:]
for newWord in intermediates[wordMask]:
if newWord in opposite:
return step + 1
if newWord == word or wordDict[newWord] < step + 1:
# word has been visited
continue
wordDict[newWord] = step + 1
tmp.append(newWord)
queue = set(tmp) if len(tmp) < len(opposite) else opposite
opposite = opposite if len(tmp) < len(opposite) else set(tmp)
step += 1
return 0
[LeetCode] 126. Word Ladder II
class Solution:
def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
# impossible to transform if endWord is not in wordList.
if endWord not in wordList: return []
# construct word mask map
wordMap = defaultdict(list)
for word in wordList:
for i in range(len(word)):
mask = word[:i] + "#" + word[i+1:]
wordMap[mask].append(word)
# save the previous word was used to transform to current word
preWord = defaultdict(list)
# save the minimum step to transform to current word
visited = defaultdict(lambda : 2 ** 31 - 1)
visited[beginWord] = 0
# use bfs to find all possible transformation paths
step = 1
queue = deque([beginWord])
while queue:
found = False
for _ in range(len(queue)):
word = queue.popleft()
if word == endWord:
found = True
for i in range(len(word)):
mask = word[:i] + "#" + word[i+1:]
for nxtWord in wordMap[mask]:
if nxtWord == word:
# skip the current word
continue
if visited[nxtWord] < step:
# already find the shortest path to nxtWord
continue
if visited[nxtWord] != step:
# find the shortest path to nxtWord
visited[nxtWord] = step
# add nxtWord to queue if this is the first time we reach to it
queue.append(nxtWord)
# save the previous word of nxtWord
preWord[nxtWord].append(word)
step += 1
if found:
break
# there is no way to reach to endWord
if not preWord[endWord]:
return []
# use dfs to construct the transformation path
result = []
stack = []
stack.append((endWord, [endWord]))
while stack:
word, tmp = stack.pop()
if word == beginWord:
result.append(tmp[::-1])
else:
for pre in preWord[word]:
stack.append((pre, tmp + [pre]))
return result
Edited on 06/19/2021. Optimize performance.
Edited on 07/24/2021. Optimize performance.
[LeetCode] 125. Valid Palindrome
class Solution:
def isPalindrome(self, s: str) -> bool:
i, j = 0, len(s) - 1
while i < j:
while i < j and not s[i].isalnum():
i += 1
while i < j and not s[j].isalnum():
j -= 1
if i < j and s[i].lower() != s[j].lower():
break
i += 1
j -= 1
return i >= j
[LeetCode] 124. Binary Tree Maximum Path Sum
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxPathSum(self, root: TreeNode) -> int:
self.result = -1000
def postOrder(node):
if node.left and node.right:
left = postOrder(node.left)
right = postOrder(node.right)
self.result = max(self.result, node.val + left + right, node.val + left, node.val + right, node.val)
return max(node.val + left, node.val + right, node.val)
if node.left:
left = postOrder(node.left)
self.result = max(self.result, node.val + left, node.val)
return max(node.val + left, node.val)
if node.right:
right = postOrder(node.right)
self.result = max(self.result, node.val + right, node.val)
return max(node.val + right, node.val)
self.result = max(self.result, node.val)
return node.val
if root:
postOrder(root)
return self.result
[LeetCode] 123. Best Time to Buy and Sell Stock III
class Solution:
def maxProfit(self, prices: List[int]) -> int:
N = len(prices)
left = [0] * N
mi = prices[0]
for i in range(1, N):
left[i] = max(left[i-1], prices[i] - mi)
mi = min(mi, prices[i])
right = [0] * N
mx = prices[N-1]
for i in reversed(range(N-1)):
right[i] = max(right[i+1], mx - prices[i])
mx = max(mx, prices[i])
result =0
for i in range(N):
if i == 0:
result = max(result, right[i])
elif i == N-1:
result = max(result, left[i])
else:
result = max(result, left[i-1] + right[i])
return result
Edited on 10/15/2021. Fix the logic error in previous code.