【LeetCode】初级算法:一、数组


简介:

这是由 LeetCode 官方推出的经典面试题目清单,将题目分为以下三个部分:

  1. 初级算法 - 帮助入门
  2. 中级算法 - 巩固训练
  3. 高级算法 - 提升进阶

这一系列 LeetBook 将帮助掌握算法及数据结构,并提高编程能力。

我将这一部分的题目内容和我自己练习的答案记录成文章,方便以后查阅、纠正和复习。


数组问题在面试中出现频率很高,你极有可能在面试中遇到。我们推荐以下题目:只出现一次的数字旋转数组两个数组的交集 II两数之和。”


1.1 删除排序数组中的重复项

给你一个有序数组nums ,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(1)额外空间的条件下完成。

示例 1:

输入:nums = [1,1,2]

输出:2, nums = [1,2]

解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]

输出:5, nums = [0,1,2,3,4]

解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。


官方答案:

# 官方答案
class solution:
    def remove(self,nums):
        if not nums:
            return 0
        n = len(nums)
        slow = fast = 1
        while fast < n:
            if nums[fast] != nums[fast - 1]:
                nums[slow] = nums[fast]
                slow += 1
            fast += 1
        return slow, nums[:slow]

自练:

# 自练
class solution:
    def remove(self,nums):
        if not nums:
            return 0
        n = len(nums)
        left = 0
        right = 1
        while right < n :
            if nums[right] != nums[left]:              
                nums[left+1] = nums[right]         
                left += 1    
            right += 1
        return left+1, nums[:left+1]

输入:

nums = [0,0,1,2,3,4,4,4,5,5,6]
print(solution().remove(nums))

输出:

(7, [0, 1, 2, 3, 4, 5, 6])

执行用时:28 ms, 在所有 Python 提交中击败了51.14%的用户

内存消耗:13.7 MB, 在所有 Python 提交中击败了77.73%的用户

通过测试用例:362 / 362


1.2 买卖股票的最佳时机 II

给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: prices = [7,1,5,3,6,4]

输出: 7

解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。

随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例 2:

输入: prices = [1,2,3,4,5]

输出: 4

解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。

注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入: prices = [7,6,4,3,1]

输出: 0

解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。


输入:

class Solution:
    def maxProfit(self, prices):
        profit = 0
        for i in range(len(prices)):
            if i > 0 and prices[i] - prices[i-1] > 0:
                profit += prices[i] - prices[i-1]
        return profit


prices = [7,1,5,3,6,4]
Solution().maxProfit(prices)

输出:

7

执行用时: 48 ms, 在所有 Python3 提交中击败了15.77%的用户

内存消耗: 15.6 MB, 在所有 Python3 提交中击败了33.59%的用户

通过测试用例: 200 / 200


1.3 旋转数组

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3

输出: [5,6,7,1,2,3,4]

解释:

向右轮转 1 步: [7,1,2,3,4,5,6]

向右轮转 2 步: [6,7,1,2,3,4,5]

向右轮转 3 步: [5,6,7,1,2,3,4]

示例 2:

输入:nums = [-1,-100,3,99], k = 2

输出:[3,99,-1,-100]

解释:

向右轮转 1 步: [99,-1,-100,3]

向右轮转 2 步: [3,99,-1,-100]


法1:
输入:

# 最好inplace操作
class Solution:
    def rotate(self, nums, k):
        """
        Do not return anything, modify nums in-place instead.
        """
        nums1 = nums.copy()
        k = k%len(nums1)
        if len(nums) > 1:
            for i in range(len(nums1)):
                if k + i <= len(nums1) - 1:
                    nums[k + i] = nums1[i]
                else:
                    new = k + i - len(nums1)
                    nums[new] = nums1[i]


nums = [1,2]
Solution().rotate(nums,3)
nums

输出:

[2, 1]

执行用时: 60 ms, 在所有 Python3 提交中击败了22.18%的用户

内存消耗: 20.9 MB, 在所有 Python3 提交中击败了41.19%的用户

通过测试用例:38 / 38


法2:
输入:

class Solution:
    def rotate(self, nums, k):
        """
        Do not return anything, modify nums in-place instead.
        """
        l = len(nums)
        if k > l:
            k -= l%k

        nums[:] = nums[l - k:] + nums[:l - k]

nums = [1,2,3,4,5,6,7]
Solution().rotate(nums,3)
nums

输出:

[5,6,7,1,2,3,4]

执行用时:44 ms, 在所有 Python3 提交中击败了70.68%的用户

内存消耗:21 MB, 在所有 Python3 提交中击败了58.39%的用户

通过测试用例:38 / 38


1.4 存在重复元素

给定一个整数数组,判断是否存在重复元素。

如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

示例 1:

输入: [1,2,3,1]

输出: true

示例 2:

输入: [1,2,3,4]

输出: false

示例 3:

输入: [1,1,1,3,3,4,3,2,4,2]

输出: true


法1
输入:

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

nums = [1,2,3,1]
Solution().containsDuplicate(nums)

输出:

True

执行用时:108 ms, 在所有 Python 提交中击败了13.14%的用户

内存消耗:20 MB, 在所有 Python 提交中击败了85.25%的用户

通过测试用例:71 / 71


法2
输入:

class Solution(object):
    def containsDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        return True if len(nums) > len(set(nums)) else False


nums = [1,2,3,1]
Solution().containsDuplicate(nums)

输出:

True

执行用时:16 ms, 在所有 Python 提交中击败了96.32%的用户

内存消耗:17.3 MB, 在所有 Python 提交中击败了31.75%的用户

通过测试用例:20 / 20


1.5 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]

输出: 1

示例 2:

输入: [4,1,2,1,2]

输出: 4


法1
输入:

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        nums.sort()
        for i in range(len(nums)-1):
            if i%2 == 0 and nums[i] != nums[i+1]:
                return nums[i]
        return nums[-1]

nums = [4,1,2,1,2]
Solution().singleNumber(nums)

输出:

4

执行用时:56 ms, 在所有 Python3 提交中击败了23.88%的用户

内存消耗:17 MB, 在所有 Python3 提交中击败了38.73%的用户

通过测试用例:61 / 61


法2
输入:
高级用法异或:^
0异或任何数不变,任何数与自己异或为0。a⊕b⊕a=b。异或满足加法结合律和交换律。

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        result = 0
        for num in nums:
            result ^= num
        return result


nums = [2,2,1]
Solution().singleNumber(nums)

输出:

1

执行用时:20 ms, 在所有 Python 提交中击败了91.87%的用户

内存消耗:14.4 MB, 在所有 Python 提交中击败了89.62%的用户

通过测试用例:61 / 61


1.6 两个数组的交集 II

给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]

输出:[2,2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]

输出:[4,9]


class Solution(object):
    def intersect(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        result = []
        equel_index = -1
        nums1.sort()
        nums2.sort()
        if len(nums1) >= len(nums2):
            for num2 in nums2:
                if equel_index+1 < len(nums1):
                    for i1 in range(equel_index+1, len(nums1)):
                        if num2 == nums1[i1]:
                            result.append(num2)
                            equel_index = i1
                            break
        else:
            for num1 in nums1:
                if equel_index+1 < len(nums2):
                    for i2 in range(equel_index+1, len(nums2)):
                        if num1 == nums2[i2]:
                            result.append(num1)
                            equel_index = i2
                            break
        return result


nums1 = [1,2,2,1]
nums2 = [2,2]
Solution().intersect(nums1,nums2)

输出:

[2, 2]

执行用时:40 ms, 在所有 Python 提交中击败了15.44%的用户

内存消耗:13.1 MB, 在所有 Python 提交中击败了77.41%的用户

通过测试用例:55 / 55


1.7 加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]

输出:[1,2,4]

解释:输入数组表示数字 123。

示例 2:

输入:digits = [4,3,2,1]

输出:[4,3,2,2]

解释:输入数组表示数字 4321。

示例 3:

输入:digits = [0]

输出:[1]


输入:

class Solution(object):
    def plusOne(self, digits):
        """
        :type digits: List[int]
        :rtype: List[int]
        """
        return [int(i) for i in str(int(''.join([str(i) for i in digits]))+1)]


nums = [1,2,3,9]
Solution().plusOne(nums)

输出:

[1, 2, 4, 0]

执行用时:20 ms, 在所有 Python 提交中击败了37.11%的用户

内存消耗:13 MB, 在所有 Python 提交中击败了55.45%的用户

通过测试用例:111 / 111


1.8 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]

输出: [1,3,12,0,0]

说明:

必须在原数组上操作,不能拷贝额外的数组。

尽量减少操作次数。

相关标签 数组 双指针


思路及解法

使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。

右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。

注意到以下性质:

  1. 左指针左边均为非零数;

  2. 右指针左边直到左指针处均为零。

因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。


输入:

class Solution(object):
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        left = right = 0
        while right < len(nums):
            if nums[right] != 0:
                nums[left], nums[right] = nums[right], nums[left]               
                left += 1
            right += 1
        return nums


nums = [0,1,0,3,12]
Solution().moveZeroes(nums)

输出:

[1, 3, 12, 0, 0]

执行用时:28 ms, 在所有 Python 提交中击败了75.75%的用户

内存消耗:13.9 MB, 在所有 Python 提交中击败了82.12%的用户

通过测试用例:74 / 74


1.9 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9

输出:[0,1]

解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6

输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6

输出:[0,1]


提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

进阶: 你可以想出一个时间复杂度小于 O(n2) 的算法吗?

相关标签 数组 哈希表


法1: 暴力

输入:

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        for i in range(len(nums)-1):
            for j in range(i+1, len(nums)):
                if nums[i] + nums[j] == target:
                    return i, j

nums, target = [3,2,3], 6
Solution().twoSum(nums, target)

输出:

(0, 2)

执行用时:2792 ms, 在所有 Python3 提交中击败了33.59%的用户

内存消耗:13.5 MB, 在所有 Python 提交中击败了87.62%的用户

通过测试用例:57 / 57


法2
输入:

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        dict_nums = {}
        for i, num in enumerate(nums):
            tmp = target - num
            if dict_nums.get(tmp) is None:
                dict_nums[num] = i
            else:
                return dict_nums[tmp], i

nums, target = [2,11,15,7],9
Solution().twoSum(nums, target)

输出:

(0, 3)

执行用时:40 ms, 在所有 Python3 提交中击败了77.68%的用户

内存消耗:16.4 MB, 在所有 Python3 提交中击败了9.12%的用户

通过测试用例:57 / 57


法3: 理想状态是只做一次for循环,通过target减左指针元素的差确定右指针的位置

输入:

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        right = 0
        for left in range(len(nums)):
            if left != len(nums) - 1:
                right_num = target - nums[left]
                if right_num in nums:
                    index = [idx for idx, num in enumerate(nums) if num == right_num]
                    if len(index) == 1:
                        if left != index[0]:
                            right = index[0]
                        else:
                            left += 1
                            continue
                    else:
                        right = index[1]
                    break
                left += 1
        return left, right

    
nums, target = [2,11,15,7],9
Solution().twoSum(nums, target)

输出:

(0, 3)

执行用时:416 ms, 在所有 Python 提交中击败了47.24%的用户

内存消耗:13.6 MB, 在所有 Python 提交中击败了81.94%的用户

通过测试用例:57 / 57


1.11 旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

示例 1:
请添加图片描述
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]

输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2:

请添加图片描述
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]

输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

示例 3:

输入:matrix = [[1]]

输出:[[1]]

示例 4:

输入:matrix = [[1,2],[3,4]]

输出:[[3,1],[4,2]]

提示:

  • matrix.length == n
  • matrix[i].length == n
  • 1 <= n <= 20
  • -1000 <= matrix[i][j] <= 1000

相关标签 数组 数学 矩阵


思路:先转置(沿对角线翻转),再将每行左右元素对调

输入:

class Solution(object):
    def rotate(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: None Do not return anything, modify matrix in-place instead.
        """
        width = len(matrix)
        # 1.转置(对角线翻转)
        for row in range(width):
           	for col in range(row + 1, width):
                matrix[row][col], matrix[col][row] = matrix[col][row], matrix[row][col]
        # 2.左右对调(沿左边缘镜像)
        for row in range(width):
        	col = 0
            while col < width / 2:
                matrix[row][col], matrix[row][-1 - col] = matrix[row][-1 - col], matrix[row][col]
                col += 1


matrix = [[5, 1, 9, 11],
          [2, 4, 8, 10],
          [13, 3, 6, 7],
          [15, 14, 12, 16]]
Solution().rotate(matrix)

输出:

[[15, 13,  2,  5],
 [14,  3,  4,  1],
 [12,  6,  8,  9],
 [16,  7, 10, 11]]

执行用时:8 ms, 在所有 Python 提交中击败了98.61%的用户

内存消耗:12.9 MB, 在所有 Python 提交中击败了88.41%的用户

通过测试用例:21 / 21

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值