文章目录
简介:
这是由 LeetCode 官方推出的经典面试题目清单,将题目分为以下三个部分:
- 初级算法 - 帮助入门
- 中级算法 - 巩固训练
- 高级算法 - 提升进阶
这一系列 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]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
相关标签 数组
双指针
思路及解法
使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
注意到以下性质:
-
左指针左边均为非零数;
-
右指针左边直到左指针处均为零。
因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。
输入:
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