- 第215题:数组中的第k个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
解法一:排序,再返回倒数第k个元素。
但是堆排序的时间复杂度:O(nlogn),空间复杂度O(1),这个时间复杂度并不理想,让我们试着用额外空间来优化时间复杂度。
代码1:
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
nums.sort()
return nums[-k]
解法二:创建一个小顶堆,将所有数组中的元素加入堆中,并保持堆的大小小于等于 k。这样,堆中就保留了前 k 个最大的元素。这样,堆顶的元素就是正确答案。
(python自带的heapq模块实现的是最小堆,没有提供最大堆的实现。)
向大小为 k 的堆中添加元素的时间复杂度为 O(logk),我们将重复该操作 N 次,故总时间复杂度为 O(nlogk)。
在 Python 的 heapq 库中有一个 nlargest 方法,具有同样的时间复杂度,能将代码简化到只有一行。
本方法优化了时间复杂度,但需要O(k) 的空间复杂度,用于存储堆元素。
代码2:
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
return heapq.nlargest(k,nums)[-1]
- 第264题: 丑数II
编写一个程序,找出第 n 个丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:
1 是丑数。
n 不超过1690。
思想:2种思想。堆和动态规划:
(1)堆:最小堆。因为丑数是2,3,5的倍数,我们要不断地把它们的倍数压入堆中,再按顺序弹出。要注意比如6(2×3,3×2),这种就要弹出两次。也有可能数字是要弹出多次的。所以要写while循环,弹出重复的最小数字。
时间复杂度:nlogn
class Solution(object):
def nthUglyNumber(self, n):
"""
:type n: int
:rtype: int
"""
heap = [1]
heapq.heapify(heap) # 列表转换成堆
for _ in range(n):
result = heapq.heappop(heap) # 弹出最小值,也就是第一位heap[0]
while heap and result == heap[0]:
result = heapq.heappop(heap)
a,b,c = result*2,result*3,result*5 #要准备把倍数,推入堆中
for t in [a,b,c]:
heapq.heappush(heap,t)
return result
(2) 动态规划。
后面的丑数一定是由前面的丑数乘以2、3或5得到。所以第n个丑数一定是由前n-1个数中的某3个丑数(分别记为index2、index3、index5)分别乘以2、3或者5得到的数中的最小数, 即第n个丑数由 [index2]*2、[index3]*3、[index5]*5中的最小数得出。循环中,让它们分别和当前第n个丑数比较,若和当前第n个丑数相等,则更新它们的值。注:一次最少更新一个值(如遇到当前第n个丑数是6时,index2和index3都要更新)。
class Solution(object):
def nthUglyNumber(self, n):
"""
:type n: int
:rtype: int
"""
idx2 = 0
idx3 = 0
idx5 = 0
res = [1]
while len(res)<n:
res.append(min(res[idx2]*2, res[idx3]*3, res[idx5]*5))
if res[idx2]*2 == res[-1]:
idx2 +=1
if res[idx3]*3 == res[-1]:
idx3 +=1
if res[idx5]*5 == res[-1]:
idx5 +=1
return res[-1]
附:回顾第263题:丑数(简单题)
编写一个程序判断给定的数是否为丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。
示例 1:
输入: 6
输出: true
解释: 6 = 2 × 3
示例 2:
输入: 8
输出: true
解释: 8 = 2 × 2 × 2
示例 3:
输入: 14
输出: false
解释: 14 不是丑数,因为它包含了另外一个质因数 7。
说明:
1 是丑数。
输入不会超过 32 位有符号整数的范围: [−2^31, 2^31 − 1]。
class Solution(object):
def isUgly(self, num):
"""
:type num: int
:rtype: bool
"""
if num <= 0:
return False
for i in [2,3,5]:
while num%i == 0:
num /= i
return num == 1
- 第313题:超级丑数 Super Ugly Number
编写一段程序来查找第 n 个超级丑数。
超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数。
示例:
输入: n = 12, primes = [2,7,13,19]
输出: 32
解释: 给定长度为 4 的质数列表 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。
说明:
1 是任何给定 primes 的超级丑数。
给定 primes 中的数字以升序排列。
0 < k ≤ 100, 0 < n ≤ 10^6, 0 < primes[i] < 1000 。
第 n 个超级丑数确保在 32 位有符整数范围内。
思路一:采用堆的思想。就类似于264题的丑数II。超级丑数是primies=[xx,xx,xx,xx]中的数的倍数,我们要不断地把它们的倍数压入堆中,再按顺序弹出。
但这个思路,在这边存在问题,就是可能会超出时间限制。
class Solution(object):
def nthSuperUglyNumber(self, n, primes):
"""
:type n: int
:type primes: List[int]
:rtype: int
"""
heap = [1]
heapq.heapify(heap)
for _ in range(n):
result = heapq.heappop(heap)
while heap and result == heap[0]:
result = heapq.heappop(heap)
for p in primes:
t = result*p
heapq.heappush(heap, t)
return result
思路二:将丑数II的3个指针,改为多个指针。第n个丑数一定是由前n-1个数中的某k个丑数 分别乘以primes[xx,xx,xx,xx]中的数,所得到的最小数。
class Solution(object):
def nthSuperUglyNumber(self, n, primes):
"""
:type n: int
:type primes: List[int]
:rtype: int
"""
results = [1 for _ in range(n)]
pointers = [0 for _ in range(len(primes))]
for i in range(1,n): #循环results,其第一位已经定了是1
nextMin = 0xffffffff
for j in range(len(primes)):
nextMin = min(nextMin,results[pointers[j]]*primes[j])
results[i] = nextMin
for j in range(len(primes)):
if nextMin == results[pointers[j]]*primes[j]:
pointers[j] += 1
return results[-1]
- 第347题:前K个高频元素
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
说明:
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
解法一:运用到heapq.nlargest,这个函数可接收参数key,用于dict或其他数据结构类型使用
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
dict = {}
for num in nums:
if num not in dict:
dict[num] = 1
else:
dict[num] += 1
return heapq.nlargest(k, dict, key = dict.get)
解法二: 将构建字典部份,改成 collections.Counter(),一行代码解决。
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
count = collections.Counter(nums)
return heapq.nlargest(k, count, key = count.get)
- 第355题:设计推特 Design Twitter
设计一个简化版的推特(Twitter),可以让用户实现发送推文,关注/取消关注其他用户,能够看见关注人(包括自己)的最近十条推文。你的设计需要支持以下的几个功能:
postTweet(userId, tweetId): 创建一条新的推文
getNewsFeed(userId): 检索最近的十条推文。每个推文都必须是由此用户关注的人或者是用户自己发出的。推文必须按照时间顺序由最近的开始排序。
follow(followerId, followeeId): 关注一个用户
unfollow(followerId, followeeId): 取消关注一个用户
示例:
Twitter twitter = new Twitter();
// 用户1发送了一条新推文 (用户id = 1, 推文id = 5).
twitter.postTweet(1, 5);
// 用户1的获取推文应当返回一个列表,其中包含一个id为5的推文.
twitter.getNewsFeed(1);
// 用户1关注了用户2.
twitter.follow(1, 2);
// 用户2发送了一个新推文 (推文id = 6).
twitter.postTweet(2, 6);
// 用户1的获取推文应当返回一个列表,其中包含两个推文,id分别为 -> [6, 5].
// 推文id6应当在推文id5之前,因为它是在5之后发送的.
twitter.getNewsFeed(1);
// 用户1取消关注了用户2.
twitter.unfollow(1, 2);
// 用户1的获取推文应当返回一个列表,其中包含一个id为5的推文.
// 因为用户1已经不再关注用户2.
twitter.getNewsFeed(1);
代码:
class Twitter(object):
def __init__(self):
"""
Initialize your data structure here.
"""
self.timer = 0
self.followings = collections.defaultdict(set) # 关注的人,用set表示
self.messages = collections.defaultdict(list) # 推文列表
def postTweet(self, userId, tweetId):
"""
Compose a new tweet.
:type userId: int
:type tweetId: int
:rtype: None
"""
self.messages[userId].append([self.timer, tweetId]) # 时间戳+推文
self.timer += 1
def getNewsFeed(self, userId):
"""
Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent.
:type userId: int
:rtype: List[int]
"""
News = []
News += self.messages[userId]
for follower in self.followings[userId]:
News += self.messages[follower]
News = sorted(News, key=lambda x:x[0], reverse=True)
return [news[1] for news in News][:10]
def follow(self, followerId, followeeId):
"""
Follower follows a followee. If the operation is invalid, it should be a no-op.
:type followerId: int
:type followeeId: int
:rtype: None
"""
if followeeId not in self.followings[followerId] and followerId != followeeId:
self.followings[followerId].add(followeeId)
def unfollow(self, followerId, followeeId):
"""
Follower unfollows a followee. If the operation is invalid, it should be a no-op.
:type followerId: int
:type followeeId: int
:rtype: None
"""
if followeeId in self.followings[followerId]:
self.followings[followerId].remove(followeeId)
# Your Twitter object will be instantiated and called as such:
# obj = Twitter()
# obj.postTweet(userId,tweetId)
# param_2 = obj.getNewsFeed(userId)
# obj.follow(followerId,followeeId)
# obj.unfollow(followerId,followeeId)
- 第373题: 查找和最小的k对数字
给定两个以升序排列的整形数组 nums1 和 nums2, 以及一个整数 k。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。
找到和最小的 k 对数字 (u1,v1), (u2,v2) … (uk,vk)。
示例 1:
输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出: [1,2],[1,4],[1,6]
解释: 返回序列中的前 3 对数:
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
示例 2:
输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
输出: [1,1],[1,1]
解释: 返回序列中的前 2 对数:
[1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]
示例 3:
输入: nums1 = [1,2], nums2 = [3], k = 3
输出: [1,3],[2,3]
解释: 也可能序列中所有的数对都被返回:[1,3],[2,3]
思路一: 暴力解法
class Solution(object):
def kSmallestPairs(self, nums1, nums2, k):
"""
:type nums1: List[int]
:type nums2: List[int]
:type k: int
:rtype: List[List[int]]
"""
if not nums1 or not nums2:
return []
res = []
for i in nums1:
for j in nums2:
res.append([i,j])
res = sorted(res, key = lambda x:x[0]+x[1])
return res[:k]
思路二:采用堆的解法
class Solution(object):
def kSmallestPairs(self, nums1, nums2, k):
"""
:type nums1: List[int]
:type nums2: List[int]
:type k: int
:rtype: List[List[int]]
"""
import heapq
if not nums1 or not nums2:
return []
heap = []
res = []
heapq.heappush(heap,(nums1[0]+nums2[0],0,0))
visited = {(0,0)} #记录,防止重复
while heap and len(res) < k:
_,i,j = heapq.heappop(heap)
res.append([nums1[i],nums2[j]])
## 每次nums1或nums2移动
if i+1 < len(nums1) and (i+1,j) not in visited:
heapq.heappush(heap,(nums1[i+1]+nums2[j],i+1,j))
visited.add((i+1,j))
if j+1 < len(nums2) and (i,j+1) not in visited:
heapq.heappush(heap,(nums1[i]+nums2[j+1],i,j+1))
visited.add((i,j+1))
return res
- 第378题:有序矩阵中第k小的元素
给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第k小元素,而不是第k个元素。
示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
返回 13。
说明:
你可以假设 k 的值永远是有效的, 1 ≤ k ≤ n^2 。
思路一:二分法。看到这种有序(或者部分有序)的数组,一般考虑使用二分查找进行优化。
思路非常简单:
1.找出二维矩阵中最小的数left,最大的数right,那么第k小的数必定在left~right之间
2.mid=(left+right) / 2;在二维矩阵中寻找小于等于mid的元素个数count
3.若这个count小于k,表明第k小的数在右半部分且不包含mid,即left=mid+1, right=right,又保证了第k小的数在left~right之间
4.若这个count大于k,表明第k小的数在左半部分且可能包含mid,即left=left, right=mid,又保证了第k小的数在left~right之间
5.因为每次循环中都保证了第k小的数在left~right之间,当left==right时,第k小的数即被找出,等于right或left
注意:这里的left, mid, right是数值,不是索引位置。
class Solution(object):
def kthSmallest(self, matrix, k):
"""
:type matrix: List[List[int]]
:type k: int
:rtype: int
"""
## 1. 二分查找法
l = matrix[0][0]
r = matrix[-1][-1]
n = len(matrix)
while l < r:
mid = (l+r)//2
count = 0
for i in range(n):
for j in range(n):
if matrix[i][j] <= mid:
count+=1
if count < k:
l = mid+1
else:
r = mid
return l
思路二:堆。不用把每个元素都进堆,只需要把“最可能是最小值”的进堆即可。也就是说我们每次进堆的元素只要包括下一个最小的元素即可。
我们把左上角最小的元素和其索引进堆,然后每次把元素的右边的元素进堆。当是第一列元素的时候,把这个元素下面的元素也进堆。(不能把每一个元素的右边和下边都进堆,因为会重复)
class Solution(object):
def kthSmallest(self, matrix, k):
"""
:type matrix: List[List[int]]
:type k: int
:rtype: int
"""
n = len(matrix)
heap = []
heapq.heappush(heap,(matrix[0][0],0,0))
for _ in range(k):
ans, i, j = heapq.heappop(heap)
if j == 0 and i+1<n:
heapq.heappush(heap,(matrix[i+1][j],i+1,j))
if j+1<n:
heapq.heappush(heap,(matrix[i][j+1],i,j+1))
return ans
- 第451题:根据字符出现频率排序
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例 1:
输入:
“tree”
输出:
“eert”
解释:
'e’出现两次,'r’和’t’都只出现一次。
因此’e’必须出现在’r’和’t’之前。此外,"eetr"也是一个有效的答案。
示例 2:
输入:
“cccaaa”
输出:
“cccaaa”
解释:
'c’和’a’都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:
输入:
“Aabb”
输出:
“bbAa”
解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意’A’和’a’被认为是两种不同的字符。
class Solution(object):
def frequencySort(self, s):
"""
:type s: str
:rtype: str
"""
look_up = {}
for ch in s:
if ch not in look_up:
look_up[ch] = 1
else:
look_up[ch] += 1
# look_up = dict(collections.Counter(s)) #可以快速生成字典
look_up = sorted(look_up.items(), key = lambda x:x[1], reverse = True)
result = []
for char,count in look_up:
result.extend([char]*count)
return "".join(result)
- 第659题:分割数组为连续子序列
输入一个按升序排序的整数数组(可能包含重复数字),你需要将它们分割成几个子序列,其中每个子序列至少包含三个连续整数。返回你是否能做出这样的分割?
示例 1:
输入: [1,2,3,3,4,5]
输出: True
解释:
你可以分割出这样两个连续子序列 :
1, 2, 3
3, 4, 5
示例 2:
输入: [1,2,3,3,4,4,5,5]
输出: True
解释:
你可以分割出这样两个连续子序列 :
1, 2, 3, 4, 5
3, 4, 5
示例 3:
输入: [1,2,3,4,4,5]
输出: False
提示:
输入的数组长度范围为 [1, 10000]
思想:count[x]:每个数字的出现次数, tails[x] :恰好在 x 之前结束的链的数目。
现在我们逐一考虑每个数字,如果有一个链恰好在 x 之前结束,我们将 x 加入此链中。否则,如果我们可以新建立一条链就新建。
备注:
dict和Counter的区别。dict,当key不存在时,会报错。而Counter继承了dict类,当key不存在时,返回0。
class Solution(object):
def isPossible(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
count = collections.Counter(nums) # 使用Counter形式,即当key不存在时,返回0。不会报错
tails = collections.Counter() # Counter()继承dict类
for x in nums:
if count[x] == 0:
continue
elif tails[x] > 0: ## 一定要先判断count[x] == 0 和 tails[x] >0
tails[x] -= 1
tails[x+1] += 1
count[x] -= 1
elif count[x+1]>0 and count[x+2]>0:
count[x+1] -= 1
count[x+2] -=1
count[x] -= 1
tails[x+3] +=1
else:
return False
return True