目录
849. 到最近的人的最大距离
532. 数组中的K-diff数对
581. 最短无序连续子数组
914. 卡牌分组
204. 计数质数
624. 数组列表中的最大距离
673. 最长递增子序列的个数
277. 搜寻名人
470. 用 Rand7() 实现 Rand10()
321. 拼接最大数
https://leetcode-cn.com/problems/maximize-distance-to-closest-person/
在一排座位( seats)中,1 代表有人坐在座位上,0 代表座位上是空的。至少有一个空座位,且至少有一人坐在座位上。亚历克斯希望坐在一个能够使他与离他最近的人之间的距离达到最大化的座位上。返回他到离他最近的人的最大距离。
示例 1:输入:[1,0,0,0,1,0,1],输出:2
解释:如果亚历克斯坐在第二个空位(seats[2])上,他到离他最近的人的距离为 2 。如果亚历克斯坐在其它任何一个空位上,他到离他最近的人的距离为 1 。因此,他到离他最近的人的最大距离是 2 。
示例 2:输入:[1,0,0,0],输出:3
解释: 如果亚历克斯坐在最后一个座位上,他离最近的人有 3 个座位远。这是可能的最大距离,所以答案是 3 。
提示:1 <= seats.length <= 20000,seats 中只含有 0 和 1,至少有一个 0,且至少有一个 1。
思路
一:双指针法,[l,r]中只有l和r上有人,则这段空位到最近的人的距离为(r - l) / 2,最左端和最右端有点特殊,需要单独处理。
class Solution(object):
def maxDistToClosest(self, seats):
"""
:type seats: List[int]
:rtype: int
"""
res, l, r = 0, 0, 0
# 处理最左端是空位的情况
while l < len(seats):
if seats[l] == 1:
break
l += 1
res = l - 0
r = l + 1
# [l,r]
while r < len(seats):
if seats[r] == 1:
res = max(res, (r - l) // 2)
l = r
r += 1
# 处理最右端是空位的情况
if seats[-1] == 0:
res = max(res, len(seats) - 1 - l)
return res
class Solution(object):
def maxDistToClosest(self, seats):
l, res, r = 0, 1, len(seats) - 1
while l < len(seats) and seats[l] == 0:
l += 1
res = max(l, res)
while r >= 0 and seats[r] == 0:
r -= 1
res = max(len(seats) - 1 - r, res)
for r in range(l, r + 1):
if seats[r] == 0:
while seats[l] != 0:
l += 1
res = max(res, (r - l) // 2 + 1)
else:
l = r
return res
https://leetcode-cn.com/problems/k-diff-pairs-in-an-array/
给定一个整数数组和一个整数 k, 你需要在数组里找到不同的 k-diff 数对。这里将 k-diff 数对定义为一个整数对 (i, j), 其中 i 和 j 都是数组中的数字,且两数之差的绝对值是 k.
示例 1:输入: [3, 1, 4, 1, 5], k = 2,输出: 2
解释: 数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。尽管数组中有两个1,但我们只应返回不同的数对的数量。
示例 2:输入:[1, 2, 3, 4, 5], k = 1,输出: 4
解释: 数组中有四个 1-diff 数对, (1, 2), (2, 3), (3, 4) 和 (4, 5)。
示例 3:输入: [1, 3, 1, 5, 4], k = 0,输出: 1
解释: 数组中只有一个 0-diff 数对,(1, 1)。
注意:数对 (i, j) 和数对 (j, i) 被算作同一数对。数组的长度不超过10,000。所有输入的整数的范围在 [-1e7, 1e7]。
思路
一:先排序加双指针,将k=0的情况单拎出来,若存在两个以上相同的数算一个解;若k!=0用set将重复元素只保留一个。若不单独处理k=0的情况,例[1,1,3,4 ,5] ,k=0,l和r可能相等,(0, 1) (2, 2) (3, 3) (4, 4)。
class Solution(object):
def findPairs(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
res = 0
if not nums or k < 0:
return res
if k == 0:
nums = sorted(nums)
cnt, val = 1, nums[0]
for i in range(1, len(nums)):
if nums[i] == val:
cnt += 1
if i == len(nums) - 1:
res += 1
else:
if cnt > 1:
res += 1
cnt, val = 1, nums[i]
return res
nums = list(set(nums))
nums = sorted(nums)
l = 0
for r in range(len(nums)):
if nums[r] - nums[l] == k:
res += 1
l += 1
elif nums[r] - nums[l] > k:
while nums[r] - nums[l] > k:
l += 1
if nums[r] - nums[l] == k:
res += 1
l += 1
return res
二:排序后遍历数组,定义快慢指针,r为快指针,l为慢指针,同时定义prev变量来记录前一个符合要求的字符,避免重复字符的影响。此处可以保证l>r。
class Solution(object):
def findPairs(self, nums, k):
# 特判,看的评论,绝对值不可能为负,直接返回0
if k < 0 or len(nums) < 2:
return 0
nums = sorted(nums)
l, prev = 0, nums[0] - 1
count = 0
for r in range(1, len(nums)):
while (nums[r] - nums[l] > k or nums[l] == prev) and (r != l + 1):
l += 1
if nums[r] - nums[l] == k and nums[l] != prev:
count += 1
prev = nums[l]
l += 1
return count
三:借助哈希表,saw存放的是以访问的数据,diff中存放的是K-diff数对其中的一个数,且集合不含重复元素,可利用该特性去重。
class Solution(object):
def findPairs(self, nums, k):
if k < 0:
return 0
saw, diff = set(), set()
for num in nums:
if num - k in saw:
diff.add(num-k)
if num + k in saw:
diff.add(num)
saw.add(num)
return len(diff)
https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray/
给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。你找到的子数组应是最短的,请输出它的长度。
示例 1:输入: [2, 6, 4, 8, 10, 9, 15],输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
说明 :输入的数组长度范围在 [1, 10,000]。输入的数组可能包含重复元素 ,所以升序的意思是<=。
思路
一:先排序,从两边找出不对的位置,即可。
class Solution(object):
def findUnsortedSubarray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
nums_copy, n = nums[:], len(nums)
nums_copy = sorted(nums_copy)
l, r = -1, n
for i in range(n):
if nums_copy[i] != nums[i]:
l = i
break
for i in range(n - 1, -1, -1):
if nums_copy[i] != nums[i]:
r = i
break
if l == -1:
return 0
return r - l + 1
二:转自https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray/solution/java-ji-bai-100-shi-jian-fu-za-du-on-kong-jian-fu-/大神的题解,
一个例子明白下面的算法
输入[2, 3, 11, 8, 10, 9, 15, 22]
算法流程:
从左往右遍历数组, 发现{15, 22}是递增的, 同时15比前面所有的元素都大, 说明{15, 22}的位置已经固定了
从右往左遍历数组, 发现{2, 3}是递增的, 同时3比右边所有的元素都小, 所以{2,3}的位置也已经固定了经过两遍遍历之后发现{2,3}和{15,22}不用排序了,只需要将{11, 8, 10, 9}这4个元素排序即可, 所以最终输出4
遍历的过程就是在找哪些元素位置已经固定, 不需要排序了首先明确两个变量的含义,r表示[r n-1]范围上的元素不用排序,l表示[0, l-1]范围上的元素不用排序,所以最终需要排序的连续子数组范围是[l r], 一共有r-l+1个元素
接下来是遍历过程
从左往右遍历数组, 记录nums[i]左侧的最大值, 如果nums[i]>=cur_max, 就更新cur_max, 如果nums[i]从右往左遍历数组, 记录arr[i]右侧的最小值, 如果nums[i]<=cur_min, 就更新cur_min, 如果nums[i]>cur_min, 则使用变量l记录索引i; 遍历结束后, l左侧的部分不需要排序
这两遍遍历做的事情: 找到递增的部分
class Solution(object):
def findUnsortedSubarray(self, nums):
n = len(nums)
l, r = -1, -1
cur_max, cur_min = nums[0], nums[n - 1]
for i in range(n):
if cur_max <= nums[i]:
cur_max = nums[i]
else:
r = i
for i in range(n - 1, -1, -1):
if cur_min >= nums[i]:
cur_min = nums[i]
else:
l = i
if r > l:
return r - l + 1
return 0
https://leetcode-cn.com/problems/x-of-a-kind-in-a-deck-of-cards/
给定一副牌,每张牌上都写着一个整数。此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:每组都有 X 张牌。组内所有的牌上都写着相同的整数。仅当你可选的 X >= 2 时返回 true。
示例 1:输入:[1,2,3,4,4,3,2,1],输出:true
解释:可行的分组是 [1,1],[2,2],[3,3],[4,4]
示例 2:输入:[1,1,1,2,2,2,3,3],输出:false
解释:没有满足要求的分组。
示例 3:输入:[1],输出:false
解释:没有满足要求的分组。
示例 4:输入:[1,1],输出:true
解释:可行的分组是 [1,1]
示例 5:输入:[1,1,2,2,2,2],输出:true
解释:可行的分组是 [1,1],[2,2],[2,2]
提示:1 <= deck.length <= 10000,0 <= deck[i] < 10000
思路
一:copy官方题解,https://leetcode-cn.com/problems/x-of-a-kind-in-a-deck-of-cards/solution/qia-pai-fen-zu-by-leetcode/,同理假设 rec[num] 是数字 num的卡片个数,同样对于所有的num 满足rec[num] % X == 0,其中每堆有 X 张卡片。因此,X 一定可以整除rec[num] 的最大公约数。如果最大公约数 g 超过 1,那么就有 X = g 满足条件,否则不满足条件。
from collections import Counter class Solution(object): def hasGroupsSizeX(self, deck): """ :type deck: List[int] :rtype: bool """ if not deck: return False rec = Counter(deck) val = rec[deck[0]] for k, v in rec.items(): if val < v: val = self._common_divisor(v, val) else: val = self._common_divisor(val, v) return val != 1 def _common_divisor(self, a, b): if a % b == 0: return b return self._common_divisor(b, a % b)
https://leetcode-cn.com/problems/count-primes/
统计所有小于非负整数 n 的质数的数量。
示例:输入: 10 输出: 4 解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
题解
一:暴力解法,质数只能被1和自己整除,我们依次检查该数是否是质数,只要看到sqrt(i)就好,时间复杂度。
class Solution(object):
def countPrimes(self, n):
"""
:type n: int
:rtype: int
"""
num = 1
if n <= 2:
return 0
for i in xrange(3, n, 2):
j, flag = 2, True
limit = min(int(sqrt(i)) , i - 1)
while j <= limit:
if i % j == 0:
flag = False
break
j += 1
if flag:
num += 1
return num
二:筛法。用flag来标记是否是质数,flag[i]=True表示i是质数,flag[i]=False,表示i不是质数。这边注意一下,代码中0,1也为True,其实他们不是质数,不过木有关系。我们从2开始看的,若n<=2,则不会进入循环,直接返回0。若n>2,则进入循环,开始筛法。
class Solution(object):
def countPrimes(self, n):
flag = [True] * n
num = 0
for i in xrange(2, n):
if flag[i]:
num += 1
for j in xrange(i + i, n, i):
flag[j] = False
return num
https://leetcode-cn.com/problems/maximum-distance-in-arrays/
给定 m 个数组,每个数组都已经按照升序排好序了。现在你需要从两个不同的数组中选择两个整数(每个数组选一个)并且计算它们的距离。两个整数 a 和 b 之间的距离定义为它们差的绝对值 |a-b| 。你的任务就是去找到最大距离
示例 1:
输入:
[[1,2,3],
[4,5],
[1,2,3]]
输出: 4
解释:一种得到答案 4 的方法是从第一个数组或者第三个数组中选择 1,同时从第二个数组中选择 5 。
注意:每个给定数组至少会有 1 个数字。列表中至少有两个非空数组。所有 m 个数组中的数字总数目在范围 [2, 10000] 内。m 个数组中所有整数的范围在 [-10000, 10000] 内。
题解
一:其实我们最关心的就是m个数组的最小值(第一个元素,记录在min_num中)以及m个数组的最大值(最后一个元素记录在max_num中)的差。然后我们最关新的是最小值里面最小的那个,以及最大值里面最大的那个。可以通过heapq.nsmallest取最小的两个,heapq.nlargest取最大的两个。至于为啥是两个,是因为最小值和最大值有可能在同一个数组中,不符合题意。
import heapq
class Solution(object):
def maxDistance(self, arrays):
"""
:type arrays: List[List[int]]
:rtype: int
"""
min_num = [(arrays[i][0], i) for i in range(len(arrays))]
max_num = [(arrays[i][-1], i) for i in range(len(arrays))]
rec_min = heapq.nsmallest(2, min_num, key=lambda a: a[0])
rec_max = heapq.nlargest(2, max_num, key=lambda a: a[0])
if rec_max[0][1] != rec_min[0][1]:
return abs(rec_max[0][0] - rec_min[0][0])
else:
return max(abs(rec_max[0][0] - rec_min[1][0]), abs(rec_max[1][0] - rec_min[0][0]))
二:其实我们还是关注最小里面最小的那个以及最大的里面最大的那个,我们考虑第i个数组时,可以拿到前i-1个数组的最小值的最小值,以及最大值的最大值。
import heapq
class Solution(object):
def maxDistance(self, arrays):
res, tmp_max, tmp_min = 0, float("-inf"), float("inf")
for i in range(len(arrays)):
res = max([res, tmp_max - arrays[i][0], arrays[i][-1] - tmp_min])
tmp_min = min(tmp_min, arrays[i][0])
tmp_max = max(tmp_max, arrays[i][-1])
return res
https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/
给定一个未排序的整数数组,找到最长递增子序列的个数。
示例 1:输入: [1,3,5,4,7],输出: 2,解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:输入: [2,2,2,2,2],输出: 5,解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
注意: 给定的数组长度不超过 2000 并且结果一定是32位有符号整数。
题解
一:动态规划,O(n^2)的时间复杂度,res[i]表示以i结尾的最长递增子序列的长度,count[i]表示以i结尾的最长子序列的个数。res数组的更新好理解,这边解释一下count数组的更新,若i 可以接在j的后面,我们看若res[i] == res[j] + 1,则表明接在j后面也是其中一个最长子序列,那么count[i]的个数必须加上以j结尾的最长子序列的个数。res[i] < res[j] + 1,则说明,接在j后面的到的最长子序列的长度要比之前的字符都要长,这时候最长子序列的长度增加,故之前count[i]的值要清零,然后其个数就是count[j]的个数。
class Solution(object):
def findNumberOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) <= 1:
return len(nums)
res = [1] * len(nums)
count = [1] * len(nums)
for i in range(1, len(nums)):
for j in range(i):
if nums[i] > nums[j]:
if res[i] > res[j] + 1:
continue
elif res[i] == res[j] + 1:
count[i] += count[j]
else:
res[i] = res[j] + 1
count[i] = count[j]
max_freq, cnt = max(res), 0
for i in range(len(nums)):
if res[i] == max_freq:
cnt += count[i]
return cnt
https://leetcode-cn.com/problems/find-the-celebrity/
假设你是一个专业的狗仔,参加了一个 n 人派对,其中每个人被从 0 到 n - 1 标号。在这个派对人群当中可能存在一位 “名人”。所谓 “名人” 的定义是:其他所有 n - 1 个人都认识他/她,而他/她并不认识其他任何人。
现在你想要确认这个 “名人” 是谁,或者确定这里没有 “名人”。而你唯一能做的就是问诸如 “A 你好呀,请问你认不认识 B呀?” 的问题,以确定 A 是否认识 B。你需要在(渐近意义上)尽可能少的问题内来确定这位 “名人” 是谁(或者确定这里没有 “名人”)。
在本题中,你可以使用辅助函数 bool knows(a, b) 获取到 A 是否认识 B。请你来实现一个函数 int findCelebrity(n)。派对最多只会有一个 “名人” 参加。若 “名人” 存在,请返回他/她的编号;若 “名人” 不存在,请返回 -1。
示例 1:
输入: graph = [[1,1,0],[0,1,0],[1,1,1]],输出: 1,解析: 有编号分别为 0、1 和 2 的三个人。graph[i][j] = 1 代表编号为 i 的人认识编号为 j 的人,而 graph[i][j] = 0 则代表编号为 i 的人不认识编号为 j 的人。“名人” 是编号 1 的人,因为 0 和 2 均认识他/她,但 1 不认识任何人。
示例 2:输入: graph = [[1,0,1],[1,1,0],[0,1,1]],输出: -1,解析: 没有 “名人”
注意:该有向图是以邻接矩阵的形式给出的,是一个 n × n 的矩阵, a[i][j] = 1 代表 i 与 j 认识,a[i][j] = 0 则代表 i 与 j 不认识。
请记住,您是无法直接访问邻接矩阵的。
题解
一:活用任何人都认识名人,但名人不认识任何人这一特性。如果 knows(l,r) 为ture,说明l不可能是名人,如果 knows(l,r) 为false, 说明r不可能是名人,也就说说任意两人相互比较总能淘汰一个人。这样就可以在线性时间内找到名人,最简单的方法是迭代一遍数组。但是有可能宴会不存在名人,需要教验一遍,他不认识其他人,其他人都认识他。
# The knows API is already defined for you.
# @param a, person a
# @param b, person b
# @return a boolean, whether a knows b
# def knows(a, b):
class Solution(object):
def findCelebrity(self, n):
"""
:type n: int
:rtype: int
"""
l, r = 0, n - 1
while l < r:
if knows(l, r): # l 认识 r, l一定不是
l += 1
else: #l不认识r,r一定不是明星
r -= 1
for i in range(n):
if l == i:
continue
if knows(l, i) or not knows(i, l):
return -1
return l
https://leetcode-cn.com/problems/implement-rand10-using-rand7/
已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。不要使用系统的 Math.random() 方法。
示例 1:输入: 1,输出: [7]
示例 2:输入: 2,输出: [8,4]
示例 3:输入: 3,输出: [8,1,10]
提示:rand7 已定义。传入参数: n 表示 rand10 的调用次数。
题解
一:拒绝采样。转自官方题解,https://leetcode-cn.com/problems/implement-rand10-using-rand7/solution/yong-rand7-shi-xian-rand10-by-leetcode/,
我们可以用拒绝采样的方法实现 Rand10()。在拒绝采样中,如果生成的随机数满足要求,那么久返回该随机数,否则会不断生成直到一个满足要求的随机数为止。若我们调用两次 Rand7(),那么可以生成 [1, 49] 之间的随机整数,我们只用到其中的 40 个,用来实现 Rand10(),而拒绝剩下的 9 个数,如下图所示。
我们来分析这种方法在平均情况下需要调用 Rand7() 的次数。我们称连续调用两次 Rand7() 为一轮,在第一轮中,有 40/49 的概率不被拒绝,而有 9/49 的概率被拒绝,进入第二轮。在第二轮中也是如此,因此调用 Rand7() 的期望次数为:
# The rand7() API is already defined for you.
# def rand7():
# @return a random integer in the range 1 to 7
class Solution(object):
def rand10(self):
"""
:rtype: int
"""
num = 0
while True:
row, col = rand7(), rand7()
num = col + (row - 1) * 7
if num <= 40:
break
return (num - 1) % 10 + 1
复杂度分析
时间复杂度:期望时间复杂度为 O(1),但最坏情况下会达到 O(∞)(一直被拒绝)。
空间复杂度:O(1)。
https://leetcode-cn.com/problems/create-maximum-number/
给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。
求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。说明: 请尽可能地优化你算法的时间和空间复杂度。
示例 1:输入:nums1 = [3, 4, 6, 5],nums2 = [9, 1, 2, 5, 8, 3],k = 5。输出:[9, 8, 6, 5, 3]
示例 2:输入:nums1 = [6, 7],nums2 = [6, 0, 4],k = 5。输出:[6, 7, 6, 0, 4]
示例 3:输入:nums1 = [3, 9],nums2 = [8, 9],k = 3。输出:[9, 8, 9]
题解
一:转自https://leetcode-cn.com/problems/create-maximum-number/solution/java-qiu-zi-wen-ti-he-bing-zi-wen-ti-geng-xin-jie-/,
假设答案的最大拼接数中, 有s个来自nums1, K-s个来自nums2
假设 s个来自nums1的子序列是nums1长度为s的最大子序列,
K - s 个来自nums2的子序列是长度为K - s 的最大子序列
证明:
反证法: 假设 s 个 来自nums1的子序列x 不是 nums1长度为s的最大子序列y
即存在长度为s的子序列 y > x, 假设y = 124 x = 123
假设来自nums2的最大子序列为z, z无论拆分到x的任何一个位置, 把相同的位置放置到y上,
y上合并的数字一定大于x上合并的数字, 所以最大子序列来自y, 证毕。因此,可以首先分别求出nums1中长度为s的最大子序列,和nums2中长度为k-s的最大子序列,
然后求它们归并起来的最大子序列的长度,最后对一切可能的s求最大值。总结:
步骤1: 枚举所有nums1可能的长度 Len1
步骤2: 获取nums1里长度为 Len1的最大子序列res1, 获取nums2里长度为k- Len1的最大序列res2
步骤3: 合并l1和res1成为数字最大的res
步骤4: 判断res是否比结果ans大, 是的话更新ans =res
步骤5: Len1的长度加1, 继续步骤1
class Solution(object):
def maxNumber(self, nums1, nums2, k):
"""
:type nums1: List[int]
:type nums2: List[int]
:type k: int
:rtype: List[int]
"""
# 求出单个数组可以组成k位的最大数组
def getKLargest(nums, k):
# popCnt表示最多可以不要nums里几个数字,要不组成不了
res, popCnt = [], len(nums) - k
for i in range(len(nums)):
while res and popCnt > 0 and res[-1] < nums[i]:
res.pop(len(res) - 1)
popCnt -= 1
if len(res) < k:
res.append(nums[i])
else:
popCnt -= 1
return res[:k]
def merge(res1, res2):
res = []
while res1 and res2:
if res1 > res2:
res.append(res1.pop(0))
else:
res.append(res2.pop(0))
res = res + res1 + res2
return res
# minLen1 = max([0, k - len(nums2)])
# maxLen1 = min(k, len(nums1))
ans = []
# for Len1 in range(minLen1, maxLen1 + 1):
for Len1 in range(k + 1):
if Len1 <= len(nums1) and k - Len1 <= len(nums2):
# 取num1的Len1位, num2的k - Len1
res1 = getKLargest(nums1, Len1)
res2 = getKLargest(nums2, k - Len1)
# 合并
ans = max(ans, merge(res1, res2))
return ans