[LeetCode] 1 ≤ a[i] ≤ n (n = size of array)，找出陣列內重複的值
Tags: Leetcode

這篇記錄一下LeetCode上面很像的兩題解法：

442. Find All Duplicates in an Array

448. Find All Numbers Disappeared in an Array

442. Find All Duplicates in an Array

題目

Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.

Find all the elements that appear twice in this array.

Could you do it without extra space and in O(n) runtime?

Example:

Input:

[4,3,2,7,8,2,3,1] Output:

[2,3]

給一個整數組成的陣列，裡面的數字一定在1~陣列長度之間，有些數字會出現兩次，其他數字僅出現一次。找出並回傳出現兩次的數字，不能使用額外的空間，且時間複雜度要是O(n)。

解法

天真暴力解

先不理題目關於時間和空間的要求，直接創造一個hash table去解：

var findDuplicates = function(nums) {

let ans = []

let obj = {}



for (let i = 0; i < nums.length; i++) {

if (obj[nums[i]] >= 1) {

obj[nums[i]] +=1

} else {

obj[nums[i]] = 1

}



}

for (let key in obj) {

if (obj[key] === 2) ans.push(Number(key))

}



return ans



};

先用for迴圈創造一個計數的hash table，key是數字，value是數字在陣列中出現的次數。再來去遍歷我的hash table，如果有找到value次數是2，便回傳相應的key。

這樣做創造了額外的空間，所以不符合題目要求，不過剛好有機會可以熟悉怎麼用hash table解題，不失一個練習XD

思考

如果不能創造額外空間，想必只能在原陣列中遍歷了，因為我的目的是要去找到重複出現的值，既然是「找」，直接的想法是我有沒有辦法在遍歷的同時，在陣列內「創造」一個類似hash table的形式讓我去做查找？

假設陣列為[4,3,2,7,8,2,3,1]，重新看一下題目條件：

Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array) (給一個整數組成的陣列，裡面的數字一定在1~陣列長度之間)

也就是說，nums[i]-1可以被當成在陣列中查找的key，不會有爆掉的問題。(因為nums[i]一定在1~8之間。如果沒有這個條件，可能會出現nums[i]=20，)。

現在跑一個迴圈，觀察i, nums[i]和nums[nums[i]-1]的變化：

for (let i = 0; i < nums.length; i++) {

const key = nums[i] - 1

return nums[key]

} i key nums[key]

--------------------------------------

i = 0 -> nums[i]-1 = 3 -> nums[3] = 7

i = 1 -> nums[i]-1 = 2 -> nums[2] = 2

i = 2 -> nums[i]-1 = 1 -> nums[1] = 3

i = 3 -> nums[i]-1 = 6 -> nums[6] = 3

i = 4 -> nums[i]-1 = 7 -> nums[7] = 1

i = 5 -> nums[i]-1 = 1 -> nums[1] = 3

i = 6 -> nums[i]-1 = 2 -> nums[2] = 2

i = 7 -> nums[i]-1 = 0 -> nums[0] = 4

可以看到我的nums[key]會隨著i的變化去對應到陣列內的數字。等於說我在跑迴圈時，直接拿這個key對陣列做查找的動作。

再來，要怎麼樣去知道數字出現了第二次呢？因為在陣列當中，我只能想辦法去「標記」數字。最好呈現「兩次」的方法應該就是布林值了吧！

我們可以用正負去標記查找到的數字：遇到正數的話，就把它標為負數；遇到負數的話，表示我是第二次使用這個key，便可以知道key+1就是答案。

程式碼

var findDuplicates = function(nums) {

let ans = []



for (let i = 0; i < nums.length; i++) {

const key = Math.abs(nums[i]) - 1

if (nums[key] > 0) {

nums[key] = -nums[key]

} else {

ans.push(key+1)

}

}

return ans



};

程式碼有幾點要特別小心：

要記得幫key加上絕對值，因為我們會用正負號去標記陣列內元素，在跑迴圈時，可能會讓key變成負數而出錯。 陣列內元素從1開始，但位置是從0開始，所以key要記得-1、最後的答案要記得key+1。

448. Find All Numbers Disappeared in an Array

題目

Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.

Find all the elements of [1, n] inclusive that do not appear in this array.

Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space.

Example:

Input:

[4,3,2,7,8,2,3,1] Output:

[5,6]

和上一題大同小異，唯一的差別是這次要找到的是陣列內「消失」的數字。這題定義的「正常」陣列是：陣列有多長，陣列內就應該有幾個從1~陣列長度的不重複數字。如果沒有出現的數字就表示他們「消失」了。

解法

思考

因為題目給定的條件，如果有重複的數字就會讓「正常」的數字消失，所以來觀察一下上一題的解法：

如果我單純看上一題的「遇到正數標負號」部分：

const nums = [4,3,2,7,8,2,3,1] for (let i = 0; i < nums.length; i++) {

const key = Math.abs(nums[i]) - 1

if (nums[key] > 0) {

nums[key] = -nums[key]

} i key nums[key] nums

-------------------------------------------------------

i = 0 -> key = 3 -> nums[3] = 7 -> [4,3,2,-7,8,2,3,1]

i = 1 -> key = 2 -> nums[2] = 2 -> [4,3,-2,-7,8,2,3,1]

i = 2 -> key = 1 -> nums[1] = 3 -> [4,-3,-2,-7,8,2,3,1]

i = 3 -> key = 6 -> nums[6] = 3 -> [4,-3,-2,-7,8,2,-3,1]

i = 4 -> key = 7 -> nums[7] = 1 -> [4,-3,-2,-7,8,2,-3,-1]

i = 5 -> key = 1 -> nums[1] = 3 -> [4,-3,-2,-7,8,2,-3,1]

i = 6 -> key = 2 -> nums[2] = 2 -> [4,-3,-2,-7,8,2,-3,1]

i = 7 -> key = 0 -> nums[0] = 4 -> [-4,-3,-2,-7,8,2,-3,1]

觀察一下最後的nums，會發現只有8,2不是負數，為什麼呢？因為我的key沒有出現過key=4或key=5， 所以就等同於nums[i]缺少了5和6。

程式碼

var findDisappearedNumbers = function(nums) {

let ans = []

for (let i = 0; i < nums.length; i++) {

const key = Math.abs(nums[i]) - 1

if (nums[key] > 0) {

nums[key] = -nums[key]

}

}



for (let i = 0; i < nums.length; i++) {

if (nums[i] > 0) {

ans.push(i+1)

}

}



return ans



};

在標記過一輪正負之後，再掃過一次陣列，如果是正數，就表示那個正數的位置i+1是消失的數字。