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

JavaScript數(shù)組去重的6種實現(xiàn)方式(從 O(n2) 到 O(n))

 更新時間:2026年05月26日 09:37:15   作者:Darling嚕啦啦  
數(shù)組去重是前端面試中的高頻題目,本文通過 6 種不同的實現(xiàn)方式,帶你從暴力雙重循環(huán)一路進(jìn)化到 ES6 的 Set 一行代碼,同時深入理解時間復(fù)雜度與空間復(fù)雜度的權(quán)衡,需要的朋友可以參考下

前言

今天在課程中,老師帶我們用 6 種不同的方式 解決了同一道題——數(shù)組去重。從最基礎(chǔ)的雙重循環(huán),到利用數(shù)組 API,再到 ES6 的 Set,每種方法都有其獨特的思路和適用場景。

更重要的是,通過這道題,我真正理解了時間復(fù)雜度空間復(fù)雜度的概念,以及"空間換時間"的算法思想。

一、編碼規(guī)范:寫好函數(shù)的第一步

在開始之前,先聊聊代碼規(guī)范。老師在課上反復(fù)強(qiáng)調(diào):

1.1 注釋是代碼的一部分

/**
 * @func 數(shù)組去重
 * @param {Array} arr 數(shù)組
 * @return {Array} 去重后的數(shù)組
 * @author hzs
 * @date 2026-05-25
 */
function unique(arr) {
    // ...
}

代碼的開發(fā)者和使用者可能不是同一個人,你可能忘記當(dāng)時為什么這么寫。注釋會提高代碼的可讀性,是代碼的一部分。

1.2 函數(shù)設(shè)計三原則

原則說明
一個函數(shù)一個功能單一職責(zé),便于維護(hù)和測試
封裝復(fù)雜功能調(diào)用者不需要了解內(nèi)部實現(xiàn)
健壯性——校驗參數(shù)對輸入進(jìn)行類型檢查,避免異常

1.3 參數(shù)校驗?zāi)0?/h3>
function unique(arr) {
    // Array.isArray() 是數(shù)組的靜態(tài)方法,無需實例化即可調(diào)用
    if (!Array.isArray(arr)) {
        console.log('type error');
        return [];
    }
    // ...具體邏輯
}

以下 6 種實現(xiàn)都會包含這個參數(shù)校驗,后續(xù)代碼中不再重復(fù)說明。

二、方法一:雙重循環(huán)(暴力法)

思路

維護(hù)一個結(jié)果數(shù)組 res,遍歷原數(shù)組,對每個元素檢查是否已存在于 res 中。

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error');
        return [];
    }
    let res = [arr[0]];
    for (let i = 1; i < arr.length; i++) {
        let flag = true;  // 標(biāo)記是否重復(fù)
        for (let j = 0; j < res.length; j++) {
            // === 恒等:值相等且類型相等
            // 1 === '1' → false(弱類型語言中的嚴(yán)格比較)
            if (arr[i] === res[j]) {
                flag = false;
                break;
            }
        }
        if (flag) {
            res.push(arr[i]);
        }
    }
    return res;
}

console.log(unique([1, 2, 3, 4, 5, 5, 6]));
// [1, 2, 3, 4, 5, 6]

復(fù)雜度分析

時間復(fù)雜度:O(n2)
├── 外層循環(huán) n 次
└── 內(nèi)層循環(huán)最多 n 次
    總計:n × n = n2

空間復(fù)雜度:O(n)
└── 結(jié)果數(shù)組 res 最多存儲 n 個元素

優(yōu)點:思路最直觀,適合初學(xué)者理解。缺點:性能差,數(shù)據(jù)量大時明顯卡頓。

三、方法二:indexOf 優(yōu)化

思路

利用 Array.prototype.indexOf() 方法替代內(nèi)層循環(huán),判斷元素是否已存在于結(jié)果數(shù)組中。

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error');
        return [];
    }
    const res = [];
    for (let i = 0; i < arr.length; i++) {
        // indexOf 返回元素第一次出現(xiàn)的索引
        // 如果返回 -1,說明 res 中不存在該元素
        if (res.indexOf(arr[i]) === -1) {
            res.push(arr[i]);
        }
    }
    return res;
}

關(guān)鍵 API

arr.indexOf(item)
// 返回 item 在 arr 中第一次出現(xiàn)的索引
// 找不到返回 -1

[1, 2, 3, 2].indexOf(2)   // 1
[1, 2, 3].indexOf(4)      // -1

復(fù)雜度分析

時間復(fù)雜度:O(n2)
├── 外層循環(huán) n 次
└── indexOf 內(nèi)部也是一次遍歷 O(n)
    總計:仍然是 n2

空間復(fù)雜度:O(n)

本質(zhì)上和方法一相同,只是用 indexOf 替代了手寫內(nèi)層循環(huán),代碼更簡潔,但時間復(fù)雜度沒有改善。

四、方法三:filter + indexOf

思路

利用 Array.prototype.filter() 方法,配合 indexOf 進(jìn)行過濾。

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error');
        return [];
    }
    return arr.filter(function(item, index) {
        // 只保留第一次出現(xiàn)的元素
        // indexOf 返回第一個索引,如果等于當(dāng)前 index,說明是第一次出現(xiàn)
        return index === arr.indexOf(item);
    });
}

console.log(unique([1, 2, 3, 4, 5, 5, 6]));
// [1, 2, 3, 4, 5, 6]

關(guān)鍵 API

arr.filter(function(item, index) {
    // 返回 true → 保留該元素
    // 返回 false → 過濾掉該元素
    return true | false;
});

工作原理

原數(shù)組:[1, 2, 3, 4, 5, 5, 6]
索引:    0  1  2  3  4  5  6

filter 遍歷過程:
┌──────┬───────┬────────────────┬────────┐
│ item │ index │ indexOf(item)  │ 保留?  │
├──────┼───────┼────────────────┼────────┤
│  1   │   0   │       0        │  ?    │
│  2   │   1   │       1        │  ?    │
│  3   │   2   │       2        │  ?    │
│  4   │   3   │       3        │  ?    │
│  5   │   4   │       4        │  ?    │
│  5   │   5   │       4        │  ?    │  ← 第二個 5 被過濾
│  6   │   6   │       6        │  ?    │
└──────┴───────┴────────────────┴────────┘

復(fù)雜度分析

時間復(fù)雜度:O(n2)
├── filter 遍歷 n 次
└── 每次 indexOf 遍歷 O(n)
    總計:仍然是 n2

空間復(fù)雜度:O(n)

函數(shù)式編程風(fēng)格,代碼最簡潔優(yōu)雅,但性能上仍然是 O(n²)。

五、方法四:排序后相鄰比較

思路

先對數(shù)組排序,然后只需比較相鄰元素是否相同。

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error');
        return [];
    }
    // O(n2) → O(nlogn)
    arr = arr.sort();
    let res = [arr[0]];
    for (let i = 1; i < arr.length; i++) {
        // 相鄰元素不相等,保留
        if (arr[i] !== arr[i - 1]) {
            res.push(arr[i]);
        }
    }
    return res;
}

為什么更快?

排序前:[3, 1, 4, 1, 5, 9, 2, 6, 5]
排序后:[1, 1, 2, 3, 4, 5, 5, 6, 9]
              ↑         ↑
           相鄰比較即可,無需兩兩比較

復(fù)雜度分析

時間復(fù)雜度:O(nlogn)
├── sort() 排序:O(nlogn)
└── 遍歷比較:O(n)
    總計:O(nlogn) + O(n) = O(nlogn)  ← 顯著提升!

空間復(fù)雜度:O(n)

性能提升明顯,從 O(n²) 降到 O(nlogn)。但注意:sort() 默認(rèn)按字符串排序,對數(shù)字?jǐn)?shù)組需要傳入比較函數(shù) arr.sort((a, b) => a - b)。

六、方法五:對象字面量 / HashMap(空間換時間)

思路

利用 JavaScript 對象字面量作為 HashMap,以數(shù)組元素為 key,實現(xiàn) O(1) 的查找。

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error');
        return [];
    }
    let res = [],
        obj = {};  // 對象字面量充當(dāng) HashMap
    for (let i = 0; i < arr.length; i++) {
        // obj[variable] — 變量作為 key(動態(tài)屬性訪問)
        // obj.name — 常量作為 key(點號訪問)
        if (!obj[arr[i]]) {
            res.push(arr[i]);
            obj[arr[i]] = 1;  // 標(biāo)記為已存在
        } else {
            obj[arr[i]]++;    // 記錄出現(xiàn)次數(shù)
        }
    }
    return res;
}

核心原理

對象字面量充當(dāng) HashMap

遍歷 [1, 2, 3, 2, 4, 3]

Step 1: obj = {},  res = [1]     obj[1] = 1
Step 2: obj = {1:1}, res = [1,2] obj[2] = 1
Step 3: obj = {1:1,2:1}, res = [1,2,3] obj[3] = 1
Step 4: obj[2] 已存在!跳過
Step 5: obj = {1:1,2:1,3:1}, res = [1,2,3,4] obj[4] = 1
Step 6: obj[3] 已存在!跳過

結(jié)果:[1, 2, 3, 4]

復(fù)雜度分析

時間復(fù)雜度:O(n)
├── 只需遍歷一次數(shù)組
└── 對象屬性查找是 O(1)
    總計:O(n) × O(1) = O(n)  ← 最優(yōu)!

空間復(fù)雜度:O(n)
├── 結(jié)果數(shù)組 O(n)
└── HashMap 對象 O(n)
    總計:O(n)  ← 用空間換時間

經(jīng)典的空間換時間策略。JavaScript 早期沒有 HashMap,對象字面量就是最好的替代方案。注意:如果數(shù)組元素是對象,需要用 JSON.stringify() 轉(zhuǎn)換為字符串作為 key。

七、方法六:ES6 Set(終極方案)

思路

利用 ES6 新增的 Set 數(shù)據(jù)結(jié)構(gòu)——天生不重復(fù)的集合

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error');
        return [];
    }
    return [...new Set(arr)];  // Set 轉(zhuǎn)換為數(shù)組
}

一行代碼搞定

const unique = arr => [...new Set(arr)];

Set 是什么?

Set 的特性
├── 不重復(fù)的數(shù)據(jù)容器
├── 內(nèi)部使用 HashMap 實現(xiàn)
├── 查找/插入的時間復(fù)雜度 O(1)
└── ES6 新增的數(shù)據(jù)結(jié)構(gòu)

復(fù)雜度分析

時間復(fù)雜度:O(n)
├── new Set(arr):遍歷數(shù)組構(gòu)建 Set,O(n)
└── ...展開運算符:遍歷 Set 轉(zhuǎn)數(shù)組,O(n)
    總計:O(n)

空間復(fù)雜度:O(n)
└── Set 容器存儲 n 個元素

生產(chǎn)環(huán)境推薦方案:代碼最簡潔、性能最優(yōu)、語義最清晰。

八、六種方法全面對比

8.1 復(fù)雜度對比

方法時間復(fù)雜度空間復(fù)雜度核心思路
① 雙重循環(huán)O(n²)O(n)暴力枚舉
② indexOfO(n²)O(n)API 替代內(nèi)層循環(huán)
③ filter+indexOfO(n²)O(n)函數(shù)式風(fēng)格
④ 排序+相鄰比較O(nlogn)O(n)先排序降低比較次數(shù)
⑤ 對象字面量/HashMapO(n)O(n)空間換時間
⑥ ES6 SetO(n)O(n)利用 Set 天生去重

8.2 復(fù)雜度直觀感受

執(zhí)行時間對比(假設(shè) n = 10000)

O(n2)     :100,000,000 次操作  ??
O(nlogn)  :    132,877 次操作  ??
O(n)      :     10,000 次操作  ??

差距巨大!算法選擇直接影響程序性能

8.3 適用場景

場景推薦方法原因
生產(chǎn)環(huán)境⑥ Set簡潔、高效、現(xiàn)代
面試手寫⑤ HashMap展示算法思維
學(xué)習(xí)理解①②③④理解基本原理
大數(shù)據(jù)量④⑤⑥避免 O(n²)
兼容舊瀏覽器④⑤不依賴 ES6

九、涉及的核心數(shù)組 API

速查表

API類型作用示例
Array.isArray()靜態(tài)方法判斷是否是數(shù)組Array.isArray([1])true
arr.indexOf(item)實例方法返回首次出現(xiàn)的索引[1,2,3].indexOf(2)1
arr.filter(fn)實例方法過濾數(shù)組,返回新數(shù)組arr.filter(x => x > 0)
arr.sort()實例方法排序(原地修改)arr.sort((a,b) => a-b)

十、知識圖譜

?? 數(shù)組去重知識圖譜

編碼規(guī)范
├── JSDoc 注釋規(guī)范
├── 一個函數(shù)一個功能
├── 參數(shù)校驗(健壯性)
└── === 嚴(yán)格相等

六種實現(xiàn)方式
├── O(n2) 暴力法
│   ├── 雙重循環(huán)
│   ├── indexOf
│   └── filter + indexOf
│
├── O(nlogn) 排序法
│   └── sort + 相鄰比較
│
└── O(n) 哈希法
    ├── 對象字面量 / HashMap
    └── ES6 Set

核心概念
├── 時間復(fù)雜度(執(zhí)行效率)
├── 空間復(fù)雜度(內(nèi)存占用)
├── 空間換時間(算法權(quán)衡)
└── HashMap 原理(O(1) 查找)

結(jié)語

一道簡單的數(shù)組去重題,從 O(n²) 到 O(n),從暴力循環(huán)到 Set 一行代碼,背后是算法思維的進(jìn)化。

面試中,面試官考的不僅是你能不能寫出答案,更是你能否分析不同方案的時間復(fù)雜度和空間復(fù)雜度,能否根據(jù)實際場景選擇最優(yōu)方案。

記住這六個方法,理解背后的原理,你就能在面試中游刃有余。

以上就是JavaScript數(shù)組去重的6種實現(xiàn)方式(從 O(n²) 到 O(n))的詳細(xì)內(nèi)容,更多關(guān)于JavaScript數(shù)組去重實現(xiàn)方式的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • javascript 改變字體大小方法集合

    javascript 改變字體大小方法集合

    給網(wǎng)頁正文提供,小 中 大 三種字體的切換功能。用js代碼設(shè)置div style的fontSize屬性。
    2009-06-06
  • JavaScript柯里化函數(shù)式編程面試詳解

    JavaScript柯里化函數(shù)式編程面試詳解

    這篇文章主要介紹了JavaScript柯里化函數(shù)式編程,JS柯里化是前端面試中最常見的問題之一,它可以讓你的代碼更簡潔,工作更高效,感興趣想要詳細(xì)了解可以參考下文
    2023-05-05
  • JS鼠標(biāo)滾動分頁效果示例

    JS鼠標(biāo)滾動分頁效果示例

    在開發(fā)的時候為什么左邊的數(shù)據(jù)出來比右邊的慢呢?因為這里沒有進(jìn)行分頁,左邊的數(shù)據(jù)多,所以查詢相對較慢。怎么解決此問題呢?下面小編給大家?guī)砹薐S鼠標(biāo)滾動分頁效果示例,需要的的朋友參考下吧
    2017-07-07
  • javascript使用Promise對象實現(xiàn)異步編程

    javascript使用Promise對象實現(xiàn)異步編程

    這篇文章主要介紹了javascript使用Promise對象實現(xiàn)異步編程的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • JavaScript定時器實現(xiàn)的原理分析

    JavaScript定時器實現(xiàn)的原理分析

    JavaScript中的定時器大家基本在平時的開發(fā)中都遇見過吧,但是又有多少人去深入的理解其中的原理呢?本文我們就來分析一下定時器的實現(xiàn)原理、定時器的妙用、定時器使用注意事項,有興趣的朋友可以看下
    2016-12-12
  • 微信小程序局部刷新觸發(fā)整頁刷新效果的實現(xiàn)代碼

    微信小程序局部刷新觸發(fā)整頁刷新效果的實現(xiàn)代碼

    這篇文章主要介紹了微信小程序局部刷新觸發(fā)整頁刷新效果的實現(xiàn)代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-11-11
  • JavaScript 實現(xiàn)鼠標(biāo)拖動元素實例代碼

    JavaScript 實現(xiàn)鼠標(biāo)拖動元素實例代碼

    這篇文章主要介紹了JavaScript 實現(xiàn)鼠標(biāo)拖動元素實例代碼,需要的朋友可以參考下
    2014-02-02
  • 微信小程序 多行文本顯示...+顯示更多按鈕和收起更多按鈕功能

    微信小程序 多行文本顯示...+顯示更多按鈕和收起更多按鈕功能

    這篇文章主要介紹了微信小程序多行文本顯示...+顯示更多按鈕和收起更多按鈕,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • 如何利用jszip庫實現(xiàn)文件壓縮與解壓功能

    如何利用jszip庫實現(xiàn)文件壓縮與解壓功能

    本章介紹了在使用jszip庫時,開發(fā)者如何檢測不同瀏覽器版本并實施兼容性解決方案,同時提供了相關(guān)的代碼示例和調(diào)試技巧,以便于讀者能夠更好地理解和運用這些概念,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • JavaScript中var與let的區(qū)別

    JavaScript中var與let的區(qū)別

    這篇文章主要介紹了JavaScript中var與let的區(qū)別,var是JavaScript剛出現(xiàn)時就存在的變量聲明關(guān)鍵字,而let作為ES6才出現(xiàn)的變量聲明關(guān)鍵字,無疑兩者之間存在著很大的區(qū)別,下面來看看兩者之間到底存在什么
    2021-12-12

最新評論

大姚县| 资兴市| 徐水县| 高淳县| 兴化市| 聂荣县| 石楼县| 吴桥县| 长春市| 贡山| 黔东| 无极县| 三明市| 桦南县| 张家港市| 沾化县| 宁波市| 车险| 渝中区| 拉萨市| 昆明市| 嘉兴市| 乐东| 盐亭县| 长兴县| 九寨沟县| 渑池县| 怀来县| 英山县| 和静县| 武陟县| 武功县| 镶黄旗| 吉首市| 江华| 灌南县| 洱源县| 吉水县| 井陉县| 宝山区| 广安市|