除了使用dict.fromkeys()和OrderedDict.fromkeys(),还有哪些方法可以实现列表去重?

除了 dict.fromkeys()OrderedDict.fromkeys(),Python 中还有多种列表去重方法,核心差异集中在 是否保留原顺序、时间复杂度、内存占用 三个维度。以下是 6 种常用方法,按 "日常实用性 + 效率" 排序,附带原理、代码示例和场景适配:

一、核心前提:去重的两大核心需求

  • 需求 1:保留原顺序(日常开发最常用,如去重后仍保持元素首次出现的顺序);
  • 需求 2:无需保留顺序(追求极致效率或不关心顺序,如数据统计、去重后排序)。

所有方法均围绕这两个需求展开,底层逻辑本质是 "通过容器记录已出现的元素,过滤重复项"。

二、6 种列表去重方法详解

方法 1:列表推导式 + 辅助集合(推荐,保序 + 高效)

原理

  • 列表推导式 遍历原列表,保留首次出现的元素;
  • 辅助集合seen)记录已出现的元素(集合查找效率 O (1),比列表查找快);
  • 核心逻辑:遍历元素时,若元素不在 seen 中,则保留并加入 seen;否则跳过。

代码示例

python

运行

ini 复制代码
lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
seen = set()
result = [x for x in lst if x not in seen and not seen.add(x)]
print(result)  # 输出:[1, 2, 3, 4, 5](保留原顺序)

关键细节

  • not seen.add(x)set.add(x) 无返回值(返回 None),not NoneTrue,因此可简化为 "判断 + 添加" 一步完成;
  • 时间复杂度:O (n)(遍历列表 O (n) + 集合查找 O (1));
  • 内存占用:O (n)(辅助集合存储所有不重复元素)。

优点

  • 保留原顺序,效率高(比 dict.fromkeys() 更灵活,可自定义过滤逻辑);
  • 支持异构列表(如包含字符串、元组等不可变元素)。

适用场景

  • 日常开发中 "保序去重" 的核心场景(替代 dict.fromkeys(),逻辑更直观)。

方法 2:set 转列表(最快,无需保序)

原理

  • 利用 集合(set)的元素唯一性,直接将列表转为集合(自动去重),再转回列表;
  • 集合是无序结构,因此去重后会打乱原顺序。

代码示例

python

运行

scss 复制代码
lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
result = list(set(lst))
print(result)  # 输出:[1, 2, 3, 4, 5](顺序不固定,如 [2,1,3,4,5])

关键细节

  • 时间复杂度:O (n)(集合插入和转换均为线性时间,底层哈希表优化);
  • 内存占用:O (n)(集合存储不重复元素);
  • 限制:仅支持 不可变元素 (如 int、str、tuple),若列表包含可变元素(如列表、字典),会报错(TypeError: unhashable type: 'list')。

优点

  • 代码极简(一行完成),速度最快(CPython 底层优化,比纯 Python 逻辑快一个量级);
  • 自动去重 + 去重后可按需排序(如 sorted(list(set(lst))))。

适用场景

  • 无需保留原顺序,且元素为不可变类型(如数据统计、去重后排序)。

方法 3:pandasdrop_duplicates()(大数据量 + 保序)

原理

  • 利用 pandasSeries 数据结构,通过 drop_duplicates() 方法去重;
  • 底层用 C 语言优化,大数据量(十万级以上)效率远超纯 Python 方法。

代码示例

python

运行

scss 复制代码
import pandas as pd

lst = [1, 2, 2, 3, 3, 3, 4, 5, 5] * 1000  # 模拟 9000 个元素的大数据量
result = pd.Series(lst).drop_duplicates().tolist()
print(result)  # 输出:[1, 2, 3, 4, 5](保留原顺序)

关键细节

  • 时间复杂度:O (n)(底层哈希表实现,线性时间);
  • 内存占用:O (n)(Series 存储数据);
  • 优势:支持缺失值(NaN)去重,且大数据量下比纯 Python 方法快 10~100 倍。

优点

  • 大数据量处理高效,保留原顺序;
  • 支持复杂数据类型(如 datetimeobject 类型)。

缺点

  • 需安装 pandas 库(非 Python 内置);
  • 小数据量下有库调用开销,效率不如方法 1、2。

适用场景

  • 数据分析、大数据处理(如 CSV 数据、日志数据去重)。

方法 4:遍历 + 条件判断(无额外容器,保序)

原理

  • 不使用集合,仅通过列表的 in 操作判断元素是否已存在;
  • 遍历原列表,若元素不在结果列表中,则追加到结果列表。

代码示例

python

运行

ini 复制代码
lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
result = []
for x in lst:
    if x not in result:  # 列表查找效率 O(k),k 为 result 长度
        result.append(x)
print(result)  # 输出:[1, 2, 3, 4, 5](保留原顺序)

关键细节

  • 时间复杂度:O (n²)(遍历原列表 O (n) + 列表查找 O (k),k ≤ n);
  • 内存占用:O (n)(仅结果列表,无额外容器)。

优点

  • 无需额外依赖(纯 Python 原生逻辑),无哈希表限制(支持可变元素?不 ------ 列表 in 操作不依赖哈希,但若元素是可变类型如列表,仍可判断,例如 [1, [2]] 去重)。

缺点

  • 效率极低(列表查找是线性时间,列表越长越慢)。

适用场景

  • 列表长度极小(如几十个元素),且不希望引入集合(如面试题要求 "不用 set 去重")。

方法 5:collections.Counter(统计频次 + 去重,保序)

原理

  • Counter 是字典的子类,用于统计元素出现频次;
  • Counter.keys() 会保留元素的 "首次出现顺序"(Python 3.7+),因此可通过 keys() 快速去重。

代码示例

python

运行

scss 复制代码
from collections import Counter

lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
result = list(Counter(lst).keys())
print(result)  # 输出:[1, 2, 3, 4, 5](保留原顺序)

关键细节

  • 时间复杂度:O (n)(统计频次 O (n) + 提取 keys O (m),m 为不重复元素个数);
  • 内存占用:O (n)(存储元素频次);
  • 额外功能:可同时获取元素出现次数(如 Counter(lst)[2] 得到 2 的出现次数 2)。

优点

  • 保序去重的同时,可获取频次统计;
  • 语法简洁,支持不可变元素。

适用场景

  • 去重 + 频次统计的复合场景(如 "找出列表中不重复的元素,并统计每个元素出现次数")。

方法 6:itertools.groupby(需先排序,无需保序)

原理

  • groupby 用于将迭代对象中连续的重复元素分组;
  • 需先对列表排序(使重复元素连续),再提取每组的第一个元素,实现去重;
  • 因排序会打乱原顺序,因此适合无需保序的场景。

代码示例

python

运行

ini 复制代码
from itertools import groupby

lst = [1, 2, 2, 3, 3, 3, 4, 5, 5]
# 步骤 1:排序(使重复元素连续)
lst_sorted = sorted(lst)
# 步骤 2:groupby 分组,提取每组第一个元素
result = [key for key, group in groupby(lst_sorted)]
print(result)  # 输出:[1, 2, 3, 4, 5](排序后的顺序)

关键细节

  • 时间复杂度:O (n log n)(排序占主导,groupby 遍历 O (n));
  • 内存占用:O (n)(排序和分组的临时空间);
  • 限制:必须先排序,因此无法保留原顺序。

优点

  • 可同时处理 "排序 + 去重",适合需要排序后去重的场景;
  • 支持自定义排序规则(如 sorted(lst, key=lambda x: -x) 倒序后去重)。

适用场景

  • 需先排序再去重的场景(如 "对成绩列表去重,并按从高到低排序")。

三、各方法对比与场景选择

方法 是否保序 时间复杂度 内存占用 核心优势 适用场景
列表推导式 + 辅助集合 O(n) O(n) 灵活高效,保序,支持异构元素 日常保序去重(首选)
set 转列表 O(n) O(n) 代码极简,速度最快 无需保序,元素为不可变类型
pandas.drop_duplicates() O(n) O(n) 大数据量高效,支持复杂类型 数据分析、十万级以上元素去重
遍历 + 条件判断 O(n²) O(n) 纯原生逻辑,无额外依赖 列表极短,面试题场景
Counter.keys() O(n) O(n) 去重 + 频次统计 需统计元素出现次数的场景
itertools.groupby 否(排序后) O(n log n) O(n) 排序 + 去重一体化 需先排序再去重的场景

四、避坑重点

  1. 可变元素去重 :若列表包含可变元素(如 [1, [2], [2], 3]),setdict.fromkeys()Counter 会报错(因可变元素不可哈希),此时只能用 "遍历 + 条件判断"(方法 4):

    python

    运行

    css 复制代码
    lst = [1, [2], [2], 3]
    result = []
    for x in lst:
        if x not in result:
            result.append(x)
    print(result)  # 输出:[1, [2], 3](正确去重)
  2. 原列表修改 :以上方法均生成新列表,若需修改原列表,可直接赋值(如 lst = [x for x in lst if x not in seen and not seen.add(x)]);

  3. 空列表 / 单一元素:所有方法均兼容空列表(返回空)和单一元素(返回自身),无异常;

  4. 性能误区:"遍历 + 条件判断"(方法 4)虽无额外容器,但列表查找 O (n) 导致效率极低,列表长度超过 1000 时不推荐使用。

五、总结

  1. 日常保序去重 :优先选「列表推导式 + 辅助集合」(灵活高效)或「dict.fromkeys()」(语法简洁);
  2. 无需保序去重 :优先选「set 转列表」(速度最快);
  3. 大数据量去重 :优先选「pandas.drop_duplicates()」(底层 C 优化);
  4. 复合需求 :需频次统计用「Counter」,需排序用「groupby」。

根据 "是否保序、数据量、是否需要额外功能" 选择合适的方法,即可兼顾效率和可读性。

相关推荐
摇滚侠3 小时前
零基础小白自学 Git_Github 教程,git 命令行操作1,笔记18
笔记·git·github
无限进步_3 小时前
C++从入门到类和对象完全指南
开发语言·c++·windows·git·后端·github·visual studio
itwangyang5203 小时前
在 GitHub 上生成和配置个人访问令牌(PAT),并将其用于 R 环境中的凭证管理和包安装。
开发语言·r语言·github
love530love4 小时前
【ComfyUI/SD环境管理指南(二)】:如何避免插件安装导致的环境崩溃与“外科手术式”修复
人工智能·windows·python·stable diffusion·github·aigc·comfyui
Jonathan Star5 小时前
Git 的 pre-commit hook(以及其他钩子脚本)默认不会被 git commit 追踪,也不会被 git push 推送到远程仓库
github
无限进步_5 小时前
C++初始化列表详解:语法、规则与最佳实践
java·开发语言·数据库·c++·git·github·visual studio
无限进步_6 小时前
C++运算符重载完全指南:从基础到实战应用
开发语言·数据库·c++·windows·git·github·visual studio
逛逛GitHub6 小时前
盘点 近期优秀的 GitHub 开源项目。
github
nil6 小时前
shortcutkey:跨平台快捷键管理工具的设计与实现
python·开源·github