1661 字
8 分钟
代码随想录算法训练营第一天 | 数组 Part01

数组基础#

  • 数组下标从0开始,内存空间地址连续分布。
  • 数组查询时间复杂度为O(1),插入和删除时间复杂度为O(n)(因为需要移动其他元素)。

二分查找#

  • 前提条件:数组必须是有序的,才能使用二分查找快速定位元素。
  • 二分查找时间复杂度为O(log n),相比线性查找的O(n)更加高效。

704. 二分查找#

LeetCode 704. 二分查找

class Solution:
# 二分查找前需要明确区间定义:选择[left, right]还是[left, right)
# 这里采用左闭右闭区间[left, right]
def search(self, nums: List[int], target: int) -> int:
# nums下标范围是[0, len(nums)-1],采用[left, right]区间,所以right初始值为len(nums)-1
left, right = 0, len(nums) - 1
# 当left == right时,区间[left, right]仍然有效,所以条件是left <= right
while left <= right:
mid = left + (right - left) // 2
if nums[mid] > target:
# nums[mid]已经比较过,且大于target,所以新的right范围需要排除mid
right = mid - 1
elif nums[mid] < target:
# nums[mid]已经比较过,且小于target,所以新的left范围需要排除mid
left = mid + 1
else:
return mid
return -1

35.搜索插入位置#

LeetCode 35. 搜索插入位置

class Solution:
# 因为数组有序,我们希望插入后仍然有序
# 插入的合法位置,一定是"从左往右数,第一个不小于 target 的位置"
# 这就是"找第一个 >= target 的位置"
def searchInsert(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] > target:
right = mid - 1
elif nums[mid] < target:
left = mid + 1
else:
return mid
# 二分查找过程中保持的循环不变量:
# - nums[0..left-1] < target
# - nums[right+1..end] > target
# 也就是说,left左边所有元素都 < target,right右边所有元素都 > target
# 当循环结束时,left 正好指向第一个 >= target 的位置(也是插入位置)
return left

34. 在排序数组中查找元素的第一个和最后一个位置#

LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置

class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
left, right = self.leftBound(nums, target), self.rightBound(nums, target)
return [left, right]
# 寻找左边界就是找第一个 >= target 的位置
def leftBound(self, nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
else:
# 寻找左边界:当nums[mid] == target时,继续向左收缩,寻找最左端的target
right = mid - 1
# 循环结束后,left 是最后一个 <= target 的位置
# 检查边界条件:确保left在有效范围内,且nums[left]确实等于target
return left if left >= 0 and left < len(nums) and nums[left] == target else -1
# 寻找右边界就是找到第一个 <= target 的位置
def rightBound(self, nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] > target:
right = mid - 1
else:
# 寻找右边界:当nums[mid] == target时,继续向右收缩,寻找最右端的target
left = mid + 1
# 循环结束后,right 是第一个 >= target 的位置
# 检查边界条件:确保right在有效范围内,且nums[right]确实等于target
return right if right >= 0 and right < len(nums) and nums[right] == target else -1

69. x 的平方根#

LeetCode 69. x 的平方根

class Solution:
# 本质是查找满足 mid * mid <= x 的最大整数 mid
# 即找到最后一个使得平方不超过 x 的数
def mySqrt(self, x: int) -> int:
left, right = 0, x
while left <= right:
mid = left + (right - left) // 2
if mid * mid > x:
right = mid - 1
elif mid * mid < x:
left = mid + 1
else:
return mid
# 循环结束时:
# - 所有小于等于 x 的平方都在 [0, right]
# - 所有大于 x 的平方都在 [left, x]
# 此时 right 是满足 mid*mid <= x 的最大整数,即 x 的整数平方根
return right

367. 有效的完全平方数#

LeetCode 367. 有效的完全平方数

class Solution:
def isPerfectSquare(self, num: int) -> bool:
left, right = 0, num
while left <= right:
mid = left + (right - left) // 2
temp = mid * mid
if temp < num:
left = mid + 1
elif temp > num:
right = mid - 1
else:
return True
return False

双指针#

  • 双指针是处理数组或链表问题的常用算法技巧。
  • 双指针主要分为两种类型:
    • 快慢指针:通常用于链表,快指针每次移动两步,慢指针每次移动一步,用于检测环或寻找中点
    • 左右指针:在数组或字符串中使用两个指针分别从两端向中间移动,常用于查找满足条件的子数组或子字符串

27. 移除元素#

LeetCode 27. 移除元素

class Solution:
# 双指针思路:slow指针指向新数组的下一个待填充位置,fast指针遍历原数组
# 当fast指向的值等于val时,fast继续向前移动(跳过该元素)
# 当fast指向的值不等于val时,将该值复制到slow位置,然后slow和fast都向前移动
def removeElement(self, nums: List[int], val: int) -> int:
slow = 0
for fast in range(len(nums)):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
return slow

283. 移动零#

LeetCode 283. 移动零

class Solution:
# 思路与上题相同:将val改为0,额外步骤是将数组后面的位置全部填充为0
def moveZeroes(self, nums: List[int]) -> None:
slow = 0
for fast in range(len(nums)):
if nums[fast] != 0:
nums[slow] = nums[fast]
slow += 1
# 将slow之后的所有位置填充为0
for i in range(slow, len(nums)):
nums[i] = 0

844. 比较含退格的字符串#

LeetCode 844. 比较含退格的字符串

class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
s = self.traverse(s)
t = self.traverse(t)
return s == t
def traverse(self, s):
s = list(s)
slow = 0
for fast in range(len(s)):
if s[fast] != '#':
s[slow] = s[fast]
slow += 1
else:
# 关键处理:当fast指向'#'时,需要模拟退格操作
# slow向后退一格(如果可能),表示删除前一个字符
if slow > 0:
slow -= 1
return ''.join(s[:slow])

977. 有序数组的平方#

LeetCode 977. 有序数组的平方

class Solution:
# 左右双指针策略:由于存在负数,平方后最大值只能出现在数组两端
# 比较左右指针位置元素的平方大小,将较大值放入结果数组的末尾
# 然后移动对应指针,继续比较,直到处理完所有元素
def sortedSquares(self, nums: List[int]) -> List[int]:
left, right = 0, len(nums) - 1
res = [0] * len(nums)
cur = len(nums) - 1 # 结果数组的填充位置,从后往前填充
while left <= right:
multi_left = nums[left] * nums[left]
multi_right = nums[right] * nums[right]
if multi_left >= multi_right:
res[cur] = multi_left
left += 1
else:
res[cur] = multi_right
right -= 1
cur -= 1
return res
代码随想录算法训练营第一天 | 数组 Part01
https://fuwari.vercel.app/posts/code_musings_day_one/
作者
Jarrett
发布于
2025-06-11
许可协议
CC BY-NC-SA 4.0