//
// E_268_MissingNumber.swift
// AlgorithmLeetCode
//
// Created by okerivy on 2017/3/9.
// Copyright ? 2017年 okerivy. All rights reserved.
// https://leetcode.com/problems/missing-number
import Foundation
// MARK: - 題目名稱: 268. Missing Number
/* MARK: - 所屬類別:
標(biāo)簽: Array, Math, Bit Manipulation
相關(guān)題目:
(H) First Missing Positive
(E) Single Number
(M) Find the Duplicate Number
*/
/* MARK: - 題目英文:
Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array.
For example,
Given nums = [0, 1, 3] return 2.
Note:
Your algorithm should run in linear runtime complexity. Could you implement it using only constant extra space complexity?
Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.
*/
/* MARK: - 題目翻譯:
給定一個(gè)數(shù)組,數(shù)組包含 n 個(gè)取自 0, 1, 2, ..., n 的不同的數(shù)字,從數(shù)組中找到丟失的那個(gè)數(shù)字。
例如:給定 nums = [0, 1, 3] 返回 2.
注意:
你的算法應(yīng)當(dāng)使用線性的時(shí)間復(fù)雜度(即時(shí)間復(fù)雜度為O(n))。你能夠只使用常量的額外空間來解決這題嗎?
*/
/* MARK: - 解題思路:
1.
從 0, 1, 2, ..., n 中取 n 個(gè)不同的數(shù)字組成數(shù)組 nums,尋找丟失的那個(gè)數(shù)字,非常自然的想法就是將 0, 1, 2, ..., n 加和之后,減去數(shù)組 nums 的和就得到了丟失的那個(gè)數(shù)字。
思路非常簡單。但是為了避免加和造成的溢出,
這里有一個(gè)小技巧:即不需要將兩個(gè)數(shù)組先加起來之后再做減法,可以邊加邊減。
例如 數(shù)組
數(shù)字 0 1 3 4 5 6 7 8 // 缺失的是 2
下標(biāo) 0 1 2 3 4 5 6 7 // 8個(gè)元素
進(jìn)行相加相減運(yùn)算 上下全部進(jìn)行 (i - nums[i])
0-0+1-1+3-2+4-3+5-4+6-5+7-6+8-7 = 0-0 1-1 3-3 4-4 5-5 6-6 7-7 2-8 = 2-8
(i - nums[i]) = 2-8
可以看到 2 已經(jīng)出來了 因?yàn)?nums.count = 8 所以再次相加一次 ans + (i - nums[i])
nums.count + 2-8 = 8 + 2-8 = 2
2.
其基本思想是利用異或XOR運(yùn)算。我們都知道一個(gè)a^b^b =a,這意味著相同數(shù)量的兩異或操作將消除數(shù)量和揭示原數(shù)。
在這個(gè)解決方案中,我運(yùn)用異或XOR操作的指標(biāo)和數(shù)組的值。
在一個(gè)沒有缺數(shù)字完整的陣列,指標(biāo)值應(yīng)完全對(duì)應(yīng)(nums[index] = index),所以在丟失數(shù)組,剩下的最后是失蹤數(shù)字。
例如 數(shù)組
數(shù)字 0 1 3 4 5 6 7 8 // 缺失的是 2
下標(biāo) 0 1 2 3 4 5 6 7 // 0..7 有8個(gè)元素
進(jìn)行異或運(yùn)算 上下全部進(jìn)行 (i ^ nums[i])
0^0^1^1^3^2^4^3^5^4^6^5^7^6^8^7 = 0^0^1^1^3^3^4^4^5^5^6^6^7^7^8^2 = 8^2
(i ^ nums[i]) = 8^2
可以看到 2 已經(jīng)出來了 因?yàn)?xor = nums.count = 8 所以再次異或一次 xor ^ (i ^ nums[i])
8^2^nums.count = 8^2^8 = 2
---------------------------------
異或是一種基于二進(jìn)制的位運(yùn)算,用符號(hào)XOR或者 ^ 表示,
其運(yùn)算法則是對(duì)運(yùn)算符兩側(cè)數(shù)的每一個(gè)二進(jìn)制位,同值取0,異值取1。
異或的性質(zhì)
交換律:a ^ b = b ^ a
結(jié)合律:a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c
d = a ^ b ^ c 可以推出 a = d ^ b ^ c
自反性:a ^ b ^ a = b
算法題目
①1-1000放在含有1001個(gè)元素的數(shù)組中,只有唯一的一個(gè)元素值重復(fù),其它均只出現(xiàn)一次。每個(gè)數(shù)組元素只能訪問一次,設(shè)計(jì)一個(gè)算法,將它找出來;不用輔助存儲(chǔ)空間,能否設(shè)計(jì)一個(gè)算法實(shí)現(xiàn)?
前面提到異或具有交換律和結(jié)合律,所以1^2^...^n^...^n^...^1000,無論這兩個(gè)n出現(xiàn)在什么位置,都可以轉(zhuǎn)換成為1^2^...^1000^(n^n)的形式。
其次,對(duì)于任何數(shù)x,都有x^x=0,x^0=x。
所以1^2^...^n^...^n^...^1000 = 1^2^...^1000^(n^n)= 1^2^...^1000^0 = 1^2^...^1000(即序列中除了n的所有數(shù)的異或)。
令,1^2^...^n^..^1000(序列中包含一個(gè)n)的結(jié)果為T
則1^2^..^n^..^n^..^1000(序列中包含2個(gè)n)的結(jié)果就是T^n。
T^(T^n)=n。
所以,將所有的數(shù)全部異或,得到的結(jié)果與1^2^3^...^1000的結(jié)果進(jìn)行異或,得到的結(jié)果就是重復(fù)數(shù)。
*/
/* MARK: - 復(fù)雜度分析:
間復(fù)雜度是O(n),空間復(fù)雜度為O(1)
*/
// MARK: - 代碼:
private class Solution {
// 加法求和
func missingNumber(_ nums: [Int]) -> Int {
var ans = 0
for i in 0..<nums.count {
ans = ans + i - nums[i]
}
ans += nums.count
return ans
}
// 異或運(yùn)算
func missingNumber2(_ nums: [Int]) -> Int {
var xor = nums.count
for i in 0..<nums.count {
xor = xor ^ i ^ nums[i];
}
return xor;
}
}
// MARK: - 測(cè)試代碼:
func missingNumber() {
print(Solution().missingNumber([0, 1, 3, 4, 5, 6, 7]))
print(Solution().missingNumber([1, 2, 3, 4, 5]))
print(Solution().missingNumber([0, 1, 2, 4, 5]))
print(Solution().missingNumber([5, 1, 2, 0, 4]))
}