python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python  enumerate 函数

Python 中 enumerate 函数的巧妙用法

作者:铉铉这波能秀

enumerate 直译是"枚举",是Python内置函数,核心作用是遍历可迭代对象时,同时返回元素的"索引"和"值",文章介绍了Python内置的enumerate函数,该函数可以同时获取可迭代对象的索引和元素值,感兴趣的朋友跟随小编一起看看吧

在算法题目中,处理数组(List)、字符串、矩阵等可迭代对象时,“同时获取索引和元素值” 是高频需求 —— 比如找目标元素的位置、双指针遍历标记、矩阵行列定位等。Python 内置的 enumerate 函数正是用来解决这类问题的。

一、enumerate 函数的核心基础

1. 什么是 enumerate?

enumerate 直译是 “枚举”,是 Python 内置函数,核心作用是遍历可迭代对象时,同时返回元素的 “索引” 和 “值”。它底层由 C 实现,执行效率略高于手动维护索引的写法,是刷题时的首选。

2. 基础语法

enumerate(iterable, start=0)

3. 告别手动索引

# 传统写法
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 的那两个整数,并返回它们的数组下标。假设每种输入只会对应一个答案,数组中同一个元素在答案里不能重复出现,可按任意顺序返回答案。

传统暴力解法:

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)(存储哈希表):

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),又要记录该值对应的下标(最终需要返回下标作为答案)。

适用细节:

二、题目 2:移动零(LeetCode 283,Hot100 高频数组题)

题目原文:

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。要求原地操作,不拷贝额外数组,尽量减少操作次数。

非enumerate解法:

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解法:

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 的索引)与慢指针交换元素(原地修改数组)。

适用细节:

三、题目 3:找到字符串中所有字母异位词(LeetCode 438,Hot100 滑动窗口题)

题目原文

给定两个字符串 sp,找到 s 中所有 p字母异位词 的子串,返回这些子串的起始索引。字母异位词指由相同字母重排列形成的字符串(包括相同字符串),不考虑答案输出的顺序。

非enumerate写法:

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写法:

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)、判断窗口长度是否达标。

适用细节:

四、题目4:矩阵置零(LeetCode 73)

题目原文

给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用 原地 算法。

进阶:能否使用 O (1) 额外空间?

非enumerate解法:

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解法:

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 进阶技巧

1. 与列表推导式结合:一行生成 “值 - 索引” 映射

刷题中常需要快速查找元素的位置,用 enumerate + 字典推导式可一键生成映射表:

# 生成“元素值: 作为字典的“键”(适用于无重复元素的数组)
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 返回的是迭代器,只能遍历一次;若需多次使用,可转为列表:

nums = [10, 20, 30]
# 将枚举对象转为列表,方便查看所有(索引,值)对
enum_list = list(enumerate(nums))
print(enum_list)  # 输出:[(0, 10), (1, 20), (2, 30)]

3. 自定义 start:适配题目索引要求

部分题目要求索引从 1 开始(如 “第 k 个元素”),只需设置 start=1

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 的注意事项

  1. 索引是 “遍历顺序” 而非 “原数组实际索引”:若遍历的是切片 / 过滤后的数组,索引是相对索引,而非原数组索引:
    nums = [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
  2. 不要遍历中修改原对象:与普通 for 循环同理,遍历 List / 矩阵时增删元素,会导致枚举异常;
  3. 多维数组注意索引层级:嵌套 enumerate 时,外层 i 是行索引,内层 j 是列索引,避免混淆。

到此这篇关于Python 中 enumerate 函数的妙用的文章就介绍到这了,更多相关Python enumerate 函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文