• 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])
    
    • 1
    • 2
    • 3
    • 4

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

     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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    (二)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  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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 
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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 ,二者相等。
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    解题思路: 这道题好多人多用 线段树 求解的,可不知为什么贼不喜欢用哪个数据结构,它太冗长了,这道题数据量不大,就用最简单的、直观的方法去求解吧!我现在尽量不去看一些或做一些盲目带模板的解说,想多思考一些,练一下自己对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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

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

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

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

    (一)动态数组

    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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (二)内存优化

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

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

      例如:

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

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

    (一)动态数组

    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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (二)内存优化

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    总结

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

  • 相关阅读:
    Redis数据类型之list
    Linux环境下安装jdk8
    Unity开发者3D模型基础
    深入理解nginx的https sni机制
    LeetCode 2698. 求一个整数的惩罚数:模拟(二进制枚举)
    unity中压缩文件与解压文件
    企业园区办公室无线覆盖部署案例
    JAVA计算机毕业设计实验室耗材管理系统源码+系统+mysql数据库+lw文档
    粒子群算法优化双向长短期记忆神经网络的多输入单输出回归分析,粒子群算法优化gru神经网络的多输入回归分析
    PAT(甲级)2022年春季考试
  • 原文地址:https://blog.csdn.net/weixin_42269028/article/details/126598206