leetcode(1)数组系列问题

27. 移除元素

用快慢指针,快指针将慢指针位置的数覆盖

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        fast, slow = 0, 0
        while fast < len(nums):
            if nums[fast] != val:
                nums[slow] = nums[fast]
                slow += 1
            fast += 1
        return slow

26. 删除有序数组中的重复项

注意:与27. 移除元素 的区别是fast要在slow前面一个位置,并且上一题是先覆盖再移动slow下标,本题是先移动slow下标再覆盖,因此返回数组长度要+1

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        fast, slow = 1, 0
        while fast < len(nums):
            if nums[fast] != nums[slow]:
                slow += 1
                nums[slow] = nums[fast]
            fast += 1
        return slow + 1

283. 移动零

注意:与27. 移除元素 的区别是本题是交换fast和slow的数而不是覆盖

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        fast, slow = 0, 0
        while fast < len(nums):
            if nums[fast] != 0:
                nums[slow], nums[fast] = nums[fast], nums[slow]
                slow += 1
            fast += 1
        return

977. 有序数组的平方

双指针比较首尾平方的大小,必须先指定数组大小

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        n = len(nums)
        res = [0] * n
        left, right = 0, n - 1
        i = n - 1
        while i >= 0:
            if nums[left] * nums[left] > nums[right] * nums[right]:
                res[i] = nums [left] * nums[left]
                left += 1
            else:
                res[i] = nums [right] * nums[right]
                right -= 1
            i -= 1
        return res

59. 螺旋矩阵 II

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        res = [[0]*n for _ in range(n)]
        num = 0
        left, right = 0, n
        up, down = 0, n
        while num < n * n:
            for i in range(left, right):
                num += 1
                res[up][i] = num
            up += 1
            # if up > down - 1: break  可以省略
            for i in range(up, down):
                num += 1
                res[i][right - 1] = num
            right -= 1
            # if right - 1 < left: break
            for i in range(right - 1, left - 1, -1):  # 是right - 1
                num += 1
                res[down - 1][i] = num
            down -= 1
            # if down - 1 < up: break
            for i in range(down - 1, up - 1, -1):
                num += 1
                res[i][left] = num
            left += 1
            # if left > right - 1: break
        return res

54. 螺旋矩阵

同一题剑指 Offer 29. 顺时针打印矩阵

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        m = len(matrix)
        if m == 0: return []
        n = len(matrix[0])
        left, right = 0, n - 1
        up, down = 0, m - 1
        res = []
        while len(res) < m * n:
            for i in range(left, right + 1):
                res.append(matrix[up][i])
            up += 1
            if up > down: break  # 不可省略
            for i in range(up, down + 1):
                res.append(matrix[i][right])
            right -= 1
            if right < left: break
            for i in range(right, left - 1, -1):
                res.append(matrix[down][i])
            down -= 1
            if down < up: break
            for i in range(down, up - 1, -1):
                res.append(matrix[i][left])
            left += 1
            if left > right: break
        return res

268. 丢失的数字

将从 0到 n 的全部整数之和记为 total,将数组 nums 的元素之和记为 arrSum,则 arrSum 比 total 少了丢失的一个数字,因此丢失的数字即为 total 与 arrSum 之差。

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        n = len(nums)
        total = n * (n + 1) // 2  #注意项数是n + 1,首项加尾项乘以项数除以二
        arrSum = sum(i for i in nums)        
        miss = total - arrSum
        return  miss

41. 缺失的第一个正数

遍历一次数组把大于等于1的和小于数组大小的值放到原数组对应位置,然后再遍历一次数组查当前下标是否和值对应,如果不对应那这个下标就是答案,否则遍历完都没出现那么答案就是数组长度加1。

在完成交换后,新的nums[i] 可能又在 [1, N]内,需要继续进行交换操作,直到 nums[i] 不在 [1, N]内

注意到上面的方法可能会陷入死循环。如果 nums[i] 恰好与nums[nums[i] −1] 相等,那么就会无限交换下去。此时我们有 nums[i]=nums[nums[i] −1],说明 nums[i] 已经出现在了正确的位置。因此我们可以跳出循环,开始遍历下一个数。

class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        n = len(nums)
        for i in range(n):
            #新的nums[i] 可能又在 [1, N]内,需要继续进行交换操作,因此使用while
            while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]:
                # nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]错误的顺序
                 nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]
        print(nums)
        for i in range(n):
            if nums[i] != i + 1:
                return i + 1
        return n + 1

915. 分割数组

一次遍历,当后面又出现小值时,更新左数组的右边界、最大值

class Solution:
    def partitionDisjoint(self, nums: List[int]) -> int:
        left_max = cur_max = nums[0]
        res = 0
        for i in range(1, len(nums) - 1):
            cur_max = max(cur_max, nums[i])
            if nums[i] < left_max:
                res = i
                left_max = cur_max
        return res + 1

88. 合并两个有序数组

同一题:面试题 10.01. 合并排序的数组
用while循环,先判断A、B是否遍历完,再判断大小关系

class Solution:
    def merge(self, A: List[int], m: int, B: List[int], n: int) -> None:
        tail = m + n - 1
        p, q = m - 1, n - 1
        while p >= 0 or q >= 0:
            if p == -1:
                A[tail] = B[q]
                q -= 1
            elif q == -1:
                A[tail] = A[p]
                p -= 1
            elif A[p] >= B[q]:
                A[tail] = A[p]
                p -= 1
            else:
                A[tail] = B[q]
                q -= 1
            tail -= 1


315. 计算右侧小于当前元素的个数

从右往左遍历,加到排序数组中,直接查看当前元素的下标,就是右侧小于当前元素的个数

class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        from sortedcontainers import SortedList
        s = SortedList() 
        res = [0] * len(nums)
        for i in range(len(nums) - 1, -1, -1):
            s.add(nums[i])
            res[i] = s.index(nums[i])
        return res

31. 下一个排列

从后往前找到一个temp[i] < temp[i + 1],把temp[i]作为一个较小数,然后从后往前找到第一个比temp[i]大的数temp[j],交换temp[i]和temp[j],这样[i + 1, n)就是单调递减的,逆转一下变成单调递增的

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        m = len(nums)
        i = m - 2
        while i >= 0 and nums[i] >= nums[i + 1]:
            i -= 1
        if i == -1:return nums.reverse()
        j = m - 1
        while nums[j] <= nums[i]:
            j -= 1
        nums[i], nums[j] = nums[j], nums[i]
        nums[i + 1:] = sorted(nums[i + 1:])

556. 下一个更大元素 III

跟31本质上是同一题,先把数值转成列表

class Solution:
    def nextGreaterElement(self, n: int) -> int:
        nums = list(str(n))
        m = len(nums)
        i = m - 2
        while i >= 0 and nums[i] >= nums[i + 1]:  # 与后一个比较
            i -= 1
        if i == -1:return -1
        j = m - 1
        while nums[j] <= nums[i]:  # 与i比较,注意有=
            j -= 1
        nums[i], nums[j] = nums[j], nums[i]
        nums[i + 1:] = nums[i + 1:][::-1]
        res = int(''.join(nums))
        return res if res < 2 ** 31 else -1

217. 存在重复元素

取巧做法:
时间复杂度 : O(n)
空间复杂度 : O(n)

class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        return not len(nums) == len(set(nums))

用字典:

class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        hashset = {}
        for n in nums:
            if n not in hashset:
                hashset[n] = 1
            else:
                return True
        return False

用集合:

class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        hashset = set()
        for n in nums:
            if n not in hashset:
                hashset.add(n)
            else:
                return True
        return False

排序后判断前后数字是否一样
时间复杂度 : O(nlogn)。即排序的时间复杂度。扫描的时间复杂度 O(n) 可忽略。
空间复杂度 : O(1)。 考虑递归调用栈的深度就是O(logn)

class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        nums.sort()
        for i in range(1, len(nums)):
           if nums[i - 1] == nums[i]:
               return True
        return False

775. 全局倒置与局部倒置

数列排序后就就是0,1,2.......N,刚好和下标是对应的.如果一个数和他的下标偏移量超过了1,即是和他的有序排列偏移超过了1,那么全局偏移和局部偏移必然不相等

class Solution:
    def isIdealPermutation(self, nums: List[int]) -> bool:
        for i, n in enumerate(nums):
            if abs(n - i) > 1:
                return False
        return True

1752. 检查数组是否经排序和轮转得到

只有一个或0个数字比后一个大,则可以通过旋转得到

class Solution:
    def check(self, nums: List[int]) -> bool:
        n = len(nums)
        return sum(nums[i] > nums[(i + 1) % n] for i in range(n)) <= 1

1827. 最少操作使数组递增

一次遍历,记录最大值

class Solution:
    def minOperations(self, nums: List[int]) -> int:
        mx = nums[0]
        res = 0
        for i in range(1, len(nums)):
            mx = max(mx + 1, nums[i])
            res += max(0, mx - nums[i])
        return res

2032. 至少在两个数组中出现的值
用intersection函数求集合交集

class Solution:
    def twoOutOfThree(self, nums1: List[int], nums2: List[int], nums3: List[int]) -> List[int]:
        return list(set(list(set(nums1).intersection(set(nums2))) + list(set(nums2).intersection(set(nums3))) + list(set(nums1).intersection(set(nums3)))))

参考资料:
『 4种解法一网打尽 』 有序数组、归并排序、树状数组和线段树的实现及注释

posted @ 2022-05-03 09:35  YTT77  阅读(29)  评论(0编辑  收藏  举报