JS演算法之找出缺失的整數
題目:一個無序陣列裡有99個不重複正整數,範圍從1到100,唯獨缺少一個整數。如何找出這個缺失的整數?
假設陣列長度是N,那麼該解法的時間複雜度是O(1),空間複雜度是O(N)。
此題有多種解法:
1、建立一個HashMap,以1到100為鍵,值都是0 。然後遍歷整個陣列,每讀到一個整數,就找到HashMap當中對應的鍵,讓其值加一。由於陣列中缺少一個整數,最終一定有99個鍵對應的值等於1, 剩下一個鍵對應的值等於0。遍歷修改後的HashMap,找到這個值為0的鍵。
[JavaScript] 純文字檢視 複製程式碼// 當前的無序陣列 var DisorderedArray = [1,2,5,3,6,4,...99] //此處省略 var HashMap = new Array(n); // 遍歷HashMap for(var i = 0; i < HashMap.length; i++){ HashMap[i] = 0; } // 遍歷無序陣列 for(var j = 0; j < DisorderedArray.length; j++){ HashMap[j] += 1; } for(var k = 0; k < HashMap.length; k++){ if(HashMap[k] === 0){ return k; break; } }
以上解法在時間上是最優的,但額外開闢了空間,如何能夠降低空間複雜度呢?
此時,便有了解法2:
先把陣列元素進行排序,然後遍歷陣列,檢查任意兩個相鄰元素數值是否是連續的。如果不連續,則中間缺少的整數就是所要尋找的;如果全都連續,則缺少的整數不是1就是100。
假設陣列長度是N,如果用時間複雜度為O(NLogN)的排序演算法進行排序,那麼該解法的時間複雜度是O(NLogN),空間複雜度是O(1)。
[JavaScript] 純文字檢視 複製程式碼// 當前的無序陣列 var DisorderedArray = [1,2,5,3,6,4,...99] //此處省略 DisorderedArray.sort(); for(var i = 1; i < DisorderedArray.length; i++){ var prev = DisorderedArray[i-1], item = DisorderedArray[i]; if(item - prev != 1){ return item-1; break; }else{ if(DisorderedArray[0] == 2){ return 1; break; } if(DisorderedArray[n] == (DisorderedArray.length-1)){ return DisorderedArray.length; break; } } }
以上解法是沒有開闢額外空間的,但是時間複雜度又大了,有辦法讓時間和空間都進一步優化麼?
此時,解法3出現:
很簡單也很高效的方法,先算出1+2+3….+100的和,然後依次減去陣列裡的元素,最後得到的差,就是唯一缺失的整數。
假設陣列長度是N,那麼該解法的時間複雜度是O(N),空間複雜度是O(1)。
[JavaScript] 純文字檢視 複製程式碼var DisorderedArray = [1,2,5,3,6,4,...99] //此處省略 var res = 0; for(var i = 1; i < 101; i++){ res += i; } for(var j = 0; j < DisorderedArray.length; j++){ res -= DisorderedArray[j] } // 最後這裡的res就是最後缺失的整數
以上解法,對於沒有重複元素的陣列,這解法在時間和空間上已經是最優了。下面開始擴充套件問題
題目擴充套件:一個無序陣列裡有若干個正整數,範圍從1到100,其中99個整數都出現了偶數次,只有一個整數出現了奇數次(比如1,1,2,2,3,3,4,5,5),如何找到這個出現奇數次的整數?
此擴充套件的題目來看,用上面的三種方法是沒有用的,在這裡,就要運用到異或運算,於是就有這了這樣的解法:遍歷整個陣列,依次做異或運算。由於異或在位運算時相同為0,不同為1,因此所有出現偶數次的整數都會相互抵消變成0,只有唯一出現奇數次的整數會被留下。
假設陣列長度是N,那麼該解法的時間複雜度是O(N),空間複雜度是O(1)。
[JavaScript] 純文字檢視 複製程式碼// js異或運算子(^) var arr = [4,4,6,8,8,2,3,3,7,6,7,1,5,9,1,5,9]; var res; for (var i = 0; i < arr.length; i++) { var flag = false; for (var j = 0; j < arr.length; j++) { if(i != j){ var aaa = arr[i] ^ arr[j]; if(!aaa) { flag = true; break; } } } if(!flag){ res = i; break; } } console.log(res) // 5 console.log(arr[res]) // 2
此處我不得不說一下上面的方法,雖然上面的程式碼解決了問題,可是還是使用了大題程式碼,這在效能上還是會有很大的損耗的。於是就有了我下面這段程式碼,依然可以得到答案,而且大減少了程式碼量
[JavaScript] 純文字檢視 複製程式碼var arr = [4,4,6,8,8,2,3,3,7,6,7,1,5,9,1,5,9]; arr.reduce((prev,next) => prev ^ next); // 此處得到結果為2 // 由於異或在位運算時相同為0,不同為1,因此所有出現偶數次的整數都會相互抵消變成0,只有唯一出現奇數次的整數會被留下
題目第二次擴充套件:一個無序陣列裡有若干個正整數,範圍從1到100,其中98個整數都出現了偶數次,只有兩個整數出現了奇數次(比如1,1,2,2,3,4,5,5),如何找到這個出現奇數次的整數?
解法:
遍歷整個陣列,依次做異或運算。由於陣列存在兩個出現奇數次的整數,所以最終異或的結果,等同於這兩個整數的異或結果。這個結果中,至少會有一個二進位制位是1(如果都是0,說明兩個數相等,和題目不符)。
舉個例子,如果最終異或的結果是5,轉換成二進位制是00000101。此時我們可以選擇任意一個是1的二進位制位來分析,比如末位。把兩個奇數次出現的整數命名為A和B,如果末位是1,說明A和B轉為二進位制的末位不同,必定其中一個整數的末位是1,另一個整數的末位是0。
根據這個結論,我們可以把原陣列按照二進位制的末位不同,分成兩部分,一部分的末位是1,一部分的末位是0。由於A和B的末位不同,所以A在其中一部分,B在其中一部分,絕不會出現A和B在同一部分,另一部分沒有的情況。
這樣一來就簡單了,我們的問題又迴歸到了上一題的情況,按照原先的異或解法,從每一部分中找出唯一的奇數次整數即可。
假設陣列長度是N,那麼該解法的時間複雜度是O(N)。把陣列分成兩部分,並不需要藉助額外儲存空間,完全可以在按二進位制位分組的同時來做異或運算,所以空間複雜度仍然是O(1)。
相關文章
- 如何在1到100的整數陣列上找到缺失的數字陣列
- Leetcode-2028. 找出缺失的觀測資料LeetCode
- 找出不能用列表中元素相加得到的最小整數
- 【陣列】1539. 第 k 個缺失的正整數(簡單)陣列
- js小數轉整數JS
- 演算法41. 缺失的第一個正數演算法
- js找出陣列中出現最多的元素和次數JS陣列
- 劍指offer:輸入n個整數,找出其中最小的K個數。
- 找出長時序遙感影像的缺失日期並用畫素均為0的柵格填充缺失日期的檔案
- js面試題-找出字串中的數字,並替換為*JS面試題字串
- 給定一個整數陣列,找出總和最大的連續數列,並返回總和。陣列
- 【簡單演算法】1.兩數之和,給定整數陣列和目標值,找出陣列中2數之和等於目標值的元素演算法陣列
- js 將負數或小數轉成正整數JS
- 不用做任何比較判斷運算子找出兩個整數中的較大的值
- DPOS 共識演算法 - 缺失的白皮書演算法
- 找出N以內的偶數
- 每天一道演算法題系列十三之羅馬數字轉整數演算法
- 【演算法學習筆記】Meissel-Lehmer 演算法 (亞線性時間找出素數個數)演算法筆記
- js生成一定範圍內的隨機整數JS隨機
- 如何應對缺失值帶來的分佈變化?探索填充缺失值的最佳插補演算法演算法
- PHP 批鬥大會之缺失的異常PHP
- LeetCode 41. 缺失的第一個正數LeetCode
- 【演算法】求眾數-js解法演算法JS
- 每日一道演算法:整數反轉演算法
- 運籌學-整數規劃IP演算法演算法
- 每日一道演算法:羅馬數字轉整數演算法
- 數值的整數次方
- 2034 整數的個數
- 數數的位數(正整數)
- Swift-Tips之整數轉陣列Swift陣列
- Redis資料結構之整數集合Redis資料結構
- 「演算法之美系列」排序(JS版)演算法排序JS
- 輸入一個整數,返回這個整數的位數
- JS演算法——統計字元數量JS演算法字元
- Mysql 資料型別之整數型別MySQL 資料型別
- 《磨難之間》——刻意設計,缺失核心
- 每天一道演算法題:顛倒整數演算法
- C++之數字計數演算法C++演算法