第 11 天题目

0344. 反转字符串

  • 标签:字符串
  • 难度:简单

题目大意

给定一个字符串数组,将其反转。要求不能使用额外的数组空间。

解题思路

简单题,遍历字符串,将 s[i] 与 s[len-1-i] 交换即可。

代码

def reverseString(self, s: List[str]) -> None:
n = len(s)
for i in range(n//2):
s[i], s[n-1-i] = s[n-1-i], s[i]

0015. 三数之和

  • 标签:数组、双指针
  • 难度:中等

题目大意

给定一个整数数组 nums,判断 nums 中是否存在三个元素 a、b、c,满足 a + b + c = 0。要求找出所有满足要求的不重复的三元组。

解题思路

直接三重遍历查找 a、b、c 的时间复杂度是:LeetCode 课程 Task05 学习打卡(2021年11月25日~11月27日)_职场和发展。我们可以通过一些操作来降低复杂度。

先将数组进行排序,以保证按顺序查找 a、b、c 时,元素值为升序,从而保证所找到的三个元素是不重复的。同时也方便下一步使用双指针减少一重遍历。时间复杂度为:LeetCode 课程 Task05 学习打卡(2021年11月25日~11月27日)_双指针_02

第一重循环遍历 a,对于每个 a 元素,从 a 元素的下一个位置开始,使用双指针 left,right。left 指向 a 元素的下一个位置,right 指向末尾位置。先将 left 右移、right 左移去除重复元素,再进行下边的判断。

  • 若​​nums[a] + nums[left] + nums[right] = 0​​,则得到一个解,将其加入答案数组中,并继续将 left 右移,right 左移;
  • 若​​nums[a] + nums[left] + nums[right] > 0​​,说明 nums[right] 值太大,将 right 向左移;
  • 若​​nums[a] + nums[left] + nums[right] < 0​​,说明 nums[left] 值太小,将 left 右移。

代码

class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
nums.sort()
ans = []

for i in range(n):
if i > 0 and nums[i] == nums[i-1]:
continue
left = i + 1
right = n - 1
while left < right:
while left < right and left > i + 1 and nums[left] == nums[left-1]:
left += 1
while left < right and right < n - 1 and nums[right+1] == nums[right]:
right -= 1
if left < right and nums[i] + nums[left] + nums[right] == 0:
ans.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
elif nums[i] + nums[left] + nums[right] > 0:
right -= 1
else:
left += 1
return ans

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

  • 标签:数组、双指针
  • 难度:中等

题目大意

给定一个有序数组 ​​nums​​。

要求:在原数组空间基础上删除重复出现 2 次以上的元素,并返回删除后数组的新长度。

解题思路

因为数组是有序的,所以重复元素必定是连续的。可以使用双指针来解决。具体做法如下:

使用两个指针 ​​slow​​​,​​fast​​​。​​slow​​​ 指针指向即将放置元素的位置,​​fast​​ 指针指向当前待处理元素。

本题要求相同元素最多出现 2 次,并且 ​​slow - 2​​​ 是上上次放置了元素的位置。则应该检查 ​​nums[slow - 2]​​​ 和当前待处理元素 ​​nums[fast]​​ 是否相同。

  • 如果​​nums[slow - 2] == nums[fast]​​​ 时,此时必有​​nums[slow - 2] == nums[slow - 1] == nums[fast]​​​,则当前​​nums[fast]​​​ 不保留,直接向右移动快指针​​fast​​。
  • 如果​​nums[slow - 2] != nums[fast]​​​ 时,则保留​​nums[fast]​​​。将​​nums[fast]​​​ 赋值给​​nums[slow]​​​ ,同时将​​slow​​​ 右移。然后再向右移动快指针​​fast​​。

这样 ​​slow​​​ 指针左边均为处理好的数组元素,而从 ​​slow​​​ 指针指向的位置开始, ​​fast​​ 指针左边都为舍弃的重复元素。

遍历结束之后,此时 ​​slow​​ 就是新数组的长度。

代码

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

第 12 天题目

0283. 移动零

  • 标签:数组、双指针
  • 难度:简单

题目大意

给你一个数组,将所有 0 移动到末尾,并保持原有的非 0 数字的相对顺序。要求只能在原数组上进行操作。

解题思路

使用两个指针 left,right。left 指向处理好的非 0 数字数组的尾部,right 指针指向当前待处理元素。

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

此时,left 指针左边均为处理好的非零数,而从 left 指针指向的位置开始, right 指针左边都为 0。

遍历结束之后,则所有 0 都移动到了右侧,且保持了非零数的相对位置。

代码

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

0075. 颜色分类

  • 标签:数组、排序、双指针
  • 难度:中等

题目大意

给定一个数组 nums,元素值只有 0、1、2,分别代表红色、白色、蓝色。将数组进行排序,使得 红色在前,白色在中间,蓝色在最后。

要求不使用标准库函数,同时仅用常数空间,一趟扫描解决。

解题思路

使用两个指针 left,right,分别指向数组的头尾。left 表示当前处理好红色元素的尾部,right 表示当前处理好蓝色的头部。

再使用一个下标 index 遍历数组,如果遇到 nums[index] == 0,就交换 nums[index] 和 nums[left],同时将 left 右移。如果遇到 nums[index] == 2,就交换 nums[index] 和 nums[right],同时将 right 左移。

直到 index 移动到 right 位置之后,停止遍历。遍历结束之后,此时 left 左侧都是红色,right 右侧都是蓝色。

注意:移动的时候需要判断 index 和 left 的位置,因为 left 左侧是已经处理好的数组,所以需要判断 index 的位置是否小于 left,小于的话,需要更新 index 位置。

代码

class Solution:
def sortColors(self, nums: List[int]) -> None:
left = 0
right = len(nums) - 1
index = 0
while index <= right:
if index < left:
index += 1
elif nums[index] == 0:
nums[index], nums[left] = nums[left], nums[index]
left += 1
elif nums[index] == 2:
nums[index], nums[right] = nums[right], nums[index]
right -= 1
else:
index += 1

0088. 合并两个有序数组

  • 标签:数组、双指针
  • 难度:简单

题目大意

给定两个有序数组 ​​nums1​​​、​​nums2​​​。将 ​​nums2​​​ 合并到 ​​nums1​​​ 中,使 ​​nums1​​ 成为一个有序数组。

其中给定数组 nums1 空间大小为 m + n 个,其中前 m 个为 nums1 的元素。​​nums2​​​ 空间大小为 n。这样可以用 ​​nums1​​ 的空间来存储最终的有序数组。

解题思路

将两个指针 p1、p2 分别指向 nums1、nums2 元素的尾部,再用一个指针 p 指向数组 nums1 的尾部。从后向前判断当前指针下 nums1[p1] 和 nums[p2] 的值大小,将较大值存入 num1[p] 中,然后继续向前遍历。最后再将 nums 中剩余元素赋值到 num1 前面对应位置上。

代码

class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
p1 = m - 1
p2 = n - 1
p = m + n - 1
while p1 >= 0 and p2 >= 0:
if nums1[p1] < nums2[p2]:
nums1[p] = nums2[p2]
p2 -= 1
else:
nums1[p] = nums1[p1]
p1 -= 1
p -= 1

nums1[:p2+1] = nums2[:p2+1]

第 13 天题目

​0674. 最长连续递增序列​

  • 标签:数组
  • 难度:简单

题目大意

给定一个未经排序的数组 ​​nums​​。要求:找到最长且连续递增的子序列,并返回该序列的长度。

解题思路

因为要求了连续,所以只需要比较相邻的元素大小。我们使用变量 ​​count​​​ 计算当前子序列的递增长度,使用 ​​res​​ 记录最长连续递增子序列长度。然后递推求解即可。

代码

class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int:
size = len(nums)
if size == 0:
return 0
res = 1
count = 1
for i in range(size - 1):
if nums[i + 1] > nums[i]:
count += 1
else:
count = 1
if count > res:
res = count
return res

​1004. 最大连续1的个数 III​

  • 标签:双指针、滑动窗口
  • 难度:中等

题目大意

给定一个由 0、1 组成的数组 nums,再给定一个整数 k。最多可以将 k 个值从 0 变到 1。返回仅包含 1 的最长连续子数组的长度。

解题思路

使用滑动窗口的方法来做。使用两个指针 left、right 指向数组开始位置。使用 max_count 来维护仅包含 1 的最长连续子数组的长度。

不断右移 right 指针,扩大滑动窗口范围,并统计窗口内 0 元素的个数,直到 0 元素的个数超过 k 时将 left 右移,缩小滑动窗口范围,并减小 0 元素的个数。并维护 max_count。

代码

class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
max_count = 0
zero_count = 0
left, right = 0, 0
while right < len(nums):
if nums[right] == 0:
zero_count += 1
right += 1
if zero_count > k:
if nums[left] == 0:
zero_count -= 1
left += 1
max_count = max(max_count, right - left)
return max_count

​0220. 存在重复元素 III​

  • 标签:排序、有序集合、哈希表
  • 难度:中等

题目大意

给定一个整数数组 nums,以及两个整数 k、t。判断数组中是否存在两个不同下标的 i 和 j,其对应元素满足 ​​abs(nums[i] - nums[j]) <= t​​​,同时满足 ​​abs(i - j) <= k​​。如果满足条件则返回 True,不满足条件返回 False。

解题思路

对于第 i 个元素 nums[i],需要查找的区间为 LeetCode 课程 Task05 学习打卡(2021年11月25日~11月27日)_leetcode_03。可以利用桶排序的思想。

桶的大小设置为 t+1。我们将元素按照大小依次放入不同的桶中。

遍历数组 nums 中的元素,对于元素 nums[i] :

  • 如果 nums[i] 放入桶之前桶里已经有元素了,那么这两个元素必然满足​​abs(nums[i] - nums[j]) <= t​​,
  • 如果之前桶里没有元素,那么就将 nums[i] 放入对应桶中。
  • 然后再判断左右桶的左右两侧桶中是否有元素满足​​abs(nums[i] - nums[j]) <= t​​。
  • 然后将 nums[i-k] 之前的桶清空,因为这些桶中的元素与 nums[i] 已经不满足​​abs(i - j) <= k​​ 了。

最后上述满足条件的情况就返回 True,最终遍历完仍不满足条件就返回 False。

代码

class Solution:
def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
bucket_dict = dict()
for i in range(len(nums)):
# 将 nums[i] 划分到大小为 t + 1 的不同桶中
num = nums[i] // (t + 1)

# 桶中已经有元素了
if num in bucket_dict:
return True

# 把 nums[i] 放入桶中
bucket_dict[num] = nums[i]

# 判断左侧桶是否满足条件
if (num - 1) in bucket_dict and abs(bucket_dict[num - 1] - nums[i]) <= t:
return True
# 判断右侧桶是否满足条件
if (num + 1) in bucket_dict and abs(bucket_dict[num + 1] - nums[i]) <= t:
return True
# 将 i-k 之前的旧桶清除,因为之前的桶已经不满足条件了
if i >= k:
bucket_dict.pop(nums[i-k] // (t + 1))

return False