LeetCode刷题(1)
1、两数之和
给定一个整数数组 nums 和目标值 target,请你在该数组中找出和为目标值的两个整数,返回它们的数组下标。假设只有一组整数。
示例:
nums = [2,7,11,15], target = 9
#返回:
[0,1]
注意:题目容易出错的情况在于数组中出现两个相同的数字和 target - num = num 的情况。
1.1、暴力法
解题的关键是看 num2 = target - num1 是否在nums 中。因此需要两个方法:(1)成员资格检查;(2)nums.index(num2) 获取索引。
针对每一个方法需要注意的问题:
-
成员资格检查:
如果单纯地使用 num2 in nums,就可能遇到前面说的容易出错的那种情况,比如,nums = [3,2,1],target = 6,那么 num2 = 6-3 = 3 也在数组中,但是明显不符合题目要求。
-
nums.index(num2) 获取索引
从第一个方面可知,如果要从全局进行成员资格检查,则应该避免出现相同的索引。index() 函数返回的是列表中第一个与 obj 相等的元素的索引。比如:nums = [3,3],target = 6,nums = 3,如果使用 index() 函数则会找到索引为 0 的 3,也不是我们想要的。因此,我们应该在该元素之前或之后查找 num2。
#最简单的暴力法,双指针
def twoSum(nums,target):
lens = len(nums)
for i in range(lens):
for j in range(i+1,lens):
if(nums[i] + nums[j] == target):
return [i,j]
#但这种方法的运行时间太长,大约在 6068ms,内存占用:14.6MB
我们改使用一次 for 循环,在该元素之前查找 num2,以空间换时间:
def twoSum(nums,target):
lens = len(nums)
j = -1
for i in range(1,lens):
temp = nums[:i]
if (target - nums[i]) in temp:
j = temp.index(target - nums[i])
break
if j >= 0:
return [i,j]
#在元素之前查找也省去了截取列表再获取索引时索引相对位置的变化问题
#运行时间:468ms,内存大小:14.6MB
1.2、用字典来模拟哈希
只需要一次 for 循环,我们知道哈希查找数据是最快的。
def twoSum(nums,target):
hashmap = {}
for i,num in enumrate(nums):
if hashmap.get(target - num) is not None:
return [hashmap.get(target - num),i]
hashmap[num] = i #放在 if 语句之后,也是为了处理前面说的容易出错的情况
#运行时间:72ms,内存大小:15.1MB
2、删除排序数组中的重复元素
给定一个排序数组,你需要在 “原地” 删除重复的元素,返回移除后数组的新长度。
2.1、方法:双指针
数组完成排序后,我们可以放置两个指针 i 和 j,其中 i 为慢指针,而 j 是快指针。只要 nums[i] = nums[j],j 就往后移动,跳过重复项;
当 nums[i] ≠ nums[j] 时,跳过重复项已结束,因此我们必须把它(nums[j])的值复制到 nums[i+1]。然后接着上面的过程继续,直到快指针到数组末尾。
def removeDuplicates(nums):
if len(nums) == 0:
return 0
i = 0
for j in range(1,len(nums)):
if nums[j] != nums[i]:
i += 1
nums[i] = nums[j]
return i+1
#运行时间:52ms;内存大小:14.6MB
补充:此题目要求的是 “原地”,否则可以使用 set 集合,直接去掉重复元素,然后返回集合的个数即可。
3、移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
3.1、方法:双指针
题目还是要求 “原地” 修改(如果不是原地并且修改的话,可以使用列表中的 count() 方法,统计出 val 的个数,直接用列表长度减去 val 的个数即可)。
我们放置双指针 i 和 j,j 为快指针。跟上题的思路差不多,只是 if 的条件变成了 = val 而已。
def removeElement(nums: List[int], val: int) -> int:
if len(nums) == 0:
return 0
i = 0
for j in range(len(nums)):
if nums[j] != val:
nums[i] = nums[j]
i += 1
else:
continue
return i
#运行时间:44ms;内存大小:13.6MB
4、搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
4.1、方法
典型的二分查找。
def searchInsert(nums: List[int], target: int) -> int:
low = 0
high = len(nums) -1
while(high >= low): #while 条件是 >=
mid = (low + high) // 2 #python 的整除是 //
if nums[mid] == target:
return mid
elif nums[mid] > target:
high = mid - 1
else:
low = mid + 1
return low
#运行时间:40ms,内存大小:14.4MB
5、最大子序和
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
5.1、方法:动态规划
-
第一步:定义状态
既然一个连续子数组一定要以一个数作为结尾,那么我们将状态定义如下:
dp[i]:表示以 nums[i] 结尾的连续子数组的最大和。
-
第二步:思考状态转移方程
根据状态的定义,由于 nums[i] 一定会被选取,并且 dp[i] 所表示的连续子序列与 dp[i-1] 所表示的连续子序列(有可能)就差一个 nums[i]。
假设数组 nums 全是正数,那么一定有 dp[i] = dp[i-1] + nums[i]。
但也有可能 dp[i-1] 是负数,如果加上 nums[i] 会更小,那么直接让 dp[i] = nums[i]。
于是可以分类讨论:
当 dp[i-1] >= 0 时,dp[i] = dp[i-1] + nums[i];
当 dp[i-1] < 0 时,dp[i] = nums[i]。
最终我们要的是最大值,即
dp[i] = max{dp[i-1] + nums[i],nums[i]}
动态规划的问题一般经常要分类讨论,这是因为动态规划的问题本来就有最优子结构的特性。即大问题的最优化解通常是由小问题的最优化解得到的。我们需要知道大问题的小问题是什么。
-
第三步:思考初始值
dp[0] = nums[0]
-
第四步:思考输出
输出的应该是 dp 的最大值
def maxSubArray(self, nums: List[int]) -> int:
if len(nums) == 0:
return 0
dp = len(nums) * [0]
dp[0] = nums[0]
for i in range(1,len(nums)):
dp[i] = max(dp[i-1] + nums[i],nums[i]) #python 中 max 是函数
return max(dp)
#运行时间:48 ms;内存大小:14.5 MB
6、加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
6.1、方法
将数组转换为数字然后加一,接着再转换为数组即可。
def plusOne(digits: List[int]) -> List[int]:
num = int(''.join(map(str,digits))) + 1 #需要使用 map() 函数,将列表中每个数字元素变为字符串,因为 py 中不允许数字和字符串相加合并
return [int(x) for x in str(num)]
如果使用数学方法,还要考虑数字 9 的情况。
7、合并两个有序数组
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
- 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
- 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
7.1、方法一
最简单的思路,将两个数组合并后再排序,时间复杂度较差,为O((n+m)log(n+m))。这是由于这种方法没有利用两个数组本身已经有序这一点。
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
nums1[:] = sorted(nums1[:m] + nums2)
#48 ms;13.5 MB
7.2、方法二:双指针 / 从前往后
一般而言,对于有序数组可以通过 双指针法 达到 O(n+m) 的时间复杂度。
最直接的算法实现是将指针 p1 置为 nums1 的开头, p2 为 nums2 的开头,在每一步将最小值放入输出数组中。
由于 nums1 是用于输出的数组,需要将 nums1 中的前 m 个元素放在其他地方,也就需要 O(m) 的空间复杂度。
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
nums1_copy = nums1[:m]
nums1[:] = []
i = 0
j = 0
while i < m and j < n:
if nums1_copy[i] < nums2[j]:
nums1.append(nums1_copy[i])
i += 1
else:
nums1.append(nums2[j])
j += 1
#while 循环结束时,一定有一个列表到末尾了,所以下面的 if 语句只会执行一个
if i < m:
nums1.extend(nums1_copy[i:m])
if j < n:
nums1.extend(nums2[j:n])
#48 ms;13.6 MB
7.3、方法三:双指针 / 从后往前
方法二已经取得了最优的时间复杂度 O(n+m),但需要使用额外空间。这是由于在从头改变 nums1 的值时,需要把 nums1 中的元素存放在其他位置。
如果我们从结尾开始改写 nums1 的值又会如何呢?这里没有信息,因此不需要额外空间。
这里的指针 p 用于追踪添加元素的位置。
def merge(nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
i = m - 1
j = n - 1
p = m + n - 1
while i >= 0 and j >= 0:
if nums2[j] > nums1[i]:
nums1[p] = nums2[j]
p -= 1
j -= 1
else:
nums1[p] = nums1[i]
p -= 1
i -= 1
#有可能 nums1 中的元素都比 nums2 中的大,那么上面的代码只是将 nums1 中的元素后移了而已,因此需要解决 nums2 中剩余的元素
nums1[:j + 1] = nums2[:j + 1]
#44 ms;13.7MB
8、杨辉三角
给定一个非负整数 *numRows,*生成杨辉三角的前 numRows 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。
8.1、方法
其实就是数组中的每个元素都是数组。
当 numRows == 0 时:[]
当 numRows == 1 时:[[1]]
当 numRows == 2 时:[[1],[1,1]]
当 numRows > 2 时:每行的首位都是 1 ,其余的部分可按照公式——result[i][j] = result[i-1][j-1] + result[i-1][j] 来计算得出。
def generate(numRows: int) -> List[List[int]]:
if numRows == 0:
return []
elif numRows == 1:
return [[1]]
elif numRows == 2:
return [[1],[1,1]]
else:
result = [[1],[1,1]]
for i in range(2,numRows):
list = [1] * (i+1)
result.append(list)
for j in range(1,i):
result[i][j] = result[i-1][j-1] + result[i-1][j]
return result
#44 ms;13.7 MB
9、杨辉三角 II
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
示例:
输入: 3
输出: [1,3,3,1]
9.1、方法
返回第 k 行,我们也要求出第 k 行的所有元素。方法如上。不过需要注意的是这里的参数 rowIndex 与上面的不太一样。
def getRow(rowIndex: int) -> List[int]:
if rowIndex == 0:
return [1]
elif rowIndex == 1:
return [1,1]
else:
result = [[1],[1,1]]
for i in range(2,rowIndex+1):
list = [1] * (i+1)
result.append(list)
for j in range(1,i):
result[i][j] = result[i-1][j-1] + result[i-1][j]
return result[rowIndex]
#36 ms;13.6 MB
10、买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
10.1、方法一:暴力法
我们需要找出给定数组中两个数字之间的最大差值(即,最大利润)。此外,第二个数字(卖出价格)必须大于第一个数字(买入价格)。
形式上,对于每组 i 和 j(其中 j>i)我们需要找出 max(prices[j]−prices[i])。
#此方法会超时
def maxProfit(prices: List[int]) -> int:
ans = 0
for i in range(len(prices)):
for j in range(i + 1, len(prices)):
ans = max(ans, prices[j] - prices[i])
return ans
10.2、方法二:动态规划
我们来假设自己来购买股票。随着时间的推移,每天我们都可以选择出售股票与否。那么,假设在第 i 天,如果我们要在今天卖股票,那么我们能赚多少钱呢?
显然,如果我们真的在买卖股票,我们肯定会想:如果我是在历史最低点买的股票就好了!太好了,在题目中,我们只要用一个变量记录一个历史最低价格 minprice,我们就可以假设自己的股票是在那天买的。那么我们在第 i 天卖出股票能得到的利润就是 prices[i] - minprice。
因此,我们只需要遍历价格数组一遍,记录历史最低点,然后在每一天考虑这么一个问题:如果我是在历史最低点买进的,那么我今天卖出能赚多少钱?当考虑完所有天数之时,我们就得到了最好的答案。
def maxProfit(prices: List[int]) -> int:
inf = int(1e9)
minprice = inf
maxprofit = 0
for price in prices:
maxprofit = max(price - minprice, maxprofit)
minprice = min(price, minprice)
return maxprofit
#44 ms;14.6 MB
11、买卖股票的最佳时机II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
我们必须确定通过交易能够获得的最大利润(对于交易次数没有限制)。为此,我们需要找出那些共同使得利润最大化的买入及卖出价格。
11.1、方法
股票买卖策略:
- 单独交易日: 设今天价格 p1、明天价格 p2,则今天买入、明天卖出可赚取金额 p2−p1 (负值代表亏损)。
- 连续上涨交易日: 设此上涨交易日股票价格分别为 p1,p2,…,pn,则第一天买最后一天卖收益最大,即 pn−p1;等价于每天都买卖,即 pn−p1=(p2−p1)+(p3−p2)+…+(pn−pn−1)。
- 连续下降交易日: 则不买卖收益最大,即不会亏钱。
算法流程:
- 遍历整个股票交易日价格列表
price
,策略是所有上涨交易日都买卖(赚到所有利润),所有下降交易日都不买卖(永不亏钱)。
- 设
tmp
为第i-1
日买入与第i
日卖出赚取的利润,即tmp = prices[i] - prices[i - 1]
; - 当该天利润为正
tmp > 0
,则将利润加入总利润profit
;当利润为 000 或为负,则直接跳过; - 遍历完成后,返回总利润
profit
。
复杂度分析:
- 时间复杂度 O(N): 只需遍历一次
price
; - 空间复杂度 O(1): 变量使用常数额外空间。
def maxProfit(prices: List[int]) -> int:
profile = 0
for i in range(1,len(prices)):
tmp = prices[i] - prices[i-1]
if tmp > 0:
profile += tmp
return profile
#84 ms;14.9 MB
12、多数元素
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例:
输入: [2,2,1,1,1,2,2]
输出: 2
12.1、方法
使用 set() 去掉重复元素,然后使用 count() 函数统计个数再去比较即可。
def majorityElement(nums: List[int]) -> int:
setlist = set(nums)
for n in setlist:
if nums.count(n) > len(nums) // 2:
return n
#64 ms;15 MB
13、旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
说明:
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 要求使用空间复杂度为 O(1) 的 原地 算法。
13.1、方法一:暴力法
每次只旋转一次。我们先把最后一个元素记录下来,然后就想相当于把其他元素向后移动一位。最后把最后一个元素赋值到首位即可。
for i in range(k):
last = nums[len(nums) - 1]
for j in range(len(nums)-1,-1,-1):
nums[j] = nums[j-1]
nums[0] = last
#超出时间限制
14、存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果任意一值在数组中出现至少两次,函数返回 true
。如果数组中每个元素都不相同,则返回 false
。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
14.1、方法一:暴力法
遍历检查每个元素的出现的个数:
def containsDuplicate(nums: List[int]) -> bool:
setlist = set(nums)
for n in setlist:
if nums.count(n) > 1:
return True
return False
#超出时间限制
14.2、方法二:排序
因为题目说明至少两个,那我们只要检查到有相邻的两个元素相等,就返回 True 即可。
def containsDuplicate(nums: List[int]) -> bool:
nums.sort()
for i in range(len(nums)-1):
if nums[i] == nums[i+1]:
return True
return False
#36 ms; 16.9 MB
#注:sort() 函数是就地排序,返回值为 None
#sorted() 函数的返回值是副本,是一个列表
15、存在重复元素II
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。
示例 1:
输入: nums = [1,2,3,1], k = 3
输出: true
示例 2:
输入: nums = [1,2,3,1,2,3], k = 2
输出: false
15.1、方法一:暴力法
最直接的思想就是两个 for 循环,在元素之后查找与之相同的元素,获得索引差。
15.2、方法二:哈希查找
我们用哈希表存储,把索引作为值,把元素作为键。如果出现相同的键,说明元素值相同,那么就比较两个值(索引的差的绝对值)是否小于等于 k。因为距离至多为 k,也就是最近的距离,因此,如果不满足,我们就可以直接把对应的值换成最近遍历到的索引。
def containsNearbyDuplicate(nums: List[int], k: int) -> bool:
haspmap = {}
for i,num in enumerate(nums):
if haspmap.get(num) is not None:
if abs(i - haspmap.get(num)) <= k:
return True
haspmap[num] = i
return False
#68 ms;21.2 MB
16、移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
16.1、方法
采用双指针,j 为遍历指针,i 指针指示不为 0 的元素交换到的位置。最后,从 i 指针的位置到列表末尾的元素全部置为 0 即可。
def moveZeroes(nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
i,j = 0,0
for j in range(len(nums)):
if nums[j] != 0:
nums[i] = nums[j]
i += 1
for k in range(i,len(nums)):
nums[k] = 0
#44 ms;14.1 MB
17、第三大的数
给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是 O(n)。
示例:
输入: [3, 2, 1]
输出: 1
解释: 第三大的数是 1.
示例:
输入: [1, 2]
输出: 2
解释: 第三大的数不存在, 所以返回最大的数 2 .
示例:
输入: [2, 2, 3, 1]
输出: 1
解释: 注意,要求返回第三大的数,是指第三大且唯一出现的数。
存在两个值为2的数,它们都排第二。
17.1、方法一:一般排序
先使用 set 函数将重复元素去掉,然后再转换为列表进行排序;
如果列表长度 <= 2;则直接返回最大的数即可;
如果列表长度 > 2;则返回第三个位置的函数即可。
但注意的是:排序的时间复杂度不可能为 O(n)。
def thirdMax(nums: List[int]) -> int:
listnew = list(set(nums))
listnew.sort(reverse=True)
lenlistnew = len(listnew)
if lenlistnew == 0:
return 0
elif lenlistnew <= 2:
return listnew[0]
else:
return listnew[2]
#76 ms;14.8 MB
17.2、方法二:堆排序
- 遍历一次创建堆;
- 如果堆的长度大于 3;则返回最大的 3 个数,接着返回第 3 个位置的数即可;如果堆的长度小于 3,则直接返回最大的那个数。
from heapq import *
class Solution:
def thirdMax(self, nums: List[int]) -> int:
heap = []
setnums = set(nums)
for n in setnums:
heappush(heap,n)
if len(heap) >= 3:
return nlargest(3,heap)[2]
else:
return nlargest(1,heap)[0]
#64 ms;14.9 MB
18、找到所有数组中消失的数字
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[5,6]
18.1、方法
我们构建一个哈希表,把元素插入到哈希表中,有重复的也不要紧,会直接覆盖,最后查找哈希表,把没在里边的元素添加到结果中。当然这个也可以使用 set。
def findDisappearedNumbers(nums: List[int]) -> List[int]:
hash_table = {}
for num in nums:
hash_table[num] = 1
result = []
for num in range(1, len(nums) + 1):
if num not in hash_table:
result.append(num)
return result
#472 ms;22.7 MB
19、最大连续 1 的个数
给定一个二进制数组, 计算其中最大连续1的个数。
示例:
输入: [1,1,0,1,1,1]
输出: 3
解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3.
注意:
- 输入的数组只包含
0
和1
。 - 输入数组的长度是正整数,且不超过 10,000。
19.1、方法一
用一个计数器 count 统计 1 的数量,另一个计数器 maxcount 统计当前最大连续 1 的个数:
- 当前数字为 1 时,count + 1;
- 当前数字不为 1 时:
- 将 count 与 maxcount 比较,取其中较大的;
- 将 count 置为 0。
def findMaxConsecutiveOnes(nums: List[int]) -> int:
count = max_count = 0
for num in nums:
if num == 1:
count += 1
else:
max_count = max(max_count,count)
count = 0
return max(max_count,count)
#440 ms;14 MB
19.2、方法二
将数字转化为字符串,在 0 处分隔,获取的子串最大长度就是连续 1 的长度。
def findMaxConsecutiveOnes(nums: List[int]) -> int:
strnums = ''.join(map(str,nums))
return max(map(len,strnums.split('0')))
#516 ms;14.4 MB
20、斐波那契数
斐波那契数,通常用 F(n)
表示,形成的序列称为斐波那契数列。该数列由 0
和 1
开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
给定 N
,计算 F(N)
。
示例:
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1.
20.1、方法
递归。
def fib(N: int) -> int:
if N == 0:
return 0
elif N == 1:
return 1
else:
return fib(N-1) + fib(N-2)
#820 ms;13.5 MB
21、数组中的 K-diff 数对
给定一个整数数组和一个整数 k, 你需要在数组里找到不同的 k-diff 数对。这里将 k-diff 数对定义为一个整数对 (i, j), 其中 i 和 j 都是数组中的数字,且两数之差的绝对值是 k。
示例:
输入: [3, 1, 4, 1, 5], k = 2
输出: 2
解释: 数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。
尽管数组中有两个1,但我们只应返回不同的数对的数量。输入:[1, 2, 3, 4, 5], k = 1
输出: 4
解释: 数组中有四个 1-diff 数对, (1, 2), (2, 3), (3, 4) 和 (4, 5)。
说明:
- 数对 (i, j) 和数对 (j, i) 被算作同一数对;
- 数组的长度不超过10,000;
- 所有输入的整数的范围在 [-1e7, 1e7]。
21.1、方法
计数即可。
def findPairs(nums: List[int], k: int) -> int:
res = 0
c = collections.Counter(nums)
for i in c:
if k > 0 and i + k in c or k == 0 and c[i] > 1:
res += 1
return res
22、数组拆分 I
给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。
示例:
输入: [1,4,3,2]
输出: 4
解释: n 等于 2, 最大总和为 4 = min(1, 2) + min(3, 4)
22.1、方法
想要使得从1 到 n 的 min(ai, bi) 总和最大,则可以先排序,然后相邻两个元素组合。
def arrayPairSum(nums: List[int]) -> int:
nums.sort()
res = 0
for i in range(0,len(nums) - 1,2): #注意步阶
res += min(nums[i],nums[i+1])
return res
#420 ms;16.3 MB
23、最短无序连续子数组
给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
你找到的子数组应是最短的,请输出它的长度。
示例:
输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
23.1、方法
排序即可。找出最前面和最后面第一个不同的元素的位置。
def findUnsortedSubarray(nums: List[int]) -> int:
start,end = 0,0
numssort = sorted(nums)
if numssort == nums: #注意特殊情况
return 0
for i in range(len(nums)):
if nums[i] != numssort[i]:
start = i
break
for i in range(len(nums)-1,-1,-1):
if nums[i] != numssort[i]:
end = i
break
return abs(end-start)+1
#244 ms;15 MB
24、种花问题
假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。
示例:
输入: flowerbed = [1,0,0,0,1], n = 1
输出: True
24.1、方法
连续三个 0 就可以种一盆花。
def canPlaceFlowers(flowerbed: List[int], n: int) -> bool:
flowerbed = [0] + flowerbed + [0]
for i in range(1,len(flowerbed)-1):
if flowerbed[i] == flowerbed[i+1] == flowerbed[i-1] == 0:
n -= 1
flowerbed[i] = 1
return n <= 0
#192 ms;13.8 MB