最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python列表去重的20種實現(xiàn)方式

 更新時間:2026年05月06日 08:15:41   作者:刀法如飛  
這篇文章主要介紹了20種不同的列表去重算法,包括基礎(chǔ)循環(huán)、哈希表、集合與排序、函數(shù)式與遞歸等類別,并分析了每種方法的時間復(fù)雜度、是否保留原順序以及適用場景,文章還討論了如何處理不可哈希的元素以及在遇到需要進行業(yè)務(wù)處理的重復(fù)元素時的解決方案

列表(數(shù)組)去重是最常見的算法,非常簡單,但不同實現(xiàn)方式背后的差異巨大。AI時代,可以不手寫代碼了,但需要知道代碼背后的原理,這樣才能更好地指導(dǎo)AI編程。

最簡單的思路

新建列表,遍歷原列表,當原列表的元素不在新列表的,則添加到新列表中。

def unique(data):
    # 新建list
    new_list = []
    for item in data:
        # 原list中的項是否存在于新list中,不存在則添加。這是 O(n)操作
        if item not in new_list:
            new_list.append(item)
    return new_list

這種寫法最直觀易懂,但每次 not in 都要遍歷整個 new_list,復(fù)雜度為 O(n²)。

如何降低復(fù)雜度呢?可以從以下角度思考:

  • 哈希集合 / 字典:把查詢從 O(n) 可壓到 O(1),整體 O(n)
  • 先排序:相同元素兩兩比較再去重,O(nlogn),但會破壞原順序
  • 函數(shù)式 / 遞歸:寫法上換一種風(fēng)格,適用不同場景,本質(zhì)仍是上面的方式

第1類:基礎(chǔ)循環(huán)(方法1-8)

策略原理:遍歷原數(shù)組,直接用雙重循環(huán)或下標比較找出重復(fù)項。每一步判斷"是否存在"都是 O(n),整體復(fù)雜度 O(n²)。

適用場景:這里主要是展示算法原理,用于教學(xué)示例,弄懂編程原理。生產(chǎn)代碼不建議使用。

# 方法1:最基礎(chǔ)的線性查找
def unique_v1(data):
    new_list = []
    for item in data:
        # in 在列表上是 O(n) 掃描,整體 O(n2)
        # 該元素不在新list則添加
        if item not in new_list:
            new_list.append(item)
    return new_list

# 方法2:用下標遍歷
def unique_v2(data):
    new_list = []
    for i in range(len(data)):
        # 與第1種相同,遍歷方式換成range,復(fù)雜度不變
        if data[i] not in new_list:
            new_list.append(data[i])
    return new_list

# 方法3:列表推導(dǎo)式
def unique_v3(data):
    new_list = []
    # 利用列表推導(dǎo)式的副作用追加元素,寫法簡化,本質(zhì)與前面一樣
    [new_list.append(i) for i in data if i not in new_list]
    return new_list

# 方法4:通過元素首次出現(xiàn)位置判斷
def unique_v4(data):
    new_list = []
    for i in range(len(data)):
        # data.index(x) 返回 x 在 data 里第一次出現(xiàn)的下標
        # 當前下標恰好等于該值時,說明該元素是首次出現(xiàn),將首次出現(xiàn)的添加到新list
        if i == data.index(data[i]):
            new_list.append(data[i])
    return new_list

# 方法5:原地刪除(從右往左掃描)
def unique_v5(data):
    l = len(data)
    while l > 0:
        l -= 1
        i = l
        while i > 0:
            i -= 1
            # 在 [0, l) 區(qū)間里尋找與 data[l] 相同的元素
            # 找到就刪后面那個,保留前面的
            if data[i] == data[l]:
                del data[l]
                break
    return data
# 修改原列表,空間 O(1)

# 方法6:原地刪除(從左往右掃)
def unique_v6(data):
    l = len(data)
    i = 0
    while i < l:
        j = i + 1
        while j < l:
            # 把 data[i] 后面所有等于它的元素刪掉
            if data[i] == data[j]:
                del data[j]
                l -= 1   # 列表變短,長度同步更新
            else:
                j += 1
        i += 1
    return data

# 方法7:用 try-except 替代 in
def unique_v7(data):
    new_list = []
    for item in data:
        try:
            # index 找不到會拋 ValueError
            new_list.index(item)
        except ValueError:
            # 找不到才追加
            new_list.append(item)
    return new_list
# 實際上不會這么使用——拿異常處理正常的控制流,性能和可讀性都吃虧

# 方法8:雙層循環(huán)+下標判斷
def unique_v8(data):
    new_list = []
    for i in range(len(data)):
        j = 0
        while j <= i:
            # 看 data[i] 在它之前是否出現(xiàn)過
            if data[i] == data[j]:
                # 只有 j == i(前面都沒遇到)時才追加
                if i == j:
                    new_list.append(data[i])
                break
            j += 1
    return new_list
# 內(nèi)層只跑到 i 而非 n,比較次數(shù)約為方法1的一半,但漸進復(fù)雜度仍是 O(n2)

第2類:哈希表(方法9-12)

策略原理:利用 dictset 的鍵(Key)唯一性來記錄"已經(jīng)出現(xiàn)的元素"。哈希結(jié)構(gòu)的查詢是 O(1),因此整體降到 O(n)。代價是需要 O(n) 額外內(nèi)存空間,且元素必須可哈希——數(shù)字、字符串、元組都可以,但 list、dict 這類可變對象不行。

適用場景:日常項目的首選。需要保留原順序時尤其合適,因為一邊查重一邊按原序?qū)懭虢Y(jié)果。

# 方法9:set 配合 list——工程最常見寫法
def unique_v9(data):
    seen = set()        # set 用于 O(1) 判重
    result = []         # list 用于保持原順序
    for item in data:
        if item not in seen:
            seen.add(item)
            result.append(item)
    return result

# 方法10:dict.fromkeys()——最佳版本,實際使用首選
def unique_v10(data):
    # dict 自 Python 3.7 起保持插入順序
    # fromkeys 會自動用相同 key 覆蓋,從而完成去重
    return list(dict.fromkeys(data))

# 方法11:filter + 列表,函數(shù)式風(fēng)格
def unique_v11(data):
    seen = []
    # 內(nèi)部函數(shù)
    def check(item):
        # 閉包捕獲 seen
        # 注意 seen 是 list,in 仍是 O(n),整體仍是 O(n2)
        if item not in seen:
            seen.append(item)
            return True
        return False
    return list(filter(check, data))
# 函數(shù)式風(fēng)格,但不純粹,seen 類型選錯了,這里只是為了展示寫法

# 方法12:filter + 字典,由list改為dict,仍然不是純函數(shù)式
def unique_v12(data):
    obj = {}
    def check(item):
        # 用 dict 替代上面的 list,查詢變成 O(1)
        if obj.get(item) is None:
            obj[item] = item
            return True
        return False
    return list(filter(check, data))

第3類:集合與排序(方法13-16)

策略原理:將list直接轉(zhuǎn) set,或者通過 sort() 讓相同元素挨在一起再去重,從而簡化查找邏輯。兩種思路都不再保留原有順序。集合方式 O(n),排序方式 O(nlogn),且要求元素可比較。

適用場景:不關(guān)心順序、只關(guān)心結(jié)果集合的場合,例如統(tǒng)計去重數(shù)量、做集合運算、把列表當作"無序集合"使用。

# 方法13:set 直接轉(zhuǎn)列表,常見用法
def unique_v13(data):
    # 哈希集合天然不重復(fù)
    return list(set(data))
# 寫法最短,但順序會被打亂

# 方法14:map + filter 組合
def unique_v14(data):
    seen = []
    def mark(item):
        # 第一次見到返回元素本身,后續(xù)返回 None
        if item not in seen:
            seen.append(item)
            return item
        return None
    # 先 map 標記,再 filter 把 None 去掉
    return list(filter(lambda x: x is not None, map(mark, data)))
# 函數(shù)式風(fēng)格,但 seen 用 list 仍是 O(n2)

# 方法15:先排序再相鄰去重(從右往左刪)
def unique_v15(data):
    data.sort()  # 排序后,相同元素會聚到一起
    l = len(data)
    while l > 0:
        l -= 1
        # 相鄰兩兩比較,相同就刪后面那個
        if l > 0 and data[l] == data[l-1]:
            del data[l]
    return data
# 復(fù)雜度由 sort 決定,O(nlogn);元素需要可比較

# 方法16:先排序再相鄰去重(從左往右刪)
def unique_v16(data):
    data.sort()
    l = len(data) - 1
    i = 0
    while i < l:
        if data[i] == data[i+1]:
            del data[i]   # 刪當前,i 不前進;同時長度減一
            i -= 1
            l -= 1
        i += 1
    return data

第4類:函數(shù)式與遞歸(方法17-20)

策略原理:用 reduce、外部庫或遞歸換一種表達方式。reduce 配合元組累加器可以做到 O(n),但寫法比直接 for 循環(huán)晦澀;遞歸則吃調(diào)用棧、numpy 需要庫依賴。

適用場景:numpy 適合大規(guī)模數(shù)值數(shù)據(jù);其余幾種主要用于練習(xí)函數(shù)式或遞歸思維,工程上一般直接用第 2 類。

# 方法17:reduce + 元組累加器(函數(shù)式風(fēng)格但能跑到 O(n))
import functools

def unique_v17(data):
    def foo(acc, item):
        # 累加器是一個元組 (result, seen)
        # result 保留首次出現(xiàn)的順序,seen 用集合實現(xiàn) O(1) 判重
        result, seen = acc
        if item in seen:
            return (result, seen)
        # 這里直接修改累加器內(nèi)部的 list 和 set
        # 嚴格的純函數(shù)式應(yīng)返回新對象 (result + [item], seen | {item})
        # 但那樣每步都新建列表/集合,復(fù)雜度退回到 O(n2)
        # 在 reduce 內(nèi)做"受控副作用",換取 O(n) 的性能
        seen.add(item)
        result.append(item)
        return (result, seen)

    # 初始累加器是空列表+空集合,最后取 [0] 即得到去重結(jié)果
    return functools.reduce(foo, data, ([], set()))[0]
# O(n);保序;本質(zhì)是用 reduce 重寫了方法9的循環(huán)

# 方法18:調(diào)用 numpy.unique
def unique_v18(data):
    import numpy as np
    # numpy 底層用 C 實現(xiàn)的排序+相鄰去重
    return list(np.unique(np.array(data)))
# O(nlogn);不保序;適合大規(guī)模數(shù)值數(shù)據(jù)

# 方法19:遞歸+原地刪除
def unique_v19(data, length=None):
    # 遞歸退出條件
    if length is None:
        length = len(data)
    if length <= 1:
        return data

    last_idx = length - 1
    # 看末尾元素是否在前面出現(xiàn)過
    is_repeat = False
    for i in range(last_idx):
        if data[i] == data[last_idx]:
            is_repeat = True
            break

    # 重復(fù)則刪除
    if is_repeat:
        del data[last_idx]

    # 遞歸調(diào)用,處理前 length-1 項
    return unique_v19(data, length - 1)
# 遞歸深度 = n,大數(shù)據(jù)會棧溢出,僅作學(xué)習(xí)用

# 方法20:遞歸+拼接返回(不修改原列表)
# 遞歸自后往前逐個調(diào)用,當長度為1時終止。與上一個遞歸不同,這里將不重復(fù)的項目作為結(jié)果拼接起來
def unique_v20(data, length=None):
    if length is None:
        length = len(data)
    if length <= 1:
        return data

    last_idx = length - 1
    last_item = data[last_idx]

    is_repeat = False
    for i in range(last_idx):
        if data[i] == last_item:
            is_repeat = True
            break

    # 末尾元素重復(fù)就丟棄,否則拼到結(jié)果末尾
    result = [] if is_repeat else [last_item]
    # 切片 + 拼接都會產(chǎn)生新列表,空間開銷大
    return unique_v20(data[:last_idx], length - 1) + result
# 演示如何用遞歸構(gòu)造結(jié)果,工程上沒有實用價值

這么多實現(xiàn)方式,如何選擇?

類別時間復(fù)雜度是否保序主要場景
基礎(chǔ)循環(huán)O(n²)教學(xué)、極小規(guī)模
哈希表O(n)日常項目首選
集合 / 排序O(n) / O(nlogn)不在意順序
函數(shù)式 / 遞歸視實現(xiàn)而定看實現(xiàn)學(xué)習(xí)、特定場景

實際項目里怎么選

絕大多數(shù)情況一行就夠:

# 保序、O(n)、對所有可哈希類型有效,Python 3.7+ 自帶
result = list(dict.fromkeys(data))

不在意順序:

result = list(set(data))

數(shù)據(jù)量很大且都是數(shù)值:

import numpy as np
result = list(np.unique(data))

帶邏輯干預(yù)的去重

前面所有方法都把"重復(fù)的元素"直接丟掉。但實際工作里經(jīng)常遇到這樣的情況:遇到重復(fù)時不能簡單丟棄,要根據(jù)某個條件做處理。比如:

  • id 去重,但要保留分數(shù)最高的那條記錄
  • 去重的同時累加重復(fù)次數(shù)(即頻次統(tǒng)計)
  • 數(shù)值在某個區(qū)間內(nèi)才參與去重,區(qū)間外原樣保留

這類需求 setdict.fromkeys 都沒法直接表達,需要把"判重"和"處理"兩步拆開來寫。

def unique_with_rule(data, key=None, on_duplicate=None):
    """
    帶邏輯干預(yù)的去重。

    key: 可哈希的去重鍵,默認拿元素本身
    on_duplicate: 遇到重復(fù)時如何處理 (舊值, 新值) -> 新的"代表值"
                  返回 None 時保持舊值不變(即等同于丟棄新值)
    """
    if key is None:
        key = lambda x: x

    chosen = {}     # 鍵 -> 當前選中的元素
    order  = []     # 記錄鍵首次出現(xiàn)的順序,保證保序

    for item in data:
        k = key(item)
        if k not in chosen:
            chosen[k] = item
            order.append(k)
        elif on_duplicate is not None:
            # 遇到重復(fù)時由調(diào)用方?jīng)Q定如何合并
            merged = on_duplicate(chosen[k], item)
            if merged is not None:
                chosen[k] = merged

    return [chosen[k] for k in order]

例 1,按 id 去重,保留分數(shù)最高的:

students = [
    {'id': 1, 'name': '張三', 'score': 90},
    {'id': 1, 'name': '張三', 'score': 95},   # 同 id,分數(shù)更高
    {'id': 2, 'name': '李四',   'score': 99},
]
result = unique_with_rule(
    students,
    key=lambda x: x['id'],
    on_duplicate=lambda old, new: new if new['score'] > old['score'] else old,
)
# [{'id':1,'score':95,...}, {'id':2,'score':99,...}]

例 2,去重的同時統(tǒng)計每個值的出現(xiàn)次數(shù):

from collections import Counter

data = ['A', 'B', 'A', 'C', 'B', 'A']
# Counter 本身就是"鍵->計數(shù)",遍歷一次即可完成統(tǒng)計
counts = Counter(data)
# Python 3.7+ 起 dict / Counter 保留插入順序,因此 keys 即首次出現(xiàn)順序
unique_keys = list(counts.keys())
# unique_keys = ['A', 'B', 'C']
# counts      = {'A': 3, 'B': 2, 'C': 1}

例 3,區(qū)間過濾——只對 [0, 50] 區(qū)間內(nèi)的值去重,區(qū)間外的原樣保留:

data = [5, 12, 5, 100, 12, 200]
seen = set()
result = []
for x in data:
    if 0 <= x <= 50:
        # 區(qū)間內(nèi)才參與去重
        if x in seen:
            continue
        seen.add(x)
    # 區(qū)間外或首次出現(xiàn),都保留
    result.append(x)
# [5, 12, 100, 200]

這三個例子是同一種思路:把判重與業(yè)務(wù)規(guī)則分開。判重用哈希結(jié)構(gòu)保證 O(n),規(guī)則部分留給回調(diào)或顯式分支處理,這樣既不丟性能,又能容納各種業(yè)務(wù)變化。

處理不可哈希的元素

如果列表里是 dict、list 這類不可哈希對象,前面的 set / dict.fromkeys 都用不了,需要自己提供"去重鍵":

def deduplicate_with_key(data, key=None):
    """按自定義 key 去重,保留首次出現(xiàn)的元素"""
    if key is None:
        return list(dict.fromkeys(data))

    seen = set()
    result = []
    for item in data:
        # 把不可哈希的整體映射成可哈希的鍵
        k = key(item)
        if k not in seen:
            seen.add(k)
            result.append(item)
    return result

# 使用示例:按學(xué)生 id 去重
students = [
    {'id': 1, 'name': '張三', 'score': 90},
    {'id': 1, 'name': '張三', 'score': 95},   # id 重復(fù)
    {'id': 2, 'name': '李四', 'score': 85},
]
result = deduplicate_with_key(students, key=lambda x: x['id'])
# 只保留第一個 id=1 的學(xué)生

利用 set.add() 返回 None 的小技巧,也能把保序去重壓成一行,但可讀性下降,實際代碼不建議:

def deduplicate_short(data):
    seen = set()
    # x in seen 為 False 時才會執(zhí)行 seen.add(x)
    # add 返回 None 讓 or 短路,整個表達式得到 False,于是元素被保留
    return [x for x in data if not (x in seen or seen.add(x))]

總結(jié)

  • 默認就用 list(dict.fromkeys(data)):保序、復(fù)雜度 O(n)、一行搞定
  • 不需要保序就用 list(set(data)),利用數(shù)據(jù)結(jié)構(gòu)特性
  • 大規(guī)模數(shù)值數(shù)據(jù)用 numpy.unique,借助外部庫
  • 不可哈希元素用自定義 key 函數(shù)
  • 遇到重復(fù)要做業(yè)務(wù)處理,把判重和規(guī)則分開寫,具體算法根據(jù)情況采用

這20 種方法不是為了去記住如何編寫,而是明白其中的編程思路:

  1. 同一個問題可以從多個角度切入
  2. 選對數(shù)據(jù)結(jié)構(gòu)往往比寫更聰明的代碼更重要
  3. O(n²) 與 O(n) 在數(shù)據(jù)變大時是幾百倍的實際差距
  4. 不要過度優(yōu)化,能用 dict.fromkeys 就別用其他
  5. 遇到新問題先寫最直觀的版本,再按瓶頸逐步優(yōu)化

AI 時代,程序員不一定要手寫代碼,但一定要懂得編程思路,這樣才能更好地指導(dǎo) AI。

以上就是Python列表去重的20種實現(xiàn)方式的詳細內(nèi)容,更多關(guān)于Python列表去重的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python http服務(wù)flask架構(gòu)實用代碼詳解分析

    python http服務(wù)flask架構(gòu)實用代碼詳解分析

    本篇文章主要分享一個python的簡單http服務(wù)flask架構(gòu)。目前主流的python的服務(wù)框架有django、flask,相較于django來說,flask更小巧玲瓏。至于并發(fā)的問題,使用了gevent協(xié)程io進行處理
    2021-10-10
  • django中使用memcached示例詳解

    django中使用memcached示例詳解

    這篇文章主要為大家介紹了django中使用memcached示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • Python YAML文件處理的完整指南

    Python YAML文件處理的完整指南

    yaml是一種比xml和json更輕的文件格式,也更簡單更強大,它可以通過縮進來表示結(jié)構(gòu),聽著就和Python很配對不對?本文給大家詳細介紹了Python YAML文件處理的完整指南,需要的朋友可以參考下
    2025-07-07
  • Python GUI之tkinter詳解

    Python GUI之tkinter詳解

    今天帶大家學(xué)習(xí)Python GUI之tkinter的相關(guān)知識,文中對如何使用tkinter作了非常詳細的介紹及代碼示例,對正在學(xué)習(xí)python的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-10-10
  • 使用Python實現(xiàn)調(diào)整Excel中的行列順序

    使用Python實現(xiàn)調(diào)整Excel中的行列順序

    調(diào)整Excel?行列順序指的是改變工作表中行或列的位置,以便更好地展示和分析數(shù)據(jù),本文將介紹如何通過Python高效地調(diào)整Excel?行列順序,感興趣的可以了解下
    2025-01-01
  • Bokeh:Python交互式可視化的利器詳解

    Bokeh:Python交互式可視化的利器詳解

    這篇文章主要介紹了Bokeh:Python交互式可視化的利器,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Python hashlib模塊加密過程解析

    Python hashlib模塊加密過程解析

    這篇文章主要介紹了Python hashlib模塊加密過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • Python保持順序進行序列高效去重

    Python保持順序進行序列高效去重

    在數(shù)據(jù)處理領(lǐng)域,??保持順序的去重操作??是數(shù)據(jù)清洗的核心環(huán)節(jié),本文將全面解析Python中保持順序的去重技術(shù),有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-08-08
  • python selenium瀏覽器復(fù)用技術(shù)的使用

    python selenium瀏覽器復(fù)用技術(shù)的使用

    本文主要介紹了python selenium瀏覽器復(fù)用技術(shù)的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • python 去除txt文本中的空格、數(shù)字、特定字母等方法

    python 去除txt文本中的空格、數(shù)字、特定字母等方法

    今天小編就為大家分享一篇python 去除txt文本中的空格、數(shù)字、特定字母等方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07

最新評論

新营市| 泰州市| 沧源| 文水县| 黄山市| 泾阳县| 肇东市| 宣威市| 永顺县| 手机| 桃园市| 清河县| 湖北省| 霞浦县| 晋江市| 泊头市| 吴桥县| 丹阳市| 会同县| 阿鲁科尔沁旗| 内乡县| 庄河市| 永州市| 林口县| 台南县| 信阳市| 乐东| 万载县| 庄浪县| 宁明县| 昆明市| 乐东| 商南县| 澄迈县| 永新县| 鲁山县| 栾城县| 沅江市| 镇沅| 连城县| 临颍县|