第四周LeetCode记录

9.28 16. 复数乘法

给定两个表示复数的字符串。

返回表示它们乘积的字符串。注意,根据定义 i2 = -1 。

输入: "1+1i", "1+1i"
输出: "0+2i"
解释: (1 + i) * (1 + i) = 1 + i2 + 2 * i = 2i ,你需要将它转换为 0+2i 的形式。

输入: "1+-1i", "1+-1i"
输出: "0+-2i"
解释: (1 - i) * (1 - i) = 1 + i2 - 2 * i = -2i ,你需要将它转换为 0+-2i 的形式。

注意:

输入字符串不包含额外的空格。
输入字符串将以 a+bi 的形式给出,其中整数 a 和 b 的范围均在 [-100, 100] 之间。输出也应当符合这种形式。

我的解

class Solution:
    @classmethod
    def complexNumberMultiply(self, a: str, b: str) -> str:

        a1,a2 = a.split('+')
        b1,b2 = b.split('+')

        new_a2 = a2[:-1]
        new_b2 = b2[:-1]

        constant = int(a1)*int(b1)
        sum_i = int(a1) * int(new_b2) + int(b1) * int(new_a2)
        new_sum_i = "+" + str(sum_i)
        i = int(new_a2) * int(new_b2)
      
        return str(constant-i) + str(new_sum_i)+"i"

9.29 17. 将矩阵按对角线排序

给你一个 m * n 的整数矩阵 mat ,请你将同一条对角线上的元素(从左上到右下)按升序排序后,返回排好序的矩阵。

示例 1:

img

输入:mat = [[3,3,1,1],[2,2,1,2],[1,1,1,2]]
输出:[[1,1,1,1],[1,2,2,2],[1,2,3,3]]

思路

设有m行n列,取对角线元素组成一个新数组排序好写回去,遍历的时候要考虑行数和列数的关系。

最优解

解法一:迭代器

如下所示,用d = 行数-列数获取行列关系

0 -1 -2 -3
1 0 -1 -2
2 1 0 -1
class Solution:
    @classmethod
    def diagonalSort(self, mat: list) -> list:
        import collections
        import itertools
        m, n, d = len(mat), len(mat[0]), collections.defaultdict(list)
        for i, j in itertools.product(range(m), range(n)):
            d[i - j].append(mat[i][j])
        d = {k: iter(sorted(v)) for k, v in d.items()}
        for i,j in itertools.product(range(m),range(n)):
            mat[i][j] = next(d[i-j])
        return mat

体会一下

  • 构造i-j的原因,很巧妙的构造。
  • 使用itertools.product来代替多重for循环。
  • iter函数来实现对象的可迭代。
解法二:迭代指针
function diagonalSort(mat: number[][]): number[][] {
    const [m, n] = [mat.length, mat[0].length]
    const d: { [_: number]: number[] } = {}
    mat.forEach((v, i) => v.forEach((a, j) => {
        if (!d[i - j]) {
            d[i - j] = []
        }
        d[i - j].push(a)
    }))
    const it: { [_: number]: number } = {}
    for (const k in d) {
        d[k].sort((a, b) => a - b)
        it[k] = 0
    }
    mat.forEach((v, i) => v.forEach((_, j) => {
        mat[i][j] = d[i - j][it[i - j]++]
    }))
    return mat
};

解法三:堆
class Solution:
    def diagonalSort(self, mat: List[List[int]]) -> List[List[int]]:
        m, n = len(mat), len(mat[0])
        d = [[] for _ in range(m + n - 1)]
        for i, j in itertools.product(range(m), range(n)):
            heapq.heappush(d[i - j], mat[i][j])
        for i, j in itertools.product(range(m), range(n)):
            mat[i][j] = heapq.heappop(d[i - j])
        return mat

10.4 18. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

最优解

动态规划
class Solution:
    @classmethod
    def maxSubArray(self, nums: list) -> int:
        pre = 0
        max_ans = nums[0]
        for i in nums:
            pre = max(pre + i, i)
            max_ans = max(max_ans,pre)
        return max_ans

此题设只要求最大值,争取一次遍历求解,后一次和前一次比较,pre一直取的是被加数的最大值,也包含了遍历过的列表的信息,max_ans则会比较并存储已经存储的最大值。

max函数既可以比较,也存储了信息,之前也有做到,需要加深记忆。

动态规划的思想,要从最小的延伸到最大的通用算法。

image-20201005004530210

10.9 19. 最小除数

给你一个整数数组 nums 和一个正整数 threshold ,你需要选择一个正整数作为除数,然后将数组里每个数都除以它,并对除法结果求和。

请你找出能够使上述结果小于等于阈值 threshold 的除数中 最小 的那个。

每个数除以除数后都向上取整,比方说 7/3 = 3 , 10/2 = 5 。

题目保证一定有解。

输入:nums = [1,2,5,9], threshold = 6
输出:5
解释:如果除数为 1 ,我们可以得到和为 17 (1+2+5+9)。
如果除数为 4 ,我们可以得到和为 7 (1+1+2+3) 。如果除数为 5 ,和为 5 (1+1+1+2)。

输入:nums = [2,3,5,7,11], threshold = 11
输出:3

思路

遍历求和,直到小于阈值。

我的解

class Solution:
    @classmethod
    def smallestDivisor(self, nums: list, threshold: int) -> int:
        import math
        sum = 0
        div = 1
        while sum == 0:
            for i in nums:
                sum += math.ceil(i / div)
                if sum > threshold:
                    div += 1
                    sum = 0
                    break
                
            else:
                return div

最优解

from typing import List


class Solution:
    def smallestDivisor(self, nums: List[int], threshold: int) -> int:
        left = 1
        right = max(nums)

        while left < right:
            mid = (left + right) >> 1
            if sum([(num + mid - 1) // mid for num in nums]) > threshold:
                left = mid + 1
            else:
                right = mid
        return left

总结

除数的最小值为1,最大值是数组中最大的数,用二分法找到符合要求最大的数。

10.11 20. 丑数

请你帮忙设计一个程序,用来找出第 n 个丑数。

丑数是可以被 a b c 整除的 正整数

输入:n = 3, a = 2, b = 3, c = 5
输出:4
解释:丑数序列为 2, 3, 4, 5, 6, 8, 9, 10... 其中第 3 个是 4。

输入:n = 4, a = 2, b = 3, c = 4
输出:6
解释:丑数序列为 2, 3, 4, 6, 8, 9, 10, 12... 其中第 4 个是 6。

输入:n = 5, a = 2, b = 11, c = 13
输出:10
解释:丑数序列为 2, 4, 6, 8, 10, 11, 12, 13... 其中第 5 个是 10。

最优解

class Solution:
    @classmethod
    def nthUglyNumber(self, n, a, b, c):
        from math import gcd

        def lcm(a,b,c):
            x = (a*b)/gcd(a,b)
            return (x*c)/gcd(x,c)

        """
        计算有多少个丑数小于等于x
        + x整除a,b,c(ab算了2次,bc算了2次,ac算了2次,abc算了3次) -整除ab,bc,ac最小公倍数(abc算了3次) +整除abc最小公倍数 即为所求
         
        """

        def uglynum(x):
            return x // a + x // b + x // c - x // (a * b // gcd(a, b)) - x // (a * c // gcd(a, c)) - x // (
                        b * c // gcd(b, c)) + x // lcm(a, b, c)


        '''
        二分搜索,注意只要uglynum(mid)<n left就=mid+1 所以最后得到的left就是所求
        例如测试用例2中  a=2,b=3,c=4
        括号中为丑数                    1,(2),(3),(4),5,(6),7,(8)
        小于等于它们的丑数个数分别为     0, 1 , 2 , 3 ,3, 4, 4, 5 
        若n==4
        如果uglynum(mid)<4 则left一定能直接取到6而不是7
        '''
        left = 1
        right = n * min(a, b, c)
        while left < right:
            mid = (left + right) // 2
            if uglynum(mid) < n:
                left = mid + 1
            else:
                right = mid
        return left

总结

构建一个函数,使得输入一个值,返回有多少个丑数小于等于他。左边界为1,右边界至少为n*min(a,b,c)。然后根据二分法再去寻找。用二分法来保证如果有相同的数,取到的是第一个。比如uglynum(5)=3<4,left=6,不可能大于6。uglynum(7)=4,right会变,left不会变。所以永远不会大于最小的丑数6

两个小技巧求最小公倍数和最大公约数

    // 最小公倍数
    private long lcm(long a, long b) {
        return a * b / gcd(a, b);
    }
    
    // 最大公因数
    private long gcd(long a, long b) {
        if (a == 0) return b;
        return gcd(b % a, a);
    }
posted @ 2020-10-18 15:22  Jimmyhe  阅读(80)  评论(0编辑  收藏  举报