LeetCode Cookbook 数组习题(6)

LeetCode Cookbook 数组习题(6)

  篇接上回,开始!

561. 数组拆分

题目链接:561. 数组拆分
题目大意:给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大。返回该 最大总和 。

解题思路:排序 数组索引(记住:py3的数组索引非常好用!)。

class Solution:
    def arrayPairSum(self, nums: List[int]) -> int:
        nums.sort()
        return sum(nums[::2])

566. 重塑矩阵

题目链接:566. 重塑矩阵
题目大意:在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。重构后的矩阵需要将原始矩阵的所有元素以相同的 行遍历顺序 填充。如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。

解题思路: (一)常规展成一行,依次填充,缺点费内存;(二)找规律,省下不少内存,时间上稍微慢些。

(一)常规展开 (非常耗费内存,但速度不差)

 m,n = len(mat),len(mat[0])
        if (m == r and n == c) or (m*n != r*c): return mat
        tmp = []
        for i in range(m):
            for j in range(n):
                tmp.append(mat[i][j])
        ans = [[0 for _ in range(c)] for _ in range(r)]
        # print(ans)
        idx = 0
        for i in range(r):
            for j in range(c):
                ans[i][j] = tmp[idx]
                idx += 1
        return ans

(二)找规律(思路很清晰)

 class Solution:
    def matrixReshape(self, mat: List[List[int]], r: int, c: int) -> List[List[int]]:
        
        m,n = len(mat),len(mat[0])
        if m*n != r*c: return mat
        ans = [[0 for _ in range(c)] for _ in range(r)]
        for i in range(m*n): 
            ans[i//c][i%c] = mat[i//n][i%n]
        return ans

628. 三个数的最大乘积

题目链接:628. 三个数的最大乘积
题目大意:给你一个整型数组 nums ,在数组中找出由个数组成的最大乘积,并输出这个乘积。

解题思路: 细节题注意最后的结果输出有两种情况,最大值取决于这两者之间。

class Solution:
    def maximumProduct(self, nums: List[int]) -> int:
        min1,min2 = float('inf'),float('inf')
        max1,max2,max3 = -float('inf'),-float('inf'),-float('inf')
        for num in nums:
            if num > max1:
                max3 = max2
                max2 = max1
                max1 = num
            elif num > max2:
                max3 = max2
                max2 = num
            elif num > max3:
                max3 = num
            if num < min1:
                min2 = min1
                min1 = num
            elif num < min2:
                min2 = num
        # print(max1,max2,max3,min1,min2)
        return max(max1*max2*max3,max1*min1*min2)

661. 图片平滑器

题目链接:661. 图片平滑器
题目大意:图像平滑器 是大小为 3 x 3 的过滤器,用于对图像的每个单元格平滑处理,平滑处理后单元格的值为该单元格的平均灰度。每个单元格的 平均灰度 定义为:该单元格自身及其周围的 8 个单元格的平均值,结果需向下取整。(即,需要计算蓝色平滑器中 9 个单元格的平均值)。如果一个单元格周围存在单元格缺失的情况,则计算平均灰度时不考虑缺失的单元格(即,需要计算红色平滑器中 4 个单元格的平均值)

解题思路: 滤波器的典型原理,主要还是找规律的事情,把需要的列出来,然后去找规律。

class Solution:
    def imageSmoother(self, img: List[List[int]]) -> List[List[int]]:

        # find the law of data retrieval
        # assume matrix is 3 rows ans 3 column
        # i = 0 range 0~1
        # i = 1 range 0~2
        # i = 2 range 1~2 
        # Made another fatal mistake: (m,n) !!!!!
        # so leftBoundary max(i-1,0) rightBoundary min(i+1,3-1)
        # the matirx [m,n], so range(max(i-1,0),min(i+2,m)) 
        m,n = len(img),len(img[0]) # rows -> m ; columns -> n
        ans = [[0]*n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                total,nums = 0,0
                for x in range(max(i-1,0),min(i+2,m)):
                    for y in range(max(j-1,0),min(j+2,n)):
                        total += img[x][y]
                        nums += 1
                ans[i][j] = total // nums
        return ans

695. 岛屿的最大面积

题目链接:695. 岛屿的最大面积
题目大意: 给你一个大小为 m x n 的二进制矩阵 grid 。岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。岛屿的面积是岛上值为 1 的单元格的数目。计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。

解题思路: (一)DFS;(二)BFS。

(一)DFS

class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
    
        # DFS
        m,n = len(grid),len(grid[0])
        def DFS(i: int,j: int) -> int:
            if 0<=i<m and 0<=j<n and grid[i][j]:
                grid[i][j] = 0
                # 上下左右
                return 1 + DFS(i-1,j) + DFS(i+1,j) + DFS(i,j-1) + DFS(i,j+1)
            return 0
        
        ans = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j]:
                    ans = max(ans,DFS(i,j))
        return ans

(二)BFS

class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:

        # BFS
        def BFS(i: int,j: int) -> int:
            q = [(i,j)]
            grid[i][j] = 0
            tmp = 1
            while q:
                x,y = q.pop()
                for dx,dy  in directions:
                    nx,ny = x+dx,y+dy
                    if 0<=nx<m and 0<=ny<n and grid[nx][ny]:
                        grid[nx][ny] = 0
                        tmp += 1
                        q.append((nx,ny))
            return tmp  

697. 数组的度

题目链接:697. 数组的度
题目大意: 给定一个非空且只包含非负数的整数数组 nums,数组的 的定义是指数组里任一元素出现频数的最大值。你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。

解题思路: 用哈希表比较简单,建立两个哈希表 left,right 记录相同元素的位置,使用 counter 计数器存储个元素的出现频率,得到最大的频率对应的元素,而后根据 left和right 求取最短的连续子数组长度。

class Solution:
    def findShortestSubArray(self, nums: List[int]) -> int:
        left,right = dict(),dict()
        counter = collections.Counter()
        for i,num in enumerate(nums):
            if num not in left:
                left[num] = i
            right[num] = i
            counter[num] += 1
        degree = max(counter.values())
        print(counter.values())
        print(degree)
        ans = len(nums)
        for k,v in counter.items():
            if v == degree:
                ans = min(ans,right[k]-left[k]+1)
        return ans

713. 乘积小于 K 的子数组

题目链接:713. 乘积小于 K 的子数组
题目大意: 给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

解题思路:边走边比较,由于题目中给出是连续的子数组,个数判断起来就容易很多,这道题要做的话最好还是看看题目中对 nums 和 k 的限制,这样更好理解代码的编写含义。

class Solution:
    def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
        # 只求取子数组的个数 还是比较简单的
        ans = 0
        tmp,start = 1,0
        for i,num in enumerate(nums):
            tmp *= num
            while start <= i and tmp >= k:
                tmp //= nums[start]
                start += 1
            # 因为从题意中了解到 子数组是连续的
            ans += i - start + 1
        return ans

714. 买卖股票的最佳时机含手续费*

题目链接:714. 买卖股票的最佳时机含手续费
题目大意: 给定一个整数数组 prices,其中 prices[i] 表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。返回获得利润的最大值。

  • 注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

解题思路: 区别于 122. 买卖股票的最佳时机 II 需要动态考虑。

  • buy[i] 代表第 i 天买入的最大收益;
  • sell[i] 代表第 i 天卖出的最大收益.
  • 状态转移方程是 buy[i] = max(buy[i-1], sell[i-1]-prices[i])sell[i] = max(sell[i-1], buy[i-1]+prices[i]-fee)
class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        n = len(prices)
        sell,buy = 0,-prices[0]
        for i in range(1,n):
            print(sell,buy)
            sell = max(sell,buy+prices[i]-fee) 
            # 买的最少
            buy = max(buy,sell-prices[i])
        return sell

717. 1 比特与 2 比特字符

题目链接:717. 1 比特与 2 比特字符
题目大意: 有两种特殊字符:

  • 第一种字符可以用一比特 0 表示
  • 第二种字符可以用两比特(1011)表示
  • 给你一个以 0 结尾的二进制数组 bits ,如果最后一个字符必须是一个一比特字符,则返回 true

解题思路:超有趣的一道动态规划的题目,有些许技巧在其中。(具体看代码)

class Solution:
    def isOneBitCharacter(self, bits: List[int]) -> bool:
        # 这道题太巧妙了
        # 记住一点 看见1 就多走一步 直到最后看能不能到达最后一位
        n = len(bits)
        i = 0
        #  注意最后一位 已经肯定是0 了 所以只需要走到 n-1步即可
        while i < n-1:
            if bits[i] == 1:
                i += 1
            i += 1
        return i == n-1 

719. 找出第 K 小的数对距离数 **

题目链接:719. 找出第 K 小的数对距离
题目大意:数对 (a,b) 由整数 ab 组成,其数对距离定义为 a 和 b 的绝对差值。给你一个整数数组 nums 和一个整数 k ,数对由 nums[i]nums[j] 组成且满足 0 <= i < j < nums.length 。返回 所有数对距离中 第 k 小的数对距离。

解题思路:非常费脑子的二分法题目,代码是从一个大神哪里搬下来的,非常的好懂! 双指针相较于再次使用二分法要快一些。

class Solution:
    def smallestDistancePair(self, nums: List[int], k: int) -> int:

        #  典型的二分模板 二

        def check(diff: int) -> int:
            cnt,j = 0,0
            for i in range(n):
                while j < n and nums[j]-nums[i] <= diff:
                    j += 1
                # 记住 跳出while 循环时是nums[j]-nums[i] > diff 不符合的j 应该减去1才是复合的位置
                cnt += j-1-i
            return cnt

        nums.sort()
        print(nums)
        n = len(nums)
        L,R = 0,nums[-1]-nums[0]
        # 二分模板(二)
        while L < R:
            mid = L +(R-L)//2
            if check(mid) < k:
                L = mid+1
            else:
                R = mid
        return L 


724. 寻找数组的中心下标

题目链接:724. 寻找数组的中心下标
题目大意:给你一个整数数组 nums ,请计算数组的 中心下标 。数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。
例如:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

解题思路: 需要找规律,具体看代码,这道题挺巧妙的。

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        n = len(nums)
        total = sum(nums)
        pre = 0
        for i,num in enumerate(nums):
            if pre*2+num == total:
                return i
            pre += num
        return -1 

729. 我的日程安排表 I

题目链接:729. 我的日程安排表 I
题目大意:实现一个 MyCalendar 类来存放你的日程安排。如果要添加的日程安排不会造成 重复预订 ,则可以存储这个新的日程安排。当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生 重复预订 。日程可以用一对整数 startend 表示,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end

  • 实现 MyCalendar 类:MyCalendar() 初始化日历对象。
  • boolean book(int start, int end) 如果可以将日程安排成功添加到日历中而不会导致重复预订,返回 true 。否则,返回 false 并且不要将该日程安排添加到日历中。

例如:

输入:
["MyCalendar", "book", "book", "book"]
[[], [10, 20], [15, 25], [20, 30]]
输出:
[null, true, false, true]
解释:
MyCalendar myCalendar = new MyCalendar();
myCalendar.book(10, 20); // return True
myCalendar.book(15, 25); // return False ,这个日程安排不能添加到日历中,因为时间 15 已经被另一个日程安排预订了。
myCalendar.book(20, 30); // return True ,这个日程安排可以添加到日历中,因为第一个日程安排预订的每个时间都小于 20 ,且不包含时间 20 

解题思路: 这道题好多人多用 线段树 求解的,可不知为什么贼不喜欢用哪个数据结构,它太冗长了,这道题数据量不大,就用最简单的、直观的方法去求解吧!我现在尽量不去看一些或做一些盲目带模板的解说,想多思考一些,练一下自己对py3语言的熟悉度以及解决思路向编程语言转化的能力。

class MyCalendar:

    def __init__(self):
        self.booked = []

    def book(self, start: int, end: int) -> bool:
        if any(l<end and r>start for l,r in self.booked):
            return False
        self.booked.append((start,end))
        return True

70. 爬楼梯 and 746. 使用最小花费爬楼梯

题目链接:70. 爬楼梯
题目大意:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

  • 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
    例如:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。1. 1+ 12. 2

解题思路: 典型动态规划的题目,和 斐波那契数列 有异曲同工之妙。

(一)动态数组

class Solution:
    def climbStairs(self, n: int) -> int:
        
        # 好理解的数组
        dp = [0] * (n+1)
        dp[0]=dp[1]=1
        for i in range(2,n+1):
            dp[i] = dp[i-1]+dp[i-2]
        return dp[-1]

(二)内存优化

class Solution:
    def climbStairs(self, n: int) -> int:

        # 内存优化
        a,b = 1,1
        for i in range(2,n+1):
            a,b = b,a+b    
        return b

题目链接:746. 使用最小花费爬楼梯
题目大意:给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

  • 请你计算并返回达到楼梯顶部的最低花费。

    例如:

输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。支付 15 ,向上爬两个台阶,到达楼梯顶部。总花费为 15 

解题思路: 典型动态规划的题目,和 斐波那契数列 有异曲同工之妙。

(一)动态数组

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        # 动态数组 便于编写
        n = len(cost)
        dp = [0]*(n+1)
        for i in range(2,n+1):
            dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
        return dp[-1]

(二)内存优化

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        # 内存优化
        n = len(cost)
        a,b = 0,0
        for i in range(2,n+1):
            a,b = b,min(b+cost[i-1],a+cost[i-2])
        return b

总结

  进度有点慢了,必须加快了,做什么事情不能太拖拉了,要不自我感觉什么也干不好了,今天看了一小部分梁晓声的《读书是最对得付出的一件事》,其中讲解了一下雨果的《悲惨世界》中的米里哀主教,我记着我翻开《悲惨世界》时,被其冗杂的景物描写深感无力,现在觉着也能看下去了,不知道为什么,在梁晓声的这本书中我忽然意识到一个问题,‘平凡’确实不是一个贬义词,准确来说它是带有褒义的,因为能说出自己是一个平凡的人的时候,也就意味着自己已经寻觅到自我生命的终点,为什么这么说呢?因为昨天我同母亲进行自己以后到应该做什么?或者说应该去喜欢什么?大学四年加上现有的两年左右的研究生,我都没有搞明白这个问题,属实我是不平凡的,不是那种出色的不平凡,而是平庸,准确点有些得过且过,但事实上我想平凡的学习、生活,找到自己可以喜欢并且能去喜欢的东西或事情,搞明白自己剩下的人生应该持续不断努力的方向或总体目标,在这个物欲横流、日新月异、充斥着各种杂念的社会里,我应该怎么去成为一个 平凡 的人,这可能是我穷尽剩余时光来思考的一个我可以尝试解答的问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值