451. Sort Characters By Frequency (Medium)
Given a string s, sort it in decreasing order based on the frequency of the characters. The frequency of a character is the number of times it appears in the string.
Return the sorted string. If there are multiple answers, return any of them
class Solution:
def frequencySort(self, s: str) -> str:
d=Counter(s)
res=''
for k in sorted(d,key=lambda x: d[x],reverse=True):
res+=k*d[k]
return res
#O(n) bucket sort
class Solution:
def frequencySort(self, s: str) -> str:
if not s: return s
counts=collections.Counter(s)
max_freq=max(counts.values())
buckets=[[] for _ in range(max_freq+1)]
for c,i in counts.items():
buckets[i].append(c)
string_builder=[]
for i in range(len(buckets)-1,0,-1):
for c in buckets[i]:
string_builder.append(c*i)
return ''.join(string_builder)
答案的On解法好。bucketsort
452. Minimum Number of Arrows to Burst Balloons (Medium)
There are some spherical balloons taped onto a flat wall that represents the XY-plane. The balloons are represented as a 2D integer array points where points[i] = [xstart, xend] denotes a balloon whose horizontal diameter stretches between xstart and xend. You do not know the exact y-coordinates of the balloons.
Arrows can be shot up directly vertically (in the positive y-direction) from different points along the x-axis. A balloon with xstart and xend is burst by an arrow shot at x if xstart <= x <= xend. There is no limit to the number of arrows that can be shot. A shot arrow keeps traveling up infinitely, bursting any balloons in its path.
Given the array points, return the minimum number of arrows that must be shot to burst all balloons.
class Solution:
def findMinArrowShots(self, points: List[List[int]]) -> int:
#merge range by min range calculate len
points.sort()
res = []
for a,b in points:
# a b
# res a' b'
if res and a<=res[-1][1] :
res[-1][1] = min(res[-1][1],b)
else:
res.append([a,b])
return len(res)
#ANSWER WAY OF WRITTING
class Solution:
def findMinArrowShots(self, points: List[List[int]]) -> int:
if not points:
return 0
# sort by x_end
points.sort(key = lambda x : x[1])
arrows = 1
first_end = points[0][1]
for x_start, x_end in points:
# if the current balloon starts after the end of another one,
# one needs one more arrow
if first_end < x_start:
arrows += 1
first_end = x_end
return arrows
我的思路是找到相交的区间,看总共多少个就行。。。答案思路是track球右边位置,让碰到新气球左边大于cur end位置+1,更新end位置的greedy算法。
453. Minimum Moves to Equal Array Elements (Medium)
Given an integer array nums of size n, return the minimum number of moves required to make all array elements equal.
In one move, you can increment n - 1 elements of the array by 1.
#TLE naive solution
class Solution:
def minMoves(self, nums: List[int]) -> int:
c=0
while len(set(nums))!=1:
ind = nums.index(max(nums))
for i in range(len(nums)):
if i!=ind:
nums[i]+=1
#print(nums)
c+=1
return c
#ANSWER
class Solution:
def minMoves(self, nums: List[int]) -> int:
nums.sort()
c=0
for i in range(len(nums)-1,0,-1):
c+=nums[i]-nums[0]
return c
#ANSWER
class Solution:
def minMoves(self, nums: List[int]) -> int:
moves=0
min_=float('inf')
for i in range(len(nums)):
moves+=nums[i]
min_=min(min_,nums[i])
return moves-min_*len(nums)
#MY ANSWER
class Solution:
# 除了一个位置,其余位置同时加1。 等效于这个位置减去1。
def minMoves(self, nums: List[int]) -> int:
me=min(nums)
res = 0
for n in nums:
res+= n-me
return res
答案很巧妙,如果是答案1) 思路先sort,那么每次需要move是nums【i】-nums【0】,这样move后nums【i】和nums【0】相等,然后其余数字都加上了move次,再次move时候,求nums【i-1】-nums【0】,这样nums【0】就和nums【i-1】相等,自然也和nums【i】相等。 以此类推,可求出总共move数目。 如果是答案2)所有加1除了1个不加,和只有一个减去1是等效的。 所以就是减多少次能使所有数平衡,sum(all)-min(all)乘n。
454. 4Sum II (Medium)
Given four integer arrays nums1, nums2, nums3, and nums4 all of length n, return the number of tuples (i, j, k, l) such that:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
dic2sum=collections.defaultdict(int)
for i in nums1:
for j in nums2:
dic2sum[i+j]+=1
res=0
for k in nums3:
for l in nums4:
if -k-l in dic2sum:
res+=dic2sum[-k-l]
return res
#
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
d1 = collections.defaultdict(int)
d2 = collections.defaultdict(int)
for a in nums1:
for b in nums2:
d1[a+b]+=1
for c in nums3:
for d in nums4:
d2[c+d]+=1
res = 0
for k1,v1 in d1.items():
for k2,v2 in d2.items():
if k1+k2==0:
res+=v1*v2
return res
#ANSWER GENERALIZED
class Solution:
def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
m = collections.defaultdict(int)
lists = [A, B, C, D]
def nSumCount() -> int:
addToHash(0, 0)
return countComplements(len(lists) // 2, 0)
def addToHash(i: int, total: int) -> None:
if i == len(lists) // 2:
m[total] = m[total] + 1
else:
for a in lists[i]:
addToHash(i + 1, total + a)
def countComplements(i: int, complement: int) -> int:
if i == len(lists):
return m[complement]
cnt = 0
for a in lists[i]:
cnt += countComplements(i + 1, complement - a)
return cnt
return nSumCount()
455. Assign Cookies (Easy)
Assume you are an awesome parent and want to give your children some cookies. But, you should give each child at most one cookie.
Each child i has a greed factor g[i], which is the minimum size of a cookie that the child will be content with; and each cookie j has a size s[j]. If s[j] >= g[i], we can assign the cookie j to the child i, and the child i will be content. Your goal is to maximize the number of your content children and output the maximum number.
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
g.sort()
s.sort()
res = 0
while g:
need = g.pop(0)
while s and s[0]<need:
s.pop(0)
if s:
s.pop(0)
res+=1
return res
#
# 先对g, s两个数组进行排序
# 贪心算法
# 贪心思想1 优先满足需求因子较小的孩子。因为如果较小需求的孩子无法被满足,则之后的较大的需求更不可能能被满足了。
#贪心思想2 尽量用较小的糖果去优先满足孩子。
class Solution:
def findContentChildren(self, g, s):
"""
:type g: List[int]
:type s: List[int]
:rtype: int
"""
g.sort() # 对需求因子进行排序,从小到大
s.sort() # 对糖果数组进行排序,从小到大
child = 0 # 记录可以被满足孩子数
cookie = 0 # 记录可以满足的糖果数
while child <len(g) and cookie < len(s):
if g[child] <= s[cookie]:
child += 1
cookie += 1
return child
456. 132 Pattern (Medium)
Given an array of n integers nums, a 132 pattern is a subsequence of three integers nums[i], nums[j] and nums[k] such that i < j < k and nums[i] < nums[k] < nums[j].
Return true if there is a 132 pattern in nums, otherwise, return false.
class Solution:
def find132pattern(self, nums: List[int]) -> bool:
if len(nums) < 3:
return False
stack=[]
min_array = [-1] * len(nums)
min_array[0] = nums[0]
for i in range(1, len(nums)):
min_array[i] = min(min_array[i - 1], nums[i])
for j in range(len(nums) - 1, -1, -1):
if nums[j] <= min_array[j]:
continue
while stack and stack[-1] <= min_array[j]:
stack.pop()
#到此 确保了stack[-1] 大于 min_array[j],只需要确保下一步了
if stack and stack[-1] < nums[j]:
return True
stack.append(nums[j])
return False
估计是用stack但是。。。看答案思路:从前往后算minarry,从后往前用stack。
457. Circular Array Loop (Medium)
You are playing a game involving a circular array of non-zero integers nums. Each nums[i] denotes the number of indices forward/backward you must move if you are located at index i:
If nums[i] is positive, move nums[i] steps forward, and
If nums[i] is negative, move nums[i] steps backward.
Since the array is circular, you may assume that moving forward from the last element puts you on the first element, and moving backwards from the first element puts you on the last element.
A cycle in the array consists of a sequence of indices seq of length k where:
Following the movement rules above results in the repeating index sequence seq[0] -> seq[1] -> ... -> seq[k - 1] -> seq[0] -> ...
Every nums[seq[j]] is either all positive or all negative.
k > 1
Return true if there is a cycle in nums, or false otherwise.
class Solution:
def circularArrayLoop(self, nums: List[int]) -> bool:
#two pointer
n=len(nums)
#check every start location
for start in range(n):
if nums[start]==0: continue
slow=start
fast=(slow+nums[slow])%n
#if sign of fast and fast.next is the same we do the while loop
while nums[start]*nums[fast]>0 and nums[start] * nums[(fast+nums[fast])%n] > 0:
if slow == fast:
if slow == (slow+nums[slow])%n:
break # 1-element loop
return True
slow = (slow+nums[slow])%n
fast = (fast+nums[fast])%n
fast = (fast+nums[fast])%n
#we are here know start from start have no loop so mark visited as 0
slow = start
sgn = nums[start]
while sgn * nums[slow] > 0:
nxt = (slow+nums[slow])%n
nums[slow] = 0
slow = nxt
return False
#MY ANSWER DFS
class Solution:
def circularArrayLoop(self, nums: List[int]) -> bool:
n = len(nums)
visited = set()
self.res =False
def dfs(i,visited):
if i in visited:
if (i+nums[i])%n!=i :
#no self loop
start = i
sign = [ ]
while (start+nums[start])%n!=i:
sign.append(nums[start]>0)
start = (start+nums[start])%n
sign.append(nums[start]>0)
if len(sign)>1 and (all(sign) or not any(sign)):
self.res = True
return
visited.add(i)
dfs( (i+nums[i])%n, visited)
for i in range(n):
dfs(i,set())
return self.res
#ANSWER PYTHONIC
class Solution:
def circularArrayLoop(self, nums: List[int]) -> bool:
t = {i: (i + v) % len(nums) for i, v in enumerate(nums)}
inc = {k: v for k, v in t.items() if k != v}
while inc:
if any(k == v for k, v in inc.items()): return True
inc = {k: t[v] for k, v in inc.items() if nums[k] * nums[v] > 0 and v in inc}
return False
two pointer, but tricky ....if we meet element with different directions, then the search fail, we set all elements along the way to 0. Because 0 is fail for sure so when later search meet 0 we know the search will fail.
458. Poor Pigs (Hard)
There are buckets buckets of liquid, where exactly one of the buckets is poisonous. To figure out which one is poisonous, you feed some number of (poor) pigs the liquid to see whether they will die or not. Unfortunately, you only have minutesToTest minutes to determine which bucket is poisonous.
You can feed the pigs according to these steps:
Choose some live pigs to feed.
For each pig, choose which buckets to feed it. The pig will consume all the chosen buckets simultaneously and will take no time.
Wait for minutesToDie minutes. You may not feed any other pigs during this time.
After minutesToDie minutes have passed, any pigs that have been fed the poisonous bucket will die, and all others will survive.
Repeat this process until you run out of time.
Given buckets, minutesToDie, and minutesToTest, return the minimum number of pigs needed to figure out which bucket is poisonous within the allotted time.
class Solution:
def poorPigs(self, buckets: int, minutesToDie: int, minutesToTest: int) -> int:
return int(math.ceil(math.log(buckets, 2) / math.log(minutesToTest / minutesToDie + 1, 2)))
#MY ANSWER
class Solution:
def poorPigs(self, buckets: int, minutesToDie: int, minutesToTest: int) -> int:
if buckets == 1: return 0
round = minutesToTest//minutesToDie
# 1pig is 1 bit has 2 state only when round == 1
# 2**pig >= buckets
round = minutesToTest//minutesToDie
states = round+1
l=1
r = buckets
while l<=r:
m = (l+r)//2
if states**m<buckets:
#pig too small
l=m+1
else:
#pig too large
r=m-1
return l
good to knwo but not algorithm..
459. Repeated Substring Pattern (Easy)
Given a string s, check if it can be constructed by taking a substring of it and appending multiple copies of the substring together.
class Solution:
def repeatedSubstringPattern(self, s: str) -> bool:
for i in range(1,len(s)//2+1):
if s[:i]*(len(s)//i)==s:
return True
return False
#ANSWER
def repeatedSubstringPattern(self, str):
"""
:type str: str
:rtype: bool
"""
if not str:
return False
ss = (str + str)[1:-1]
return ss.find(str) != -1
#ANSWER
def repeatedSubstringPattern(self, str):
return s in (s+s)[1:-1]
答案思路很好, If the string S has repeated block, it could be described in terms of pattern.
S = SpSp (For example, S has two repeatable block at most)
If we repeat the string, then SS=SpSpSpSp.
Destroying first and the last pattern by removing each character, we generate a new S2=SxSpSpSy.
460. LFU Cache (Hard)
Design and implement a data structure for a Least Frequently Used (LFU) cache.
Implement the LFUCache class:
LFUCache(int capacity) Initializes the object with the capacity of the data structure.
int get(int key) Gets the value of the key if the key exists in the cache. Otherwise, returns -1.
void put(int key, int value) Update the value of the key if present, or inserts the key if not already present. When the cache reaches its capacity, it should invalidate and remove the least frequently used key before inserting a new item. For this problem, when there is a tie (i.e., two or more keys with the same frequency), the least recently used key would be invalidated.
To determine the least frequently used key, a use counter is maintained for each key in the cache. The key with the smallest use counter is the least frequently used key.
When a key is first inserted into the cache, its use counter is set to 1 (due to the put operation). The use counter for a key in the cache is incremented either a get or put operation is called on it.
The functions get and put must each run in O(1) average time complexity.
class Node:
def __init__(self,key,val):
self.key = key
self.val = val
self.freq = 1
self.pre = None
self.next = None
class DLL:
def __init__(self):
self.head = Node('H','H')
self.tail = Node('T','T')
self.head.next =self.tail
self.tail.pre = self.head
self.size = 0
def append(self,node):
self.tail.pre.next = node
node.pre = self.tail.pre
node.next =self.tail
self.tail.pre = node
self.size+=1
def pop(self, node=None):
#pop from head
if self.size == 0: return None
if not node:
node = self.head.next
node.pre.next =node.next
node.next.pre = node.pre
self.size-=1
return node
def __len__(self):
return self.size
def __repr__(self):
res = []
cur = self.head
while cur:
res.append( '-'.join(map(str,[cur.freq,cur.key,cur.val])) )
cur = cur.next
return ','.join(res)
class LFUCache:
#
# freq1 freq2 .
# H 1 2 T H 3 4 T
def __init__(self, capacity: int):
self.size = 0
self.cap = capacity
self.key2node = dict()
self.freq2dll = defaultdict(DLL)
self.minfreq = 0
def debug(self,flag=False):
if flag:
print('#'*10)
for f, dll in self.freq2dll.items():
print(f)
print(dll)
print('#'*10)
def _update(self,node):
freq = node.freq
self.freq2dll[freq].pop(node)
if self.minfreq==freq and len(self.freq2dll[freq])==0:
self.minfreq+=1
node.freq+=1
freq = node.freq
self.freq2dll[freq].append(node)
def get(self, key: int) -> int:
#print('get',key)
if key not in self.key2node: return -1
node = self.key2node[key]
self._update(node)
self.debug()
return node.val
def put(self, key: int, value: int) -> None:
if self.cap == 0: return
if key in self.key2node:
node = self.key2node[key]
self._update(node)
node.val = value
else:
if self.size==self.cap:
node = self.freq2dll[self.minfreq].pop()
del self.key2node[node.key]
self.size-=1
#if not len(self.freq2dll[self.minfreq]):
# self.minfreq+=1
node = Node(key,value)
self.key2node[key] = node
self.freq2dll[1].append(node)
self.minfreq = 1
self.size+=1
self.debug()
# Your LFUCache object will be instantiated and called as such:
# obj = LFUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
Each key is mapping to the corresponding node (self._node), where we can retrieve the node in O(1) time.
Each frequency freq is mapped to a Doubly Linked List (self._freq), where all nodes in the DLinkedList have the same frequency, freq. Moreover, each node will be always inserted in the head (indicating most recently used).
A minimum frequency self._minfreq is maintained to keep track of the minimum frequency of across all nodes in this cache, such that the DLinkedList with the min frequency can always be retrieved in O(1) time.
Here is how the algorithm works
get(key)
query the node by calling self._node[key]
find the frequency by checking node.freq, assigned as f, and query the DLinkedList that this node is in, through calling self._freq[f]
pop this node
update node's frequence, append the node to the new DLinkedList with frequency f+1
if the DLinkedList is empty and self._minfreq == f, update self._minfreq to f+1.
return node.val
put(key, value)
If key is already in cache, do the same thing as get(key), and update node.val as value
Otherwise:
if the cache is full, pop the least frequenly used element (*)
add new node to self._node
add new node to self._freq[1]
reset self._minfreq to 1
(*) The least frequently used element is the tail element in the DLinkedList with frequency self._minfreq
#MY ANSWER
# freq1 freq 2 freq3
# H 1 T H 2 T H 3 T
debug_flag = False
class Node:
def __init__(self,key,value):
self.key = key
self.val = value
self.pre = None
self.next = None
class DLL:
def __init__(self):
self.size = 0
self.head = Node('H','H')
self.tail = Node('T','T')
self.head.next = self.tail
self.tail.pre = self.head
self.key2node = dict()
def append(self,node):
key = node.key
self.key2node[key] = node
self.size+=1
#append before tail
self.tail.pre.next = node
node.pre =self.tail.pre
node.next = self.tail
self.tail.pre = node
def pop0(self, node = None):
if not node:
node = self.head.next
self.size-=1
key = node.key
node = self.key2node[key]
del self.key2node[key]
#remove node
node.pre.next = node.next
node.next.pre = node.pre
return node
def get_node(self,key):
if key in self.key2node:
return self.key2node[key]
return None
def __len__(self):
return self.size
def __repr__(self):
res = []
cur = self.head
while cur:
res.append('-'.join([str(cur.key),str(cur.val)]))
cur =cur.next
return ','.join(res)
class LFUCache:
def __init__(self, capacity: int):
self.cap = capacity
self.c = 0
self.freq2dll = collections.defaultdict(DLL)
self.key2freq = collections.defaultdict(int)
self.minfreq = None
self.debugcounter = -1
def debug(self):
if debug_flag:
print('#'*10)
for f in sorted(self.freq2dll):
dll = self.freq2dll[f]
print(f)
print(dll)
print('#'*10)
def get(self, key: int) -> int:
self.debugcounter+=1
if debug_flag:print(self.debugcounter , 'get',key)
if key not in self.key2freq: return -1
old_freq = self.key2freq[key]
new_freq = self.key2freq[key]+1
self.key2freq[key] = new_freq
node = self.freq2dll[old_freq].get_node(key)
self.freq2dll[old_freq].pop0(node)
if old_freq==self.minfreq and not self.freq2dll[old_freq]:
del self.freq2dll[old_freq]
self.minfreq = new_freq
if new_freq not in self.freq2dll:
self.freq2dll[new_freq] = DLL()
self.freq2dll[new_freq].append(node)
self.debug()
return node.val
def put(self, key: int, value: int) -> None:
self.debugcounter+=1
if debug_flag:print(self.debugcounter,'put',key,value)
if key not in self.key2freq:
self.c+=1
if self.c>self.cap:
#remove least frequent
node = self.freq2dll[self.minfreq].pop0()
del self.key2freq[node.key]
if not self.freq2dll[self.minfreq]:
del self.freq2dll[self.minfreq]
self.c-=1
self.key2freq[key] = 1
if 1 not in self.freq2dll:
self.freq2dll[1] = DLL()
node = Node(key,value)
self.freq2dll[1].append(node)
self.minfreq = 1
else:
#key in key2freq
self.key2freq[key] += 1
old_freq = self.key2freq[key]-1
new_freq = self.key2freq[key]
#update old_freq dll
node = self.freq2dll[old_freq].get_node(key)
self.freq2dll[old_freq].pop0(node)
if not self.freq2dll[old_freq]:
del self.freq2dll[old_freq]
node.val = value
# update minfreq
if old_freq == self.minfreq and not self.freq2dll[old_freq]:
self.minfreq = new_freq
#node ready to insert to new_freq dll
if new_freq not in self.freq2dll:
self.freq2dll[new_freq] = DLL()
self.freq2dll[new_freq].append(node)
self.debug()
要特别注意 update minfreq,
if old_freq == self.minfreq and not self.freq2dll[old_freq]:
self.minfreq = new_freq