Leetcode题解-算法-数组与矩阵(python版)

1、把数组中的0移动到数组尾部

283. 移动零(Easy)

使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        begin = 0
        for i, num in enumerate(nums):
            if num:
                nums[i], nums[begin] = nums[begin], nums[i]
                begin += 1

时间复杂度:O(n)
空间复杂度:O(1)

2、改变矩阵分维度

566. 重塑矩阵(Easy)

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

3、数组中连续1的最大个数

485. 最大连续 1 的个数(Easy)
方法一:
将数组尾部插入一个0,每碰见一个0,就统计两个0之间有多少个1

class Solution:
    def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
        pre_zero_index = -1
        res = 0
        nums.append(0)
        for i, num in enumerate(nums):
            if not num:
                cur_zero_index = i
                res = max(res, cur_zero_index - pre_zero_index - 1)
                pre_zero_index = cur_zero_index
        return res

方法二:
遍历数组,记录最大的连续 1 的个数和当前的连续 1 的个数。如果当前元素是 1,则将当前的连续 1 的个数加 1,否则,更新最大的连续 1 的个数,并将当前的连续 1 的个数清零。
将数组尾部放置一个 0,这样最后一个1也能统计到

class Solution:
    def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
        count = 0
        res = 0
        nums.append(0)
        for i, num in enumerate(nums):
            if num:
                count += 1
            else:
                res = max(res, count)
                count = 0
        return res

4、有序矩阵中查找目标数

240. 搜索二维矩阵 II(Medium)

矩阵具有特殊性:每行从左到右的升序排列;每列从上到下升序排列。

从矩阵的右上角开始搜索,若目标值小于矩阵元素,向左运动,若目标值大于矩阵元素,向下运动。

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        row = len(matrix)
        col = len(matrix[0])
        i, j = 0, col -1
        while 0 <= i < row and 0 <= j < col:
            if matrix[i][j] == target:
                return True
            elif matrix[i][j] < target:
                i += 1
            else:
                j -= 1
        return False

5、有序矩阵中第k小的数

378. 有序矩阵中第 K 小的元素(Medium)

方法:二分查找
区间头尾分别为矩阵最小值和最大值,每次区间统计矩阵中小于等于 mid 的数有多少个。循环求解是为了找到使得矩阵中元素 matrix[i][j]<=mid 的 mid 的最小值,也就是第 k 小的数。

每次统计矩阵中小于等于 mid 的数有多少个,从右上角向左下角移动。

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        left, right = matrix[0][0], matrix[-1][-1]
        n = len(matrix)

        def check(num):
            count = 0
            i, j = 0, n - 1
            while i < n and j >= 0:
                if num >= matrix[i][j]:
                    count += (j + 1)
                    i += 1
                else:
                    j -= 1
            return count

        while left < right:
            mid = left + (right - left) // 2
            cnt = check(mid)
            if cnt >= k:
                right = mid
            else:
                left = mid + 1
        return left

6、1-n的数中一个数被另一个替换,找重复和丢失的数

645. 错误的集合(Easy)

方法一:排序

class Solution:
    def findErrorNums(self, nums: List[int]) -> List[int]:
        nums.sort()
        res = [0, 0]

        n = len(nums)
        for i in range(1, n):
            if nums[i] == nums[i-1]:
                res[0] = nums[i]

        res[1] = int((1+n)*n/2 - (sum(nums)-res[0]))
        return res

方法二:统计每个元素出现的次数
用数组统计每个元素出现的次数,数组下标为对应的数值,数组中元素为每个元素出现的次数。

class Solution:
    def findErrorNums(self, nums: List[int]) -> List[int]:
        nums_dict = collections.Counter(nums)
        res = [0, 0]
        n = len(nums)
        for i in range(1, n+1):
            if i not in nums_dict:
                res[1] = i
            elif nums_dict[i] >= 2:
                res[0] = i
        return res

方法三:元素置反
nums 数组中的所有元素都是1到n的正数。对于 nums 中的每个元素 n,找数组中的第 n 个元素,如果为正数,乘以-1。 如果其中一个元素 num 出现两次,当第二次遇到这个数字时,将发现元素 nums[ abs(num)-1] 为负数。,此时 num 就是重复的元素。

完成反转后,如果其中一个数字 num 缺失,则第 num 个元素将为正数,即找到了缺失的数字。

class Solution:
    def findErrorNums(self, nums: List[int]) -> List[int]:
        res = []
        for _, num in enumerate(nums):
            if nums[abs(num)-1] > 0:
                nums[abs(num) - 1] *= -1
            else:
                res.append(abs(num))

        for i, num in enumerate(nums):
            if num > 0:
                res.append(i + 1)
        return res

方法四:
python的set可以直接去重

class Solution:
    def findErrorNums(self, nums: List[int]) -> List[int]:
        n = len(nums)
        res = [0, 0]
        res[0] = sum(nums) - sum(set(nums))
        res[1] = (1+n)*n//2 - sum(set(nums))
        return res

7、寻找数组中丢失的数

448. 找到所有数组中消失的数字(Easy)
方法:元素置反
1-n 的每个元素,如果出现,就将对应位置上的元素置反,最后没有置反的位置就是没有出现的元素。
时间复杂度:O(n), 空间复杂度:O(1)。

class Solution:
    def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
        res = []
        for i, num in enumerate(nums):
            if nums[abs(num)-1] > 0:
                nums[abs(num)-1] *= -1
        for i, num in enumerate(nums):
            if num > 0:
                res.append(i+1)
        return res

8、寻找数组中重复的数

442. 数组中重复的数据(Medium)

方法:元素置反
1-n 的每个元素,如果出现,就将对应位置上的元素置反。如果发现下表为 i 的元素已经置反,则元素i+1为重复元素。

class Solution:
    def findDuplicates(self, nums: List[int]) -> List[int]:
        res = []
        for num in nums:
            if nums[abs(num)-1] > 0:
                nums[abs(num)-1] *= -1
            else:
                res.append(abs(num))
        return res

9、寻找数组中重复的数(不修改数组)

287. 寻找重复数(Medium)

方法一:Floyd判圈算法

元素有重复,可以看成数组中存在环,寻找环的入口结点。用快慢指针 slow,fast,slow 一次走一步,fast 一次走两步,二者相遇时,一定在环中。相遇之后让 fast 指向数组开始位置,slow,fast 一次走一步,则在环入口相遇。

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        slow = nums[0]
        fast = nums[nums[0]]
        while slow != fast:
            slow = nums[slow]
            fast = nums[nums[fast]]
        fast = 0
        while slow != fast:
            slow = nums[slow]
            fast = nums[fast]
        return slow

链表找环的入口(证明)
请添加图片描述

链表的非环部分长度为L,环的长度为C,相遇时slow指针走了t步。则有如下表达式。
(t-L) mod C = (2t-L) mod C
(t-L) - mC = (2t-L) - nC
t = (n - m)*C
只要n,m为正整数,一定有解。此时将fast放回链表头部,slow原地不动,再走t1步,slow走到环的入口,则有
(t+t1-L) mod C = 0
解得t1=L。即两指针相遇处为环的入口。

10、构造相邻数有 k 种差值的数组

667. 优美的排列 II(Medium)

数组中有 n 个数 [1,n],用这些数构造数组,使得相邻元素差有 k 个。

问题分析:
让前 k+1 个数的差值分别为 k,k-1,k-1,……1。
前k+1个数为:1,k+1,2,k,3,k-1,4,k-2,5,……

剩下的数从 k+2 依次赋值,因为从 k+2 开始的数还没有使用,而且二者之差也被前面的差值包含了。

class Solution:
    def constructArray(self, n: int, k: int) -> List[int]:
        res = [1] * n
        diff = k
        for i in range(1, k+1):
            res[i] = res[i-1] + diff if i%2 else res[i-1] - diff
            diff -= 1
        
        for i in range(k+1, n):
            res[i] = i + 1
        return res

11、数组的度

697. 数组的度(Easy)

方法:哈希表
使用哈希表统计每一个数出现的次数,第一次,最后一次出现的位置。
每一个数映射到一个长度为 3 的数组,数组中的三个元素分别代表这个数出现的次数、第一次出现的位置和最后一次出现的位置。

class Solution:
    def findShortestSubArray(self, nums: List[int]) -> int:
        mp = dict()
        for i, num in enumerate(nums):
            if num in mp:
                mp[num][0] += 1
                mp[num][2] = i
            else:
                mp[num] = [1, i, i]

        res = 0
        max_count = 0
        for count, begin, end in mp.values():
            if count > max_count:
                res = end - begin + 1
                max_count = count
            elif count == max_count:
                res = min(res, end-begin+1)
        return res

12、对角元素相等的矩阵

766. 托普利茨矩阵(Easy)

遍历该矩阵,将每一个元素和它左上角的元素相比对即可。

class Solution:
    def isToeplitzMatrix(self, matrix: List[List[int]]) -> bool:
        row = len(matrix)
        col = len(matrix[0])
        for i in range(1, row):
            for j in range(1, col):
                if matrix[i][j] != matrix[i-1][j-1]:
                    return False
        return True

13、数组嵌套

565. 数组嵌套(Medium)

方法一:伴随数组
对于每一个元素,搜索形成环,环中有多少元素。对于一个环来说,从任何一个节点访问,这个环中的元素都没必要再次访问了,可以用伴随数组表示每个元素是否访问。

class Solution:
    def arrayNesting(self, nums: List[int]) -> int:
        visited = [0] * len(nums)
        res = 0
        for i, num in enumerate(nums):
            if visited[i] == 0:
                value = num
                cur_len = 0
                while not visited[value]:
                    visited[value] = 1
                    value = nums[value]
                    cur_len += 1
                res = max(cur_len, res)

        return res

时间复杂度:O(n)
空间复杂度:O(n)

方法二:取消伴随数组,直接修改原数组,将访问过的元素置为-1

class Solution:
    def arrayNesting(self, nums: List[int]) -> int:
        res = 0
        for i, num in enumerate(nums):
            if nums[i] != -1:
                start = num
                cur_len = 1
                nums[i] = -1
                while nums[start] != -1:
                    cur_len += 1
                    tmp = nums[start]
                    nums[start] = -1
                    start = tmp
                res = max(cur_len, res)

        return res

时间复杂度:O(n)
空间复杂度:O(1)

14、分隔数组

769. 最多能完成排序的块(Medium)

解题思路:
对于前 i 个元素,如果前 i 个元素的最大值等于 i-1,说明前 i 个元素可以分离出来单独排序,也就是说前 i 个元素为{0,1,……,i-1 },或者其他顺序排列的集合。

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        cur = 0
        for i, num in enumerate(nums):
            if num:
                nums[cur] = num
                cur += 1
        for i in range(cur, len(nums)):
            nums[i] = 0

时间复杂度:O(n)
空间复杂度:O(1)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值