剑指offer 题39-42 python题解记录

这篇博客探讨了数组中出现次数超过一半的数字的三种解法,包括字典统计、排序和摩尔投票算法。此外,还介绍了找到最小k个数的两种排序方法。接着,讲解了在数据流中实时计算中位数的两种策略,一种是遍历插入,另一种利用堆优化。最后,展示了求解连续子数组最大和的动态规划解决方案。文章深入浅出地展示了各种算法的应用和优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值