leetcode题解python版:81-85

81、搜索旋转排序数组 II

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
解:这道题与33题相似,但33题中不存在重复元素,而本题中存在重复元素.这里我们有一种简便方法,即不去找最大的元素,而是直接找target。

class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        if not nums:
            return False
        n=len(nums)
        left=0
        right=n-1
        while left<=right:
            mid=(left+right)//2
            if nums[mid]==target:
                return True
            if nums[mid]>nums[right]:#逆序,但0-mid是顺序
                if nums[mid]>target and nums[left]<=target:
                    right=mid-1
                else:
                    left=mid+1
            elif nums[right]>nums[mid]:#顺序
                if nums[mid]<target and nums[right]>=target:
                   left=mid+1
                else:
                    right=mid-1
            else:
                right=right-1#只减一,因为有可能是[1,1,target,1]这种情况,没有办法根据mid缩减right,但因为right的值与mid的相等,故都不等于target,right可以减1
        if nums[left]==target:
            return True
        else:
            return False

执行用时:32 ms, 在所有 Python3 提交中击败了98.16%的用户
内存消耗:13.9 MB, 在所有 Python3 提交中击败了32.79%的用户

当然这道题也可以先转成set去重,然后按照33题的思路去做。

82、删除排序链表中的重复元素 II

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
解:一个数不相等等价于它和前后两个数都不相等,如果满足这个条件的head,令ans.next=head,然后继续遍历,直到它位于末尾,便只需跟前一个比较,然后决定是否加入,最后ans.next=None这个封口是一定要做的,否则ans.next后面仍然是head的链,会有重复的元素。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        res=ListNode(None)
        ans=res
        if head.val!=head.next.val:
            if not head.next.next:
                return head
            else:
                res.next=head
                res=res.next
        if not head.next.next:
            return None
        while head.next.next:
            if head.val!=head.next.val and head.next.val!=head.next.next.val:
                res.next=head.next
                res=res.next
            head=head.next
        if head.val!=head.next.val:
            res.next=head.next
            res=res.next
        res.next=None#否则[1,2,2]时因为ans是连接head的,虽然没有赋值,但ans.next.val=2
        return ans.next

执行用时:48 ms, 在所有 Python3 提交中击败了80.51%的用户
内存消耗:13.8 MB在所有 Python3 提交中击败了14.61%的用户

还有一个笨方法,就是把有重复的元素的值加进列表里,然后再遍历一遍,删除重复的。

83、删除排序链表中的重复元素

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
解:这道题就简单多了,只需要顺序遍历,每一个haed和head.next比较,如果相等,则head.next=head.next.next,否则继续遍历

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        ans=head
        while head.next:
            if head.next.val==head.val:
                head.next=head.next.next
            else:
                head=head.next
        return ans       

执行用时:52 ms, 在所有 Python3 提交中击败了60.23%的用户
内存消耗:13.7 MB, 在所有 Python3 提交中击败了50.06%的用户

84、柱状图中最大的矩形 hard

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
解:这道题和装水的题可以说是互为镜像,上道题是在波谷之后找到波峰,这题是在波谷之后找波谷。
解法一:因为找不到头绪,所以就想到用动态规划法解决问题,设dp[i][j]是heights[i]到[j]的矩形的面积,事实上面积为lowest*(j-i+1),所以新加入的dp[i][j+1]也可以计算,dp[i+1][j]也可以计算,然后找到最大的即可。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        if not heights:
            return 0
        n=len(heights)
        maxS=heights[0]
        dp=[[0 for _ in range(n)] for _ in range(n)]
        for i in range(n):
            dp[i][i]=heights[i]
            if heights[i]>maxS:
                maxS=heights[i]
        for i in range(n-1):
            for j in range(i+1,n):
                dp[i][j]=min(dp[i][j-1]//(j-i),heights[j])*(j-i+1)
                if dp[i][j]>maxS:
                    maxS=dp[i][j]
        return maxS

但在进行到倒数第三个示例时会超时,所以这种遍历的操作是不行的,还是要从其内在规律着手。

解法二:中心生成法
这时可以后退一步,不考虑二维的dp[i][j],而是考虑一维的dp[i],即包含i的最大面积,这个需要向两边推进,直到找到比heights[i]小的才停止(不能包含比它小的),这样i就是底,也即数组中的最短边和矩形的高。因为最终答案一定会存在一个最短边,所以这样做是能够找到答案的
这样可以遍历两次,一次求左端点集合,一次求右端点的集合,这样就是O(n)了。此外还可以对该算法进行优化,事实上内层循环没必要一步一步移动,可以直接将j -= 1 改成 j = left[j], j +=1 改成 j = right[j]。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        left=[-1]*n
        right=[n] * n
        maxS=0
        for i in range(1,n):
            j=i-1
            while j>=0 and heights[j]>=heights[i]:
                j=left[j]
            left[i]=j
        for i in range(n-2,-1,-1):
            j=i+1
            while j<n and heights[j]>=heights[i]:
                j=right[j]
            right[i]=j
        for i in range(n):
            maxS=max(maxS,heights[i]*(right[i]-left[i]-1))
        return maxS

执行用时:92 ms, 在所有 Python3 提交中击败了14.30%的用户
内存消耗:15.6 MB, 在所有 Python3 提交中击败了66.09%的用户
相对来讲该方法还是很慢,因为要做3次循环

解法3:
若是单调递增栈,则从栈底到栈顶的元素是严格递增的,也就是说进栈的元素越来越大。元素进栈过程:对于单调递增栈,若当前进栈元素为e,从栈顶开始遍历元素,把大于e的元素弹出栈,直接遇到一个小于等于e的元素或者栈为空为止,然后再把e压入栈中。
从左到右遍历柱子,对于每一个柱子,我们想找到第一个高度小于它的柱子,那么我们就可以使用一个单调递增栈来实现。 如果柱子大于栈顶的柱子,那么说明不是我们要找的柱子,我们把它塞进去继续遍历,如果比栈顶小,那么我们就找到了第一个小于的柱子。 对于栈顶元素,其右边第一个小于它的就是当前遍历到的柱子,左边第一个小于它的就是栈中下一个要被弹出的元素,因此以当前栈顶为最小柱子的面积为当前栈顶的柱子高度 * (当前遍历到的柱子索引 - 1 - 栈中下一个要被弹出的元素索引 - 1 + 1)

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n=len(heights)
        heights=[0]+heights+[0]
        stack=[]
        maxS=0
        for i in range(n + 2):
            while stack and heights[stack[-1]]>heights[i]:
                maxS=max(maxS,heights[stack.pop(-1)]*(i-stack[-1]-1))
            stack.append(i)#每个i都进去过,说明没有漏
        return maxS

执行用时:68 ms, 在所有 Python3 提交中击败了75.63%的用户
内存消耗:15.8 MB, 在所有 Python3 提交中击败了36.70%的用户

85、最大矩形

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
解:这种乱七八糟的还是首先要想动态规划。
乍看下去动态规划很难,但是我们可以先从第一行分析,对i,dp[i]=dp[i-1],也就是说dp[i]是从i开始向左数1的个数。我们可以先按行遍历赋值,然后dp[i][j]为max(它的行数*1,min(它的行数,上一行的行数)*2,…)这样就能够完成赋值。

class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        if not matrix:
            return 0
        m=len(matrix)
        n=len(matrix[0])
        dp=[[0]*n for _ in range(m)]
        maxS=0
        for i in range(m):
            for j in range(n):
                if matrix[i][j]=='0': 
                    continue
                if j>0:
                    dp[i][j]=dp[i][j-1]+1
                else:
                    dp[i][j]=1
                width=dp[i][j]
                for k in range(i,-1,-1):
                    width=min(width, dp[k][j])
                    maxS=max(maxS, width*(i-k+1))
        return maxS

执行用时:2668 ms, 在所有 Python3 提交中击败了8.63%的用户
内存消耗:14.4 MB, 在所有 Python3 提交中击败了62.15%的用户

但是不出所料,动态规划由于是遍历,所以速度极慢,时间复杂度为O(m**2*n)

解法二:
注意到本题跟上一题的相似性,当固定一列之后这一列加上上面的元素就构成了一个柱状图,每一个点的高度是从它往上连续1的个数(不为1的点算作高度为零)

class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        if not matrix:
            return 0
        m=len(matrix)
        n=len(matrix[0])
        maxS=0
        colhigh=[0]*n
        for i in range(m):
            for j in range(n):
                if matrix[i][j]=='1':
                    colhigh[j]=colhigh[j]+1
                else: 
                    colhigh[j]=0
            maxS=max(maxS, self.largestRectangleArea(colhigh))
        return maxS

    def largestRectangleArea(self, heights: List[int]) -> int:
        n=len(heights)
        heights=[0]+heights+[0]
        stack=[]
        maxS=0
        for i in range(n + 2):
            while stack and heights[stack[-1]]>heights[i]:
                maxS=max(maxS,heights[stack.pop(-1)]*(i-stack[-1]-1))
            stack.append(i)
        return maxS

这样复杂度变为O(m*n)
执行用时:92 ms, 在所有 Python3 提交中击败了89.67%的用户
内存消耗:14.3 MB在所有 Python3 提交中击败了79.51%的用户

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值