题39:数组中出现次数超过一半的数字
法一:字典/哈希表统计
用字典记录下num出现的次数,若超过1/2,则return
时间复杂度O(N)、空间复杂度O(N)
class Solution:
def majorityElement(self, nums: List[int]) -> int:
d={}
l=len(nums)
for num in nums:
if num in d.keys():
d[num]+=1
else:
d[num]=1
if d[num]>int(l/2):
return num
法二:排序取中
思路:由于众数长度超过一半,因此排序后的中位数必然是众数(顺便梳理一遍排序算法)
法三:摩尔投票(新姿势)
投票:若当前num==x(众数),则票数加一,否则票数减一
推论:
- 整个数组的票数总和一定大于0
- 若前i个数的投票结果为0,那么后面n-i个数的投票结果一定大于0
思路:当票数为0时,指定当前数为众数,缩小范围
疑问:当票数为0后,众数就变了,那前面的票数就不为0了,如何保证结果的正确性?
正确性解释:
- 若ans!=A,那么则是利用其他数字去消除其他数字与A,那么最终剩下的依旧是A最多。
- 若ans==A:则通过前面的理论,最后剩下的一定为A。即使过程中cnt为0,根据ans!=A的理论最后剩下的还是A。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
vote = 0
for n in nums:
if vote==0:
x=n
if n==x:
vote+=1
else:
vote-=1
return x
简洁版:
class Solution:
def majorityElement(self, nums: List[int]) -> int:
vote = 0
for n in nums:
if vote==0:x=n
vote+=1 if n==x else -1
return x
题40:最小的k个数
就是考察排序:
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
#冒泡排序
alen = len(arr)
for i in range(alen-1,0,-1):
for j in range(i):
if arr[j]>arr[j+1]:
arr[j],arr[j+1]=arr[j+1],arr[j]
return arr[:k]
偷懒做法:也很妙
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
arr.sort()
return arr[:k]
题41:数据流中的中位数
法一:
addNum:遍历插入,时间复杂度O(N)
findMedian:返回中位数即可
class MedianFinder:
def __init__(self):
"""
initialize your data structure here.
"""
self.numlist=[]
def addNum(self, num: int) -> None:
###暴力插入
if len(self.numlist)==0:
self.numlist.append(num)
else:
for i in range(len(self.numlist)):
if num<self.numlist[i]:
self.numlist.insert(i,num)
break
if i==len(self.numlist)-1:
self.numlist.append(num)
def findMedian(self) -> float:
numlen=len(self.numlist)
tmp=int(numlen/2)
#print(numlen)
if numlen==0:
return None
elif numlen%2==1:
return self.numlist[tmp]
else:
return (self.numlist[tmp-1]+self.numlist[tmp])/2
# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()
法二:堆(新姿势)
- 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
- python内置的heapq模块可以实现小顶堆,而实现大顶堆的方法很巧妙:push的时候将元素去反,这样一来,虽然堆还是小顶堆,但当pop最小值的时候,将其再一次去反,就能得到最大值了,从而实现了大顶堆,妙啊
- python中堆的基本操作:
heap=[] #创建一个空堆
heappush(heap,num) #往heap里面插入元素
heappop(heap) #弹出并返回堆顶元素(最小值
)heappushpop(heap,num) #将元素先插入heap之后再弹出堆顶元素
heapreplace(heap,num) #弹出并返回最小值,然后将num插入到heap中
- 解题思路:
小顶堆A用于存储较大的数,大顶堆B用于存储较小的数,A、B各存储一半元素(当共有奇数个元素时,A多存储一个)
添加元素:- 当A和B一样长时,说明该元素应插入到A中,实现方法是:先将元素插入到B中,再将B的堆顶插入到A中;
- 当A比B长时,说明该元素应插入到B中,实现方法是:先将元素插入到A中,再将A的堆顶插入到B中;
class MedianFinder:
def __init__(self):
"""
initialize your data structure here.
"""
self.numlist=[]
self.A,self.B=[],[]
def addNum(self, num: int) -> None:
if len(self.A)==len(self.B):
heappush(self.B,-num)
heappush(self.A,-heappop(self.B))
else:
heappush(self.A,num)
heappush(self.B,-heappop(self.A))
def findMedian(self) -> float:
if (len(self.A)+len(self.B))%2:
return self.A[0]
else:
return (self.A[0]-self.B[0])/2
# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()
题42:连续子数组的最大和
思路:动态规划解题,dp[i]表示第i-1个字符处的最大和(包括第i-1个字符)。那么转移方程的思路:如果dp[i-1]小于0了,那说明加上dp[i-1]会使dp[i]更小,所以当dp[i-1]小于0时,dp[i]=nums[i],否则dp[i]=nums[i]+dp[i-1]
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
dp=[nums[0]]
for i in range(1,len(nums)):
if dp[i-1]>=0:
dp.append(dp[i-1]+nums[i])
else:
dp.append(nums[i])
return max(dp)
简洁版代码:学fei了
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
for i in range(1,len(nums)):
nums[i]+=max(nums[i-1],0)
return max(nums)