1 斐波那契(剑指offer10)
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13,特别指出:第0项是0,第1项是第一个1。
从第三项开始,每一项都等于前两项之和。
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
方法1:递归
递归法:
原理: 把 f(n) 问题的计算拆分成f(n−1) 和f(n−2) 两个子问题的计算,并递归,以 f(0) 和 f(1) 为终止条件。
缺点: 大量重复的递归计算,例如 f(n) 和f(n−1) 两者向下递归需要 各自计算f(n−2) 的值。
class Solution:
def fib(self, n: int) -> int:
a,b=0,1
for i in range(n):
a,b=b,a+b
return a
方法2:动态规划
动态规划解析:
1、状态定义: 设 dp 为一维数组,其中 dp[i] 的值代表 斐波那契数列第i个数字 。
2、转移方程: dp[i+1]=dp[i]+dp[i−1] ,即对应数列定义 f(n+1)=f(n)+f(n-1);
3、初始状态:dp[0]=0, dp[1]=1 ,即初始化前两个数字;
4、返回值:dp[n] ,即斐波那契数列的第 n 个数字。
空间复杂度降低:
1、若新建长度为 n 的 dp 列表,则空间复杂度为 O(N) 。
2、由于 dp 列表第 i 项只与第 i−1 和第 i−2 项有关,因此只需要初始化三个整形变量 sum, a, b ,利用辅助变量 sum 使 a, b 两数字交替前进即可 (具体实现见代码) 。
3、节省了 dp 列表空间,因此空间复杂度降至 O(1) 。
循环求余法:
大数越界:随着 n 增大, f(n) 会超过 Int32 甚至 Int64 的取值范围,导致最终的返回值错误。
求余运算规则:设正整数 x, y, p, 求余符号为⊙ ,则有(x+y)⊙p=(x⊙p+y⊙p)⊙p 。
解析: 根据以上规则,可推出f(n)⊙p=[f(n−1)⊙p+f(n−2)⊙p]⊙p ,从而可以在循环过程中每次计算sum=(a+b)⊙1000000007 ,此操作与最终返回前取余等价。
复杂度分析:
时间复杂度 O(n) : 计算 f(n) 需循环 n 次,每轮循环内计算操作使用 O(1) 。
空间复杂度 O(1) : 几个标志变量使用常数大小的额外空间。
class Solution:
def fib(self, n: int) -> int:
a, b = 0, 1
for _ in range(n):
a, b = b, (a + b) % 1000000007
return a
2 二分查找
# 二分搜索是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;
# 如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。
# 这种搜索算法每一次比较都使搜索范围缩小一半。
def binarySearch(nums, target):
if len(nums) == 0:
return -1
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target: # 向右查找
left = mid + 1
else:
right = mid - 1 # 向左查找
# 数据不存在,返回-1
return -1
re = binarySearch(nums=[-1, 0, 3, 5, 9, 12], target=2)
print(re)
3 无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
解法1:动态规划+哈希表
状态定义: 设动态规划列表 dp ,dp[j] 代表以字符 s[j] 为结尾的 “最长不重复子字符串” 的长度。
转移方程: 固定右边界 j ,设字符 s[j] 左边距离最近的相同字符为 s[i] ,即 s[i] = s[j] 。
1.当 i < 0 ,即 s[j] 左边无相同字符,则 dp[j] = dp[j-1] + 1 ;
2.当 dp[j-1]<j-i ,说明字符 s[i] 在子字符串 dp[j-1]区间之外 ,则 dp[j] = dp[j-1]+1;
3.当 dp[j−1]≥j−i ,说明字符 s[i] 在子字符串 dp[j−1]区间之中 ,则 dp[j] 的左边界由 s[i] 决定,即 dp[j]=j−i ;
故:当 i < 0时,由于dp[j−1]≤j 恒成立,因而dp[j−1]<j−i 恒成立,因此分支 1. 和 2. 可被合并。
返回值:max(dp) ,即全局的 “最长不重复子字符串” 的长度。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
dic = {}
res = tmp = 0
for j in range(len(s)):
i = dic.get(s[j], -1) #
dic[s[j]] = j #
tmp = tmp + 1 if tmp < j - i else j - i # dp[j - 1] -> dp[j]
res = max(res, tmp) # max(dp[j - 1], dp[j])
return res
解法2:滑动窗口
class Solution2:
def lengthOfLongestSubstring(self, s: str) -> int:
# 哈希集合,记录每个字符是否出现过
occ = set()
n = len(s)
# 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
rk, ans = -1, 0
for i in range(n):
if i != 0:
# 左指针向右移动一格,移除一个字符
occ.remove(s[i - 1])
while rk + 1 < n and s[rk + 1] not in occ:
# 不断地移动右指针
occ.add(s[rk + 1])
rk += 1
# 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i + 1)
return ans
解法3:动态规划+双指针
哈希表 dic 统计: 指针 j 遍历字符 s ,哈希表统计字符 s[j] 最后一次出现的索引 。
更新左指针 i : 根据上轮左指针 i和 dic[s[j]] ,每轮更新左边界 i ,保证区间 [i+1,j] 内无重复字符且最大。
i=max(dic[s[j]],i)
更新结果res :取上轮res和本轮双指针区间[i+1,j] 的宽度(即j−i)中的最大值。
res=max(res,j−i)
复杂度分析:
时间复杂度 O(N) : 其中 N 为字符串长度,动态规划需遍历计算 dp 列表。
空间复杂度 O(1) : 字符的 ASCII 码范围为 0 ~ 127 ,哈希表 dic 最多使用O(128)=O(1) 大小的额外空间。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
dic, res, i = {}, 0, -1
for j in range(len(s)):
if s[j] in dic:
i = max(dic[s[j]], i) # 更新左指针 i
dic[s[j]] = j # 哈希表记录
res = max(res, j - i) # 更新结果
return res
4 整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
示例 1:
输入:x = 123
输出:321
示例 2:
输入:x = -123
输出:-321
示例 3:
输入:x = 120
输出:21
示例 4:
输入:x = 0
输出:0
class Solution:
def reverse(self, x: int) -> int:
str1 = str(x)
if str1[0] == '-':
str1 = str1[0] + str1[:0:-1]
a = int(str1)
if (1 << 31) < abs(a):
return 0
else:
str1 = str1[::-1]
a = int(str1)
if a > (1 << 31) - 1:
return 0
return a
5 丑数问题:判断数据是否为丑数
丑数:编写一个程序判断给定的数是否为丑数。 丑数就是只包含质因数 2, 3, 5 的正整数。
class Soluation:
def chouShu(self, num: int):
if num <= 0:
return False
focues = [2, 3, 5]
for focue in focues:
while num % focue == 0: # 取模 - 返回除法的余数
num //= focue # 取整除 - 向下取接近商的整数 c //= a 等效于 c = c // a
return num == 1
6 丑数问题:找出第几个丑数
编写一个程序,找出第 n 个丑数。
丑数就是质因数只包含 2, 3, 5 的正整数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:
1 是丑数。
n 不超过1690。
class Solution:
def nthUglyNumber(self, n: int) -> int:
dp, a, b, c = [1] * n, 0, 0, 0
for i in range(1, n):
n2, n3, n5 = dp[a] * 2, dp[b] * 3, dp[c] * 5
dp[i] = min(n2, n3, n5)
if dp[i] == n2: a += 1
if dp[i] == n3: b += 1
if dp[i] == n5: c += 1
return dp[-1]
7 回文字符串
给定一个字符串,判断该字符串中是否可以通过重新排列组合,形成一个回文字符串。
示例 1:
输入: "code"
输出: false
示例 2:
输入: "aab"
输出: true
示例 3:
输入: "carerac"
输出: true
class Solution:
def canPermutePalindrome(self, s: str) -> bool:
dic = {} # 创建一个字典 {'a':2,'b':1}
for c in s: # 值在字符串中循环
if dic.get(c) == None: # 如果指定的k-v值为空
dic[c] = 1
else:
dic[c] += 1 # 将value值写入dic字典
count = 0
for num in dic.values(): #返回一个迭代器,可以使用 list() 来转换为列表
if num % 2 == 0: # 如果num值可以被2整除
continue
else: # 如果num值不能被2整除
count += 1 #循环+1
if count > 1:
return False
return True
8 回文排列
给定一个字符串 s ,返回其通过重新排列组合后所有可能的回文字符串,并去除重复的组合。
如不能形成任何回文排列时,则返回一个空列表。
示例 1:
输入: "aabb"
输出: ["abba", "baab"]
示例 2:
输入: "abc"
输出: []
class Solution:
def generatePalindromes(self, s: str) -> List[str]:
dic = collections.Counter(s)
odd, h = [], ''
for k in dic:
if dic[k]%2 == 1: odd.append(k)
h += k*(dic[k]//2)
if len(odd) > 1: return []
def dfs(cur, path):
if not cur:
res.add(path)
for i in range(len(cur)):
if i == 0 or cur[i] != cur[i-1]:
dfs(cur[:i] + cur[i+1:], path + cur[i])
res = set()
dfs(h, '')
return [p + p[::-1] for p in res] if not odd else [p + odd[0] + p[::-1] for p in res]
9 寻找丢失的数字
给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。
示例 1:
输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 2:
输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。
class Solution:
def missingNumber(self, nums: List[int]) -> int:
return int(len(nums)*(len(nums)+1)/2-sum(nums))
10 获取正整数在Excel中对应的列名称
给定一个正整数,返回它在 Excel 表中相对应的列名称。
示例 1:
输入: 1
输出: "A"
示例 2:
输入: 28
输出: "AB"
示例 3:
输入: 701
输出: "ZY"
class Solution:
def convertToTitle(self, n: int) -> str:
res=''
while n:
res=chr((n-1)%26+ord('A'))+res
n=(n-1)//26
return res
11 获取两数之间的所有组合
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
from itertools import combinations
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
nums=[1,2,3,4,5,6,7,8,9]
res=[]
for i in range(len(nums)):
res += list(combinations(nums,i))
res=[x for x in res if len(x) == k]
a=[]
for j in res:
if sum(j) ==n :
a.append(list(j))
return a
12 数组题目1
给定一个二进制数组, 计算其中最大连续 1 的个数。
示例:
输入:[1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.
提示:
输入的数组只包含 0 和 1 。
输入数组的长度是正整数,且不超过 10,000。
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
count,result=0,0 # 计数器
for i in range(len(nums)):
if nums[i]==1: # 遍历列表,当值为1,count的值加1
count+=1
else:
result=max(count,result) # 当值不为1,获取最大的值
count=0 # 将计数器赋值为0,从新计数
return max(result,count)
13 数组题目2
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
index=0
for i in range(len(nums)):
if nums[i] !=0: # 当num[i]不为0
nums[index]=nums[i] # num[index]赋值为num[i]
index +=1
for j in range(len(nums)): #遍历列表
if j>=index: # 当j的值大于index的值,num[j]赋值为0
nums[j]=0
return nums
14 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
解题方法1:
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
if target in nums:
for i in range(len(nums)):
if nums[i]==target:
return i
else:
if target<nums[0]:
return 0
elif target>nums[-1]:
return len(nums)
else:
for i in range(len(nums)-1):
if nums[i]<target<nums[i+1]:
return i+1
解题方法2:
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
nums.append(target) # 直接将元素加入到数组
nums.sort() # 对数组进行排序
return nums.index(target) #获取数据的元素的下标
15 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
prev=temp=ListNode() # prev为指针,temp为临时
temp.next=head # 临时值的下一个为头节点
while head is not None: # 当head不为空时
if head.val==val: #如果head的值等于val
prev.next=head.next # prev的下一个值等于head的下一个值
else: # 如果head的值不等于val
prev=prev.next # prev赋值给prev的下一个值
head=head.next
return temp.next
16 反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
n1,n2=head,None
while n1:
temp=n1.next # temp临时指针 赋值给head下个值
n1.next=n2 # n1.next=n2
n2=n1 # n2=n1
n1=temp # n1=temp temp: [2,3]
return n2
17 双指针
题目:输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例1:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def mergeTwoLists(self, L1: ListNode, L2: ListNode) -> ListNode:
cur=tep=ListNode(0)
while L1 and L2:
if L1.val<L2.val:
cur.next,L1=L1,L1.next
else:
cur.next,L2=L2,L2.next
cur=cur.next
cur.next = L1 if L1 else L2
# Python 三元表达式写法 A if x else B ,代表当 x = True 时执行 A ,否则执行 B
return tep.next
18 交替打印1-100之间的整数
# 交替打印1,2,3,4,5...100之间的整数
import threading
import time
def threadA():
"""打印奇数"""
for i in range(1, 100 + 1):
if i % 2 != 0:
lockb.acquire()
print(i),
locka.release()
time.sleep(0.1)
def threadB():
"""打印偶数"""
for i in range(1, 100 + 1):
if i % 2 == 0:
locka.acquire()
print(i),
lockb.release()
time.sleep(0.1)
if __name__ == "__main__":
locka = threading.Lock()
lockb = threading.Lock()
ta = threading.Thread(None, threadA)
tb = threading.Thread(None, threadB)
locka.acquire() # 保证a先执行
ta.start()
tb.start()
ta.join()
19 股票的最大利润
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例 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。
方法1:暴力法
class Solution:
def maxProfit(self, 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
方法2:动态规划
设共有 n 天,第 a 天买,第 b 天卖,则需保证 a<b ;可推出交易方案数共有:
(n−1)+(n−2)+⋯+2+1=n(n−1)/2
因此,暴力法的时间复杂度为 O(n^2) 。考虑使用动态规划降低时间复杂度,以下按照流程解题。
动态规划解析:
状态定义: 设动态规划列表 dp ,dp[i] 代表以 prices[i] 为结尾的子数组的最大利润(以下简称为前 i 日的最大利润 )。
转移方程: 由于题目限定 “买卖该股票一次” ,
因此前 i 日最大利润 dp[i] 等于前i−1 日最大利润 dp[i-1]和第 i 日卖出的最大利润中的最大值。
前i日最大利润=max(前(i−1)日最大利润,第i日价格−前i日最低价格)
dp[i]=max(dp[i−1],prices[i]−min(prices[0:i]))
初始状态:dp[0]=0 ,即首日利润为 0 ;
返回值: dp[n−1] ,其中 n 为 dp 列表长度。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
minprice,maxprofit= int(1e9),0 # 10的9次方
for price in prices:
minprice = min(price, minprice) # 获取最低价格
maxprofit = max(price - minprice, maxprofit) #获取最大利润
return maxprofit
20 股票最大利润II
给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: prices = [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: prices = [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
贪心算法 股票买卖策略:
- 单独交易日: 设今天价格为P1、明天价格为P2,则今天买入、明天卖出可赚取收益为P2-P1(负值代表亏损)。
- 连续上涨交易日: 设此上涨交易日股票价格分别为P1、P2...Pn,则第一天买入最后一天卖出收益最大,即为:Pn-P1,等价于每天都买卖,即 Pn-P1=(P2-P1)+(P3-P2)+...+[Pn-P(n-1)]
- 连续下降交易日: 则不买卖收益最大,即不会亏钱。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
profit = 0
for i in range(1, len(prices)):
tmp = prices[i] - prices[i-1]
if tmp > 0: profit += tmp
return profit
21 面试题-最长的连续子串
"""
一个正数整数的数组,找出其中最长的连续子串
例如:数组[1,2,4,3,4,5,7]中最长连续子串是[3,4,5]
思路:
1、栈A:A用于存储原始元素
2、栈B:B中存储A中所有 严格升序的元素的子序列。则栈A中的最大元素始终对应栈B中栈顶元素,
借助栈的思想:nums为数据栈A,stack为辅助栈B
(1)当栈B为空时,将nums[i]写入栈B
(2)栈B的栈顶元素+1等于nums[i],将nums[i]写入栈B
(3)不相等时,清空栈B中的元素
"""
class StackNode:
def maxSubArray(self, nums: list):
stack, res = [], []
for i in range(0, len(nums)):
if not stack or stack[-1] + 1 == nums[i]:
stack.append(nums[i])
else:
res = stack
stack = []
return res
# 方法二:滑动窗口思想
def maxArray(nums: list):
"""滑动窗口"""
if len(nums) <= 1: return nums
left, right, win, count = 0, 1, [], 0 # 初始化窗口长度为1,保证left<right,win窗口,count窗口长度
win.append(nums[left]) # 循环将left的值添加到窗口中
while right < len(nums):
if nums[left] + 1 == nums[right]: # 循环,如果左边界的值+1=右边界的值,窗口中新增右边界的值
win.append(nums[right])
left, right = left + 1, right + 1
if len(win) > count: # 如果窗口的长度大于count,res等于win,count的值变化为win的窗口长度
count, res = len(win), win.copy()
else: # 如果不等于,窗口置空,将有边界值加入到窗口,此时左边界等于右边界,右边界右移1位
win = []
win.append(nums[right])
left, right = right, right + 1
return res
22 三数之和(力扣15)
"""
给你一个包含 n 个整数的数组nums,判断nums中是否存在三个元素 a,b,c ,使得a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
思路:三数之和为0,即a+b+c=0,得c=-(a+b)
设最左边指针为k,双指针i,j分别为数组索引(k,len(nums)两端,双指针交替交替向中间移动。记录对于每个固定指针k的所有满足nums[k]+nums[i]+nums[j]==0的i,j组合
"""
class Solution:
def threeSum(self, nums: list):
nums.sort() # 按照从大到小排序,保证左边指针k为最小
k, res = 0, [] # 初始化为0,nums[0]
for k in range(len(nums) - 2): # 减掉2个元素
if nums[k] > 0:
break # 如果最小的值大于0,则表示三个值都为正数,跳出
if k > 0 and nums[k] == nums[k - 1]: # 循环k且去重
continue
i, j = k + 1, len(nums) - 1
while i < j:
sum = nums[k] + nums[i] + nums[j]
if sum < 0:
i += 1 # 如果三数之和小于0,则i的值递增1
elif sum > 0:
j -= 1 # 如果三数之和大于0,则j的值递减1
else:
res.append([nums[k], nums[i], nums[j]])
i, j = i + 1, j - 1
while i < j and nums[i] == nums[i - 1]: i += 1
while i < j and nums[j] == nums[j + 1]: j -= 1
return res
23 四数之和(力扣18)
"""
给你一个由 n 个整数组成的数组nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组[nums[a], nums[b], nums[c], nums[d]](若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d< n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
"""
class Solution:
def fourSum(self, nums: list, target: int):
nums.sort()
m, res = 0, []
for m in range(len(nums) - 3):
if m > 0 and nums[m] == nums[m - 1]: continue
for n in range(m + 1, len(nums) - 2):
if n > m + 1 and nums[n] == nums[n - 1]: continue
i, j = n + 1, len(nums) - 1
while i < j:
su = nums[m] + nums[n] + nums[i] + nums[j]
if su < target:
i += 1
elif su > target:
j -= 1
else:
res.append([nums[m], nums[n], nums[i], nums[j]])
i, j = i + 1, j - 1
while i < j and nums[i] == nums[i - 1]: i += 1
while i < j and nums[j] == nums[j + 1]: j -= 1
return res
24 合并2个有序数组(力扣88)
# 使用指针合并两个数组
arr1 = [1, 8, 4, 6, 7]
arr2 = [2, 5, 8, 9, 10]
class Solution:
def solu1(self, arr1, arr2):
ind = 0
ans = arr1.copy()
for i in range(0, len(arr2)):
while ind < len(arr1):
if arr2[i] <= arr1[ind]: # 范围小于数组的元素下标的最大值
ans.insert(ind + i, arr2[i]) # 向第一非数组中插入第二个数组中的数字
break # 跳出当前循环
else:
ind += 1 # ind指向的数字小于i指向的数字,ind向后移动一位
else:
ans = ans + arr2[i:] # ans加上arr2中剩余的元素
break
return ans
def solu2(self, arr1, arr2):
"""循环"""
an = arr2.copy()
for i in range(0, len(arr1)):
for j in range(0, len(arr2)):
if arr1[i] <= arr2[j]:
an.insert(i + j, arr1[i])
break
else:
j += 1
# an.sort(reverse=False) # 排序sort
return an
def merge(self, arr1, arr2):
"""双指针"""
sorted, p1, p2, m, n = [], 0, 0, len(arr1), len(arr2)
while p1 < m or p2 < n:
if p1 == m:
sorted.append(arr2[p2])
p2 += 1
elif p2 == n:
sorted.append(arr1[p1])
p1 += 1
elif arr1[p1] < arr2[p2]:
sorted.append(arr1[p1])
p1 += 1
else:
sorted.append(arr2[p2])
p2 += 1
arr1[:] = sorted
return arr1
25 根据前序和中序构建二叉树(力扣105)
class Node:
def __init__(self, data, left, right):
self.data = data
self.left = left
self.right = right
def buildTree(pre_order, mid_order):
# 忽略参数合法性判断
if len(pre_order) == 0:
return None
# 前序遍历的第一个结点一定是根结点
root_data = pre_order[0]
for i in range(0, len(mid_order)):
if mid_order[i] == root_data:
break
# 递归构造左子树和右子树
left = buildTree(pre_order[1: 1 + i], mid_order[:i])
right = buildTree(pre_order[1 + i:], mid_order[i + 1:])
return Node(root_data, left, right)
26 分发糖果(力扣135)
"""
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。你需要按照以下要求,给这些孩子分发糖果:每个孩子至少分配到 1 个糖果。相邻两个孩子评分更高的孩子会获得更多的糖果。请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
示例1:
输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
示例2:
输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。
思路:以中间为准,每个孩子必须比左边的评分高,多分1个,每个孩子必须比右边的评分高,多分1个
"""
class Solution:
def candy(self, ratings: list) -> int:
n, ans, num = len(ratings), 0, [1] * len(ratings)
for i in range(1, n): # 顺时针循环,不包括第0位元素
if ratings[i] > ratings[i - 1]:
num[i] = num[i - 1] + 1
# ans += num[i]
for i in range(n - 1, 0, -1): # 逆时针循环,不包括第0位元素
if ratings[i - 1] > ratings[i]:
num[i - 1] = max(num[i - 1], num[i] + 1)
ans += num[i]
return ans + num[0] # 返回结果加上第0位元素
27 爱生气的书店老板(力扣1052)
"""
今天,书店老板有一家店打算试营业customers.length分钟。每分钟都有一些顾客(customers[i])会进入书店,所有这些顾客都会在那一分钟结束后离开。
在某些时候,书店老板会生气。 如果书店老板在第 i 分钟生气,那么 grumpy[i] = 1,否则 grumpy[i] = 0。 当书店老板生气时,那一分钟的顾客就会不满意,不生气则他们是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续X 分钟不生气,但却只能使用一次。请你返回这一天营业下来,最多有多少客户能够感到满意。
示例:
输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], x = 3
输出:16
解释:
书店老板在最后 3 分钟保持冷静。
感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.
"""
class Solution:
def maxSatisfied(self, customers: list, grumpy: list, x: int):
ret = 0 # 计数器
# 遍历获得不生气时的客户数
for i in range(len(grumpy)):
if grumpy[i] == 0:
ret += customers[i]
customers[i] = 0 # customers = [0,0,0,2,0,1,0,5]
sumCount = sum(customers[:x])
res = sumCount
# 滑动窗口获取最大的客户满意数
for i in range(x, len(customers)):
sumCount -= customers[i - x] - customers[i]
res = max(res, sumCount)
return (res + ret)
28 可获得的最大点数(力扣1423)
"""
几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。你的点数就是你拿到手中的所有卡牌的点数之和。给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。
示例 1:
输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。
"""
class Solution:
def maxPoints(self, cardPoints: list, k=3):
n = len(cardPoints)
# 滑动窗口大小为 n-k
windowSize = n - k
# 选前 n-k 个值作为窗口的初始值
s = sum(cardPoints[:windowSize])
minSum = s
for i in range(windowSize, n):
# 滑动窗口每向右移动一格,增加从右侧进入窗口的元素值,并减少从左侧离开窗口的元素值
s += cardPoints[i] - cardPoints[i - windowSize]
minSum = min(minSum, s)
return sum(cardPoints) - minSum
29 定长子串中元音的最大数目(力扣1456)
"""
给你字符串 s 和整数 k 。请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。英文中的 元音字母 为(a, e, i, o, u)。
示例 1:
输入:s = "abciiidef", k = 3
输出:3
解释:子字符串 "iii" 包含 3 个元音字母。
"""
class Solution:
def maxVowels(self, s: str, k: int):
if not s or len(s) < k: return -1
vowels, count = {"a", "e", "i", "o", "u"}, 0
for i in range(k):
if s[i] in vowels:
count += 1
res = count
for i in range(k, len(s)):
if s[i - k] in vowels:
count -= 1 # 去除重复部分的计数
if s[i] in vowels:
count += 1
res = max(count, res)
return res
30 给定行和列的和,求可行矩阵(力扣1605)
"""
给你两个非负整数数组rowSum 和colSum,其中rowSum[i]是二维矩阵中第 i行元素的和, colSum[j]是第 j列元素的和。换言之你不知道矩阵里的每个元素,但是你知道每一行和每一列的和。请找到大小为rowSum.length x colSum.length的任意 非负整数矩阵,且该矩阵满足rowSum 和colSum的要求。请你返回任意一个满足题目要求的二维矩阵,题目保证存在 至少一个可行矩阵。
示例 1:
输入:rowSum = [3,8], colSum = [4,7]
输出:[[3,0],
[1,7]]
解释:
第 0 行:3 + 0 = 3 == rowSum[0]
第 1 行:1 + 7 = 8 == rowSum[1]
第 0 列:3 + 1 = 4 == colSum[0]
第 1 列:0 + 7 = 7 == colSum[1]
行和列的和都满足题目要求,且所有矩阵元素都是非负的。
另一个可行的矩阵为:[[1,2],
[3,5]]
"""
class Solution:
def restoreMatrix(self, rowSum: list, colSum: list):
row, col = len(rowSum), len(colSum)
matrix = [[0] * col for _ in range(row)] # matrix=[行][列]
for i in range(row):
for j in range(col):
min_num = min(rowSum[i], colSum[j]) # 获取最小值
matrix[i][j] = min_num
rowSum[i] -= min_num # 获取每行最大的值
colSum[j] -= min_num # 获取每列最大的值,递减
if rowSum[i] == 0:
break
return matrix
31 卡车上的最大单元数(力扣1710)
"""
请你将一些箱子装在 一辆卡车 上。给你一个二维数组 boxTypes ,其中 boxTypes[i] = [numberOfBoxesi, numberOfUnitsPerBoxi] :
numberOfBoxesi 是类型 i 的箱子的数量。
numberOfUnitsPerBoxi 是类型 i每个箱子可以装载的单元数量。
整数 truckSize 表示卡车上可以装载 箱子 的 最大数量 。只要箱子数量不超过 truckSize ,你就可以选择任意箱子装到卡车上。
返回卡车可以装载单元 的 最大 总数。
示例 1:
输入:boxTypes = [[1,3],[2,2],[3,1]], truckSize = 4
输出:8
解释:箱子的情况如下:
- 1 个第一类的箱子,每个里面含 3 个单元。
- 2 个第二类的箱子,每个里面含 2 个单元。
- 3 个第三类的箱子,每个里面含 1 个单元。
可以选择第一类和第二类的所有箱子,以及第三类的一个箱子。
单元总数 = (1 * 3) + (2 * 2) + (1 * 1) = 8
示例 2:
输入:boxTypes = [[5,10],[2,5],[4,7],[3,9]], truckSize = 10
输出:91
解释:
- 5个第一类箱子,每个里面含有10个单元
- 2个第二类箱子,每个里面含有5个单元
- 4个第三类箱子,每个里面含有7个单元
- 3个第四类箱子,每个里面含有9个单元
(5*10)+(3*9)+((10-5-3)*7)=14+27+50=91
"""
class Solution:
def maxUnits(self, boxTypes: list, truckSize: int):
boxTypes.sort(key=lambda x: x[1], reverse=True) # 按照二维数组的第二个元素进行排序,倒序
mas = 0
for i in range(len(boxTypes)):
if boxTypes[i][0] <= truckSize:
truckSize = truckSize - boxTypes[i][0]
mas += boxTypes[i][0] * boxTypes[i][1]
elif boxTypes[i][0] > truckSize and truckSize > 0:
mas += truckSize * boxTypes[i][1]
truckSize = 0
return mas
32 最大公约数(力扣1979)
"""
给你一个整数数组 nums ,返回数组中最大数和最小数的 最大公约数 。两个数的最大公约数是能够被两个数整除的最大正整数。
示例 1:
输入:nums = [2,6,5,9,10]
输出:2
解释:
nums 中最小的数是 2
nums 中最大的数是 10
2 和 10 的最大公约数是 2
"""
import math
class Solution:
def findGCD(self, nums: list):
# 先找到最大数、最小数,再求最大公约数
nums.sort()
return math.gcd(nums[0], nums[-1])
def findGCD2(self, nums: list):
# 先找到最大数、最小数,再求最大公约数
ma, mi = max(nums), min(nums)
return math.gcd(ma, mi)
33 反转字符串(力扣344)
"""
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
"""
class Solution:
def reverseString(self, s: list):
res = []
for i in range(len(s)):
res.insert(0, s[i])
return res
def reverseString1(self, s: list):
left, right = 0, len(s) - 1
while left < right:
s[left], s[right] = s[right], s[left]
left += 1
right -= 1
while left >= right:
return s
34 删除链表的节点(剑指offer18)
"""
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为5的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
1、初始化:双指针,cur和tmp
2、循环逻辑:当head.val=val,执行移除操作,即改变指向
3、返回值:修改后的head.next
"""
class ListNode:
def __init__(self, val):
self.val = val
self.next = None
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
if head.val == val: return head.next # 如果相等,返回下一个值
tmp, cur = head, head.next # 双指针,tmp前驱节点,cur当前节点,cur.next后继节点
while cur and cur.val != val: # 循环,如果cur为真且cur的值不等于
tmp, cur = cur, cur.next
if cur:
tmp.next = cur.next
return head
35 调整数组顺序使奇数和偶数分开
"""
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。
输入:nums = [1,2,3,4]
输出:[1,3,2,4]
注:[3,1,2,4] 也是正确的答案之一。
"""
"""
算法流程:
1、初始化:i、j为双指针,分别位于数组的左右两端
2、循环交换:当i=j跳出循环
(1)指针i遇到奇数则执行i=i+1,直到找到偶数 (i为最大值,执行递增)
(2)指针j遇到偶数则执行j=j-1,直到找到奇数 (j为最大值,执行递减)
(3)交换nums[i]和nums[j]的值
3、返回值:修改后的nums
"""
class Solution:
def exchange(self, nums: list):
i, j = 0, len(nums) - 1
while i < j:
if i < j and nums[i] % 2 == 1: i += 1
if i < j and nums[j] % 2 == 0: j -= 1
nums[i], nums[j] = nums[j], nums[i]
return nums
36 和为s的两个数字
"""
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
示例 2:
输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]
"""
"""
1、初始化:双指针i和j分别指向数组nums的左右两端(对撞双指针)
2、循环:
(1)如果nums[i]+nums[j]值相等target,返回对应下标的值,
(2)如果两数之和大于target,则表示j需要递减,即j-=1
(3)如果两数之和小于target,则表示i需要递增,即i+=1
3、返回值:[nums[i],nums[j]]
"""
class Solution:
def sumArray(self, nums: list, target: int):
i, j = 0, len(nums) - 1
while i < j:
su = nums[i] + nums[j]
if i < j and su == target:
return [nums[i], nums[j]]
elif i < j and su > target:
j = j - 1
else:
i = i + 1
return False # 如果没有返回False
37 和为s的连续正数序列
"""
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
[1,2,3,4,5,6,7,8,9]
输出:[[2,3,4],[4,5]]
示例 2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
"""
"""
1、初始化:设置左边界i和右边界j,构建滑动窗口从左向右滑动
2、循环:每轮判断滑动窗口内元素和目标值大小关系对比
(1)若相等,则记录,并向右移动左边界i=i+1
(2)若大于,则移动左边界i(减少窗口内的元素和) i=i+1,更新sum元素和
(3)若小于,则移动右边界j(增大窗口内的元素和) j=j+1,更新sum元素和
3、返回值:返回窗口win中记录的值
"""
class Solution:
def findCount(self, target: int):
"""滑动窗口"""
l, r, res, win = 1, 2, 3, [] # 初始值从1和2开始,元素和为3
while l < r:
if res == target:
win.append(list(range(l, r + 1)))
if res >= target:
res = res - l
l = l + 1
else:
r = r + 1
res = res + r
return win
38 翻转单词顺序
"""
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。
示例 2:
输入: " hello world! "
输出:"world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
"""
"""
0、特殊处理:
(1)标点符号翻转,
(2)空格需要处理,返回值不包含空格
1、初始化:
2、循环:
3、返回值:
"""
class Solution:
def reverseWords(self, s: str):
s = s.strip() # 删除空格
i = j = len(s) - 1
res = []
while i >= 0:
while i >= 0 and s[i] != " ": i -= 1
res.append(s[i + 1:j + 1])
while i >= 0 and s[i] == " ": i -= 1
j = i
return ' '.join(res)
def reverse(self, s: str):
s = s.strip()
se = s.split() # 分割
se.reverse()
return ' '.join(se)
39 滑动窗口最大值(剑指offer59)
"""
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
队首 队尾
[1 3 -1] -3 5 3 6 7 3 -1
1 [3 -1 -3] 5 3 6 7 3 -1 -3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5 3
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
"""
import collections
import heapq
class Solution(object):
def maxSlidingWindow(self, nums, k):
if not nums or k == 0: return [] # 如果nums为空或者k为0,返回空列表
deque = collections.deque()
# 如果k的值存在,当窗口下标小于k
for i in range(k):
while deque and deque[-1] < nums[i]: # 如果队列存在,队尾值小于当前值,删除队列中队尾值
deque.pop() # 删掉队尾值 [3,-1]
deque.append(nums[i]) # 在队尾加入当前值nums[i]
res = [deque[0]] # 确保队列中队首的值为最大值
# 如果k的值存在,当窗口下标大于等于k
for i in range(k, len(nums)): # 循环剩余部分
if deque[0] == nums[i - k]: # 如果队首的值等于循环体剩余部分值,删除队首的值
deque.popleft()
while deque and deque[-1] < nums[i]: # 如果队列存在,队尾值小于当前值,删除队列中队尾值
deque.pop()
deque.append(nums[i]) # 在队尾加入当前值nums[i]
res.append(deque[0])
return res
def maxSlidingWindow2(self, nums, k: int):
n = len(nums)
# 注意 Python 默认的优先队列是小根堆
q = [(-nums[i], i) for i in range(k)]
heapq.heapify(q)
ans = [-q[0][0]]
for i in range(k, n):
heapq.heappush(q, (-nums[i], i))
while q[0][1] <= i - k:
heapq.heappop(q)
ans.append(-q[0][0])
return ans
40 二分插入排序(剑指offer68)
"""
给定一个排序的整数数组 nums和一个整数目标值 target ,请在数组中找到target,并返回其下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
输入: nums = [1,3,5,6], target = 5
输出: 2
"""
class Solution:
def binarySearch(self, nums, target):
"""根据原始数据的位置进行替换"""
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return "mid值与target相同,mid为:", mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
nums.insert(left, target)
return nums[target]
41 排序数组中只出现一次的数字(剑指offer70)
"""
给定一个只包含整数的有序数组 nums ,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。
输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2
输入: nums = [3,3,7,7,10,11,11]
输出: 10
"""
import math
class Solution:
def singOne(self, nums: list):
left, right = 0, math.ceil((len(nums)) / 2)
while left <= right:
mid = (left + right) // 2
i = mid * 2
if i < len(nums) - 1 and nums[i] != nums[i + 1]: # 循环,如果不相等
if mid == 0 or nums[i - 2] == nums[i - 1]: # 如果相等,返回值
return nums[i]
left = mid + 1
else:
right = mid - 1
return nums(len(nums) - 1) # 如果没有返回False或者-1
42 最长连续序列(剑指offer119)
"""
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
输入:nums = [100,4,200,1,3,2]
示例1:
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
"""
class Solution:
def maxLength(self, nums: list):
nums.sort()
res = ret = 1 # 初始化值为1
for i in range(1, len(nums)): # 循环,从第1位元素开始
if nums[i] == nums[i - 1]: # 如果相等,直接继续
continue
if nums[i] - nums[i - 1] == 1:
ret += 1
res = max(res, ret)
else:
ret = 1
return res
43 子数组最大平均数1()
"""
给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。任何误差小于 10-5 的答案都将被视为正确答案。
示例 1:
输入:nums = [1,12,-5,-6,50,3], k = 4
输出:12.75
解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
1:[1,12,-5,-6]
2: [12,-5,-6,50]
3: [-5,-6, 50, 3]
循环窗口:往右递增,窗口中元素和=sum-左边界值+右边界值
sum(nums[left:right])/k 窗口中的元素和,再除以K,
示例 2:
输入:nums = [5], k = 1
输出:5.00000
"""
class Solution:
def findMaxAverage(self, nums: list, k: int):
left, right, winsum, res = 0, k - 1, 0, 0
for i in range(k): # 未形成窗口,循环出第一个窗口 [1,12,-5,-6]
winsum += nums[i]
res = winsum
for right in range(k, len(nums)): # 从第一个窗口循环遍历出所有窗口
winsum = winsum + nums[right] - nums[left]
res = max(res, winsum)
left, right = left + 1, right + 1
return (res / k)
44 盛最多水的容器
"""
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点(i,ai) 。在坐标内画 n 条垂直线,垂直线 i的两个端点分别为(i,ai) 和 (i, 0) 。找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为49。
"""
"""
思路:
横坐标x:nums中选择的某段长度
纵坐标y:nums中最大的两个数中最小的值
"""
class Solution:
def maxArea(self, height: list) -> int:
left, right = 0, len(height) - 1 # 定义纵坐标的值 nums[left],nums[right]取最大值,*(right-left)
res = 0
while left < right:
x = min(height[left], height[right]) # 横坐标
y = (right - left) # 纵坐标
res = max(res, x * y)
if x == height[left]: # 如果最小值为左边界,左边界右移1位
left += 1
else: # 如果最小值为右边界,右边界左移1位
right -= 1
return res
45 两地调度
"""
公司计划面试 2n 人。给你一个数组 costs ,其中 costs[i] = [aCosti, bCosti] 。第 i 人飞往 a 市的费用为 aCosti ,飞往 b 市的费用为 bCosti 。返回将每个人都飞到 a 、b 中某座城市的最低费用,要求每个城市都有 n 人抵达。
示例 1:
输入:costs = [[10,20],[30,200],[400,50],[30,20]]
输出:110
解释:
第一个人去 a 市,费用为 10。
第二个人去 a 市,费用为 30。
第三个人去 b 市,费用为 50。
第四个人去 b 市,费用为 20。
最低总费用为 10 + 30 + 50 + 20 = 110,每个城市都有一半的人在面试。
"""
class Solution:
def twoCity(self, costs: list):
# costs.sort(key=lambda x: x[1], reverse=False) # [[10, 20], [30, 20], [400, 50], [30, 200]]
cost, nums, n = 0, list(), len(costs)
for i in range(n):
nums.append([costs[i][1] - costs[i][0], i]) # 将b城的费用减掉a城的费用,得到结果[[10, 0], [170, 1], [-350, 2], [-10, 3]]
nums.sort(key=lambda x: x[0], reverse=False) # [[-350, 2], [-10, 3], [10, 0], [170, 1]]
for j in range(n):
if j < n // 2:
cost += costs[nums[j][1]][1] # costs[2][1] 获得50
else:
cost += costs[nums[j][1]][0]
return cost
46 数组变换
"""
首先,给你一个初始数组 arr。然后,每天你都要根据前一天的数组生成一个新的数组。
第i天所生成的数组,是由你对第i-1天的数组进行如下操作所得的:
假如一个元素小于它的左右邻居,那么该元素自增 1。
假如一个元素大于它的左右邻居,那么该元素自减 1。
首、尾元素 永不改变。
过些时日,你会发现数组将会不再发生变化,请返回最终所得到的数组。
输入:[1,6,3,4,3,5]
输出:[1,4,4,4,4,5]
解释:
第一天,数组从 [1,6,3,4,3,5] 变为 [1,5,4,3,4,5]。
第二天,数组从 [1,5,4,3,4,5] 变为 [1,4,4,4,4,5]。
无法再对该数组进行更多操作。
"""
class Solution:
def nums(self, arr: list):
while True:
changed = False
ans = arr[:]
for i in range(1, len(ans) - 1):
if ans[i] < ans[i - 1] and ans[i] < ans[i + 1]:
arr[i] += 1
changed = True
elif ans[i] > ans[i - 1] and ans[i] > ans[i + 1]:
arr[i] -= 1
changed = True
if changed == False:
break
return arr
47 给定行和列的和求可行矩阵
"""
给你两个非负整数数组rowSum 和colSum,其中rowSum[i]是二维矩阵中第 i行元素的和, colSum[j]是第 j列元素的和。换言之你不知道矩阵里的每个元素,但是你知道每一行和每一列的和。请找到大小为rowSum.length x colSum.length的任意 非负整数矩阵,且该矩阵满足rowSum 和colSum的要求。请你返回任意一个满足题目要求的二维矩阵,题目保证存在 至少一个可行矩阵。
示例 1:
输入:rowSum = [3,8], colSum = [4,7]
输出:[[3,0],
[1,7]]
解释:
第 0 行:3 + 0 = 3 == rowSum[0]
第 1 行:1 + 7 = 8 == rowSum[1]
第 0 列:3 + 1 = 4 == colSum[0]
第 1 列:0 + 7 = 7 == colSum[1]
行和列的和都满足题目要求,且所有矩阵元素都是非负的。
另一个可行的矩阵为:[[1,2],
[3,5]]
"""
class Solution:
def restoreMatrix(self, rowSum: list, colSum: list):
row, col = len(rowSum), len(colSum)
matrix = [[0] * col for _ in range(row)] # matrix=[行][列]
for i in range(row):
for j in range(col):
min_num = min(rowSum[i], colSum[j]) # 获取最小值
matrix[i][j] = min_num
rowSum[i] -= min_num # 获取每行最大的值
colSum[j] -= min_num # 获取每列最大的值,递减
if rowSum[i] == 0:
break
return matrix