在刷 LeetCode Hot100 的过程中,处理数组(List)、字符串、矩阵等可迭代对象时,"同时获取索引和元素值" 是高频需求 ------ 比如找目标元素的位置、双指针遍历标记、矩阵行列定位等。Python 内置的 enumerate 函数正是用来解决这类问题的。本文结合 LeetCode Hot100 高频考题,拆解 enumerate 的核心用法、进阶技巧,以及在实际刷题中的落地场景。
一、enumerate 函数的核心基础
1. 什么是 enumerate?
enumerate 直译是 "枚举",是 Python 内置函数,核心作用是遍历可迭代对象时,同时返回元素的 "索引" 和 "值"。它底层由 C 实现,执行效率略高于手动维护索引的写法,是刷题时的首选。
2. 基础语法
python
enumerate(iterable, start=0)
- 参数 :
iterable:必需,可迭代对象(List、字符串、元组、矩阵等);start:可选,索引起始值,默认 0(可根据题目需求设为 1、-1 等)。
- 返回值 :枚举对象(迭代器),每次迭代返回
(索引, 元素值)元组。
3. 告别手动索引
python
# 传统写法
nums = [1, 3, 5, 7]
i = 0
for val in nums:
print(f"索引 {i}:值 {val}")
i += 1
# enumerate 写法
for idx, val in enumerate(nums):
print(f"索引 {idx}:值 {val}")
# 自定义索引起始值(比如题目要求索引从 1 开始)
for idx, val in enumerate(nums, start=1):
print(f"索引 {idx}:值 {val}")
二、LeetCode Hot100 中的高频应用场景
一、题目 1:两数之和(LeetCode 1,Hot100 长期在榜)
题目原文:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。假设每种输入只会对应一个答案,数组中同一个元素在答案里不能重复出现,可按任意顺序返回答案。
传统暴力解法:
python
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
n=len(nums)
for i in range(n):
for j in range(i+1,n):
if target == nums[i]+nums[j]:
return[i,j]
传统暴力解法需要两层循环(时间复杂度 O(n2)),而哈希表可以将「查找另一个数」的操作从 O(n) 降到 O(1),整体时间复杂度优化为 O(n),空间复杂度 O(n)(存储哈希表):
python
from typing import List # 导入List类型注解,用于指定参数和返回值类型
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
"""
给定一个整数数组 nums 和一个整数目标值 target,
请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,且同一个元素不能使用两遍。
你可以按任意顺序返回答案。
:param nums: 整数数组,存储待查找的数字
:param target: 目标和,需要找到两个数相加等于该值
:return: 包含两个数下标List[int],顺序无要求
"""
# 初始化一个空字典(哈希表),用于存储「数字: 下标」的映射
# 作用:将遍历过的数字和其下标记录下来,后续可以O(1)时间查询是否存在目标差值
hashtable = dict()
# 遍历数组,enumerate同时返回下标i和对应数字num
for i, num in enumerate(nums):
# 计算目标差值:target - 当前数字 = 需要找的另一个数
complement = target - num
# 检查哈希表中是否已经存在这个差值
# 如果存在,说明之前遍历过的某个数字和当前数字相加等于target
if complement in hashtable:
# 返回「差值对应的下标」和「当前数字的下标」,即为答案
return [hashtable[complement], i]
# 如果不存在,将当前数字和其下标存入哈希表,供后续遍历查询
# 注意:先检查再存入,避免同一个元素被重复使用(比如num=3, target=6时,不会匹配到自己)
hashtable[nums[i]] = i
# 题目保证有且仅有一个答案,理论上不会执行到这里,返回空列表作为兜底
return []
核心场景:需要 "值 + 下标" 绑定且下标是答案的一部分
enumerate的核心作用:遍历数组时,既要获取元素值(计算补数target-num),又要记录该值对应的下标(最终需要返回下标作为答案)。
适用细节:
- 题目要求返回 "满足条件的元素下标",而非元素值,因此必须把 "值" 和 "其原始下标" 绑定;
enumerate省去了 "手动维护下标变量i+i+=1" 的操作,且让 "值 - 下标" 的映射在循环中直接可用,避免先存所有 "值 - 下标" 再二次遍历的冗余。
二、题目 2:移动零(LeetCode 283,Hot100 高频数组题)
题目原文:
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。要求原地操作,不拷贝额外数组,尽量减少操作次数。
非enumerate解法:
python
class Solution(object):
def moveZeroes(self, nums):
n=len(nums)
left = right = 0
while right<n:
if nums[right]!=0:
nums[left],nums[right]=nums[right],nums[left]
left+=1
right+=1
enumerate解法:
python
class Solution(object):
def moveZeroes(self, nums):
slow = 0 # 慢指针:记录非零元素应存放的位置,初始为0
# enumerate的fast_idx作为快指针,遍历数组筛选非零元素
for fast_idx, val in enumerate(nums):
if val != 0: # 遇到非零元素,与慢指针位置交换并后移慢指针
nums[slow], nums[fast_idx] = nums[fast_idx], nums[slow]
slow += 1
核心场景:双指针遍历,快指针需同时获取 "索引 + 值" 做筛选
enumerate的核心作用:快指针遍历数组时,既要通过val判断 "是否为 0"(筛选非零元素),又要通过fast_idx(enumerate 的索引)与慢指针交换元素(原地修改数组)。
适用细节:
- 题目要求 "原地修改数组",需要通过索引定位元素位置进行交换;
enumerate让快指针的 "索引(交换位置)" 和 "值(筛选条件)" 在同一循环变量中,无需单独通过nums[i]取值,逻辑更紧凑。
三、题目 3:找到字符串中所有字母异位词(LeetCode 438,Hot100 滑动窗口题)
题目原文
给定两个字符串 s 和 p,找到 s 中所有 p 的 字母异位词 的子串,返回这些子串的起始索引。字母异位词指由相同字母重排列形成的字符串(包括相同字符串),不考虑答案输出的顺序。
非enumerate写法:
python
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str # 原字符串
:type p: str # 目标异位词的基准字符串
:rtype: List[int] # 返回所有异位词在s中的起始索引
"""
# 初始化结果列表
result = []
# 获取p和s的长度
len_p, len_s = len(p), len(s)
# 边界条件:如果p比s长,不可能有异位词,直接返回空列表
if len_p > len_s:
return result
# 初始化两个数组,统计26个小写字母的出现次数(ASCII码映射)
# ord('a') = 97,所以用 ord(c) - ord('a') 得到0-25的索引
count_p = [0] * 26
count_window = [0] * 26
# 第一步:统计p的字符计数,同时初始化滑动窗口的初始状态(前len_p个字符)
for i in range(len_p):
# 统计p的字符
count_p[ord(p[i]) - ord('a')] += 1
# 统计s前len_p个字符(初始窗口)
count_window[ord(s[i]) - ord('a')] += 1
# 检查初始窗口是否匹配
if count_p == count_window:
result.append(0)
# 第二步:滑动窗口遍历s的剩余部分(从len_p到len_s-1)
# 窗口起始索引从1开始,结束索引从len_p开始
for right in range(len_p, len_s):
# 移出窗口左侧的字符(窗口起始索引是 right - len_p)
left_char = s[right - len_p]
count_window[ord(left_char) - ord('a')] -= 1
# 移入窗口右侧的新字符
right_char = s[right]
count_window[ord(right_char) - ord('a')] += 1
# 检查当前窗口是否匹配p的字符计数
if count_p == count_window:
# 记录当前窗口的起始索引
result.append(right - len_p + 1)
return result
enumerate写法:
python
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
result = []
len_p, len_s = len(p), len(s)
# 边界条件:p比s长直接返回空
if len_p > len_s:
return result
# 初始化26字母计数数组
count_p = [0] * 26
count_window = [0] * 26
# 第一步:统计p的字符,同时初始化窗口前len_p个字符(用enumerate遍历)
# 遍历p的字符,统计计数
for idx, char in enumerate(p):
count_p[ord(char) - ord('a')] += 1
# 遍历s前len_p个字符,初始化窗口(用enumerate更清晰)
for idx, char in enumerate(s[:len_p]):
count_window[ord(char) - ord('a')] += 1
# 检查初始窗口是否匹配
if count_p == count_window:
result.append(0)
# 第二步:滑动窗口(用enumerate遍历s中从len_p开始的部分)
# 遍历的是s[len_p:],enumerate的start参数指定索引从len_p开始(对应原s的索引)
for right_idx, right_char in enumerate(s[len_p:], start=len_p):
# 计算要移出窗口的左侧字符的索引
left_idx = right_idx - len_p
left_char = s[left_idx]
# 移出左侧字符:计数-1
count_window[ord(left_char) - ord('a')] -= 1
# 移入右侧新字符:计数+1
count_window[ord(right_char) - ord('a')] += 1
# 检查当前窗口是否匹配,匹配则记录起始索引(left_idx + 1)
if count_p == count_window:
result.append(left_idx + 1)
return result
核心场景:滑动窗口遍历,需索引计算窗口边界 + 值维护窗口状态
enumerate的核心作用:遍历字符串时,既要通过char(值)更新窗口字符计数,又要通过idx(索引)计算窗口左边界(idx - p_len + 1)、判断窗口长度是否达标。
适用细节:
- 题目要求返回 "异位词子串的起始索引",需要通过索引定位窗口位置;
enumerate让 "当前字符" 和 "当前位置" 直接绑定,避免手动计算索引时的错位(比如窗口左边界计算错误)。
四、题目4:矩阵置零(LeetCode 73)
题目原文
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用 原地 算法。
进阶:能否使用 O (1) 额外空间?
非enumerate解法:
python
class Solution(object):
def setZeroes(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
"""
# 初始化两个集合,分别存储需要置零的行和列
rows = set()
cols = set()
# 第一步:遍历矩阵,标记需要置零的行和列
m = len(matrix) # 矩阵的行数
if m == 0:
return
n = len(matrix[0]) # 矩阵的列数
for i in range(m):
for j in range(n):
if matrix[i][j] == 0:
rows.add(i)
cols.add(j)
# 第二步:根据标记的行和列,将对应位置置零
for i in range(m):
for j in range(n):
if i in rows or j in cols:
matrix[i][j] = 0
enumerate解法:
python
class Solution(object):
def setZeroes(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
"""
# 初始化两个集合,分别存储需要置零的行和列
rows = set()
cols = set()
# 第一步:遍历矩阵,用enumerate获取行索引+行数据,标记需要置零的行和列
# 外层enumerate:i是行索引,row是当前行的所有元素
for i, row in enumerate(matrix):
# 内层enumerate:j是列索引,val是当前位置的元素值
for j, val in enumerate(row):
if val == 0:
rows.add(i) # 标记需要置零的行
cols.add(j) # 标记需要置零的列
# 第二步:再次遍历矩阵,根据标记置零(同样用enumerate)
for i, row in enumerate(matrix):
for j, _ in enumerate(row): # 只需要列索引j,元素值用_占位即可
if i in rows or j in cols:
matrix[i][j] = 0
-
遍历需同时用 "索引 + 值":
-
外层
enumerate(matrix):i是行索引(用于标记首列matrix[i][0]),row是行元素(用于内层遍历); -
内层
enumerate(row):j是列索引(用于标记首行matrix[0][j]),val是元素值(判断是否为 0);两者缺一不可,完全符合 "要值也要索引" 的核心场景。
-
-
原地修改依赖索引定位 :题目要求 "原地算法",置零操作需要通过行索引
i、列索引j精准定位元素位置(如matrix[i][j] = 0),enumerate省去了手动维护i/j自增的麻烦。
三、enumerate 进阶技巧
1. 与列表推导式结合:一行生成 "值 - 索引" 映射
刷题中常需要快速查找元素的位置,用 enumerate + 字典推导式可一键生成映射表:
python
# 生成"元素值: 作为字典的"键"(适用于无重复元素的数组)
nums = [2, 7, 11, 15]
# 字典推导式 + enumerate:键是元素值,值是对应索引
val2idx = {val: idx for idx, val in enumerate(nums)}
print(val2idx[7]) # 输出:1(快速找到7的索引)
适用场景:Hot100 两数之和(快速通过值找索引,时间复杂度从 O (n²) 降为 O (n))。
2. 转换为列表:查看枚举结果(调试 / 验证用)
enumerate 返回的是迭代器,只能遍历一次;若需多次使用,可转为列表:
python
nums = [10, 20, 30]
# 将枚举对象转为列表,方便查看所有(索引,值)对
enum_list = list(enumerate(nums))
print(enum_list) # 输出:[(0, 10), (1, 20), (2, 30)]
3. 自定义 start:适配题目索引要求
部分题目要求索引从 1 开始(如 "第 k 个元素"),只需设置 start=1:
python
nums = [5, 3, 8]
# start=1 让索引从1开始,符合题目"第k个元素"的表述习惯
for idx, val in enumerate(nums, start=1):
print(f"第 {idx} 个元素:{val}") # 输出:第1个元素:5,第2个元素:3...
四、使用 enumerate 的注意事项
-
索引是 "遍历顺序" 而非 "原数组实际索引" :若遍历的是切片 / 过滤后的数组,索引是相对索引,而非原数组索引:
pythonnums = [1, 2, 3, 4] # 遍历切片 nums[2:](元素是3、4),enumerate 的索引从0开始,而非原数组的2 for idx, val in enumerate(nums[2:]): print(idx, val) # 输出:0 3;1 4 -
不要遍历中修改原对象:与普通 for 循环同理,遍历 List / 矩阵时增删元素,会导致枚举异常;
-
多维数组注意索引层级 :嵌套 enumerate 时,外层
i是行索引,内层j是列索引,避免混淆。