Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Day 71 】2021-11-19 - 260. 只出现一次的数字 III #90

Open
azl397985856 opened this issue Nov 18, 2021 · 99 comments
Open

【Day 71 】2021-11-19 - 260. 只出现一次的数字 III #90

azl397985856 opened this issue Nov 18, 2021 · 99 comments

Comments

@azl397985856
Copy link
Contributor

260. 只出现一次的数字 III

入选理由

暂无

题目地址

https://leetcode-cn.com/problems/single-number-iii/

前置知识

  • 位运算
  • 数组
  • 哈希表

题目描述

给定一个整数数组 nums其中恰好有两个元素只出现一次其余所有元素均出现两次找出只出现一次的那两个元素示例 :

输入: [1,2,1,3,2,5]
输出: [3,5]
注意结果输出的顺序并不重要对于上面的例子, [5, 3也是正确答案你的算法应该具有线性时间复杂度你能否仅使用常数空间复杂度来实现
@yanglr
Copy link

yanglr commented Nov 18, 2021

思路:

位运算。

方法: 位运算

这个题是LeetCode136的加强版,136题中一趟异或即可搞定,这题弄一趟异或是不行的了,我们需要考虑分组处理,弄成2组,每一组满足LeetCode136的条件。

算法步骤:
对nums数组中的所有数异或运算, 算出两个落单数的异或结果theXor。

对theXor, 从低位开始向高位扫描, 找出两数的异或结果中的第1个1, 这恰好是两个落单数第一个不同的bit位。

接下来, 想办法把所有数分为2组, 且每个组只有1个落单的数, 只需要找一个概率均为50%的条件即可, 比如判断与mask最高位的同一个位置的二进制位是1还是0。

剩下的操作就是和LeetCode136 的位运算解法一样了。

代码:

实现语言: C++

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {        
        int theXor = 0;
        for (int num : nums)   // 对nums数组中的所有数异或运算, 算出两个落单数的异或结果
            theXor ^= num;
        int mask = 1;
        while ((theXor & mask) == 0) /*  从低位开始向高位扫描, 找出两数的异或结果中的第1个1, 这恰好是两个落单数第一个不同的bit位  */
            mask <<= 1;
        /* 接下来, 想办法把所有数分为2组, 且每个组只有1个落单的数, 只需要找一个概率均为50%的条件即可, 比如判断数组中的每一个元素与mask最高位的同一个位置的二进制位是1还是0 */
        int group1 = 0, group2 = 0;
        vector<int> res;
        for (int num : nums)   
        {
            if (num & mask)
                group1 ^= num; 
            else group2 ^= num;
        }
        res = {group1, group2};
        return res;
    }
};

复杂度分析:

  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

@ZacheryCao
Copy link

Idea:

Bitmask. Use XOR for all elements to get the XOR result (BITMASK) of two single elements. The variable BITMASK records the different bits of two single elements. Use BITMASK & (-BITMASK) to get the most right different bit (R_D) of two single elements. Use variable R_D to do the XOR again for all elements to get one single element X. The the other single element will be BITMASK^X

Code:

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        mask = 0
        for i in nums:
            mask ^= i
        d = mask & (-mask)
        x = 0
        for i in nums:
            if i & d:
                x ^= i
        return [x, mask ^ x]

Complexity:

Time: O(N)
Space: O(1)

@florenzliu
Copy link

Explanation

  • use a bitmask to get the 2 numbers
  • bitmask & (-bitmask): isolate the rightmost 1-bit
  • traverse all the numbers again and find the one with the same rightmost 1-bit, XOR

Python

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        a = 0
        for i in nums:
            a ^= i
        
        diff = a & (-a)
        
        b = 0
        for j in nums:
            if j & diff:
                b ^= j
        return [b, a^b] 

Complexity:

  • Time Complexity: O(n)
  • Space Complexity: O(1)

@biancaone
Copy link

class Solution:
    def singleNumber(self, nums):
        s = reduce(xor, nums)
        nz = s & (s-1) ^ s
        num1 = reduce(xor, filter(lambda n: n & nz, nums))
        return(num1, s ^ num1)

@yingliucreates
Copy link

link:

https://leetcode.com/problems/single-number-iii/

代码 Javascript

const singleNumber = function (nums) {
  let set = new Set();
  nums.forEach((x) => (set.has(x) ? set.delete(x) : set.add(x)));
  return Array.from(set);
};

@JiangyanLiNEU
Copy link

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        zor = reduce(xor, nums)
        res = reduce(xor, filter(lambda i: i & (zor ^ (zor & (zor - 1))), nums))
        return [res,res^zor]

@pophy
Copy link

pophy commented Nov 18, 2021

思路

  • bit运算规律
    • 交换律
    • 结合律
    • A^A = 0
    • A^0 = A
  • 因为只有两个不同数a, b出现1次
    • 将nums[] 依次异或 则有 eor = a^b
    • 因为a != b,则eor必有某一位等于1
    • 用eor & (~eor + 1)取得最右边的1 ,rightOne
      • 再次遍历nums,检查rightOne & nums[i] ==1
      • 如此可以将数组分为两部分 a,b分别在不同的两边
      • 由此可以分别得到a,b

Java Code

    public int[] singleNumber(int[] nums) {
        int eor = 0;
        for (int n : nums) {
            eor ^= n;
        }
        int rightOne = eor & (~eor + 1);
        int eor_ = 0;
        for (int n : nums) {
            if ((n & rightOne) == 0) {
                eor_ ^= n;
            }
        }
        int a = eor_;
        int b = eor ^ a;
        return new int[]{a,b};
    }

时间&空间

  • 时间 O(n)
  • 空间 O(1)

@yachtcoder
Copy link

Complexity: O(n); O(1)

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        x = reduce(xor, nums)
        a = reduce(xor, filter(lambda n: n & (x&(-x)), nums))
        return [a, x^a]

@tongxw
Copy link

tongxw commented Nov 18, 2021

思路

所有数字求异或,得到的结果是两个单独数字a和b的异或xor;
从右向左找到xor第一个二进制位取1的位置,根据异或性质,a和b在这个位置的二进制位不同
把数组分为两组,第一组在这个二进制位取0,另一组取1。
然后两组分别求异或,结果就是这两个数字。

代码

class Solution {
    public int[] singleNumber(int[] nums) {
      int xor = 0;
      for (int num: nums) {
        xor ^= num;
      }
      
      int mask = 1;
      while ((xor & mask) == 0) {
        mask = mask << 1;
      }
      
      int[] ans = new int[2];
      for (int num: nums) {
        if ((num & mask) == 0) {
          ans[0] ^= num;
        } else {
          ans[1] ^= num;
        }
      }
      
      return ans;
    }
}

TC: O(n)
SC: O(1)

@XinnXuu
Copy link

XinnXuu commented Nov 18, 2021

Idea

  1. 把所有的数都XOR起来,出现两次的数会被约掉,结果只会剩下两个出现一次的数a、b的XOR,记为sum(因为a和b只出现一次,所以其异或值sum不可能为0)。
  2. 步骤一的结果sum中,bit为1 的位置代表两数位不同的位置。
  3. 选择最低位第一个出现1的位置存为mask,对nums进行遍历,对所有的元素进行异或,分别找mask位值为1和0的元素。

Code


class Solution {
    public int[] singleNumber(int[] nums) {
        int sum = 0;
        for (int i : nums){
            sum ^= i;
        }
        int mask = 1;
        while ((mask & sum) == 0){
            mask <<= 1;
        }
        int[] res = new int[2];
        for (int i : nums){
            if ((mask & i) == 0){
                res[0] ^= i;
            } else {
                res[1] ^= i;
            }
        }
        return res;
    }
}

Complexity


  • Time Complexity: O(N)
  • Space Complexity: O(1)

@ginnydyy
Copy link

Problem

https://leetcode.com/problems/single-number-iii/

Notes

  • Bit manipulation and divide and conquer
  • x & (-x) keep the lowest bit with value 1
  • x & y keep the common bits with value 1
  • x ^ x == 0
  • 0 ^ x == x

Solution

class Solution {
    public int[] singleNumber(int[] nums) {
        int xor = 0;
        for(int num: nums){
            xor = xor ^ num;
        }
        
        int lowest = xor & (-xor);
        
        int res1 = 0;
        int res2 = 0;
        
        for(int num: nums){
            if((num & lowest) != 0){
                res1 = res1 ^ num;
            }else{
                res2 = res2 ^ num;
            }
        }
        
        return new int[]{res1, res2};
    }
}

Complexity

  • T: O(n), n is the length of the given nums array.
  • S: O(1), no extra space is required, only contant space.

@leo173701
Copy link

class Solution:
    def singleNumber(self, A: List[int]) -> List[int]:
        s = 0
        for x in A:
            s ^= x
        y = s & (-s)

        ans = [0,0]
        for x in A:
            if (x & y) != 0:
                ans[0] ^= x
            else:
                ans[1] ^= x                 
        return ans

@zhangzz2015
Copy link

思路

  • 利用 xor 异或操作,xor 操作的特点 ,相同为0 相异为1
  • xor(a, a) = 0 xor(a, 0) = xor(0, a) = a xor(1, 1) = xor(0, 0) = 0 xor(1, 0) = xor(0, 1) = 1 xor(a, b) = c => xor(a, c) = b and xor(b, c) = a
  • 当对所有数使用异或操作,讲得到是只出现1次的两个数进行的异或。关键如何把这两个数分开。a ^ b = c 。对于c 找到任意一位为1的数,可知道,只有a 或者 b 在该位置上为1,这个条件可用来把原始的数组分成两类,1类在该位置上是1的,另外1类在该位置上0 这样可在利用对分组所有数进行异或操作,则可得到 a 或者 b,利用 c ^ a = b 或者c^b = a 可得到另外一个数。
  • 另外一个trick,可利用 rightMost = c & -c 可找到 c 最右边的1的位置。

关键点

代码

  • 语言支持:C++

C++ Code:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        
// 1. xor(a, a) = 0
//2. xor(a, 0) = xor(0, a) = a
//3. xor(1, 1) = xor(0, 0) = 0
//4. xor(1, 0) = xor(0, 1) = 1
///5. xor(a, b) = c => xor(a, c) = b and xor(b, c) = a  
///     calculate c = a^ b
        int bitTwoNum =0; 
        for(auto & it : nums)
            bitTwoNum =(bitTwoNum ^ it) ; 
        
///  find c right most 
        int rightMost = 1; 
        while(  !(bitTwoNum & rightMost ) )
        {
            rightMost = rightMost << 1; 
        }

//  get a or b.         
        int bitOneNum =0; 
        for(auto & it : nums)
        {
            if( it & rightMost)
            {
                bitOneNum =(bitOneNum ^ it); 
            }
        }
//  get another b or a         
        return {bitOneNum , bitOneNum ^ bitTwoNum} ; 
        
    }
};

@joeytor
Copy link

joeytor commented Nov 18, 2021

思路

先遍历一次 nums, 对于每个元素 n xorsum^= n

这样操作之后, 出现两次都数字都会被消去, 因为 i ^ i = 0, 最后 xorsum 只剩下出现一次的元素的xor, 即 xorsum = i ^ j, i, j 是 nums 中只出现了一次的元素

这个元素因为 i 和 j 只出现了一次并且不同, 所以 一定不为 0, 那么去出 这个 xorsum 的 最 least significant 1 (last_one = xorsum & -xorsum)

对于 last_one 这个元素, 因为 xor 之后是 1, 代表 只有两种情况, i & last_one = 1, j & last_one = 0 或者反过来 i & last_one = 0, j & last_one = 1

所以再次遍历数组, 用 last_one & n 将 i 和 j 区分开成为两组

分别两组做 nums1 ^= n, num2 ^= n, 之前出现两次都元素一定会被分进同一组, 于是会互相消去, i 和 j 一定会被分在两组, 于是剩下的 nums1 和 nums2 就是 i 和 j, 就是两个出现了一次的元素

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xorsum = 0
        for n in nums:
            xorsum ^= n
        
        last_one = xorsum & -xorsum

        num1, num2 = 0, 0
        for n in nums:
            if last_one & n:
                num1 ^= n
            else:
                num2 ^= n

        return [num1, num2]

复杂度

时间复杂度: O(n) 两次遍历的复杂度

空间复杂度: O(1)

@heyqz
Copy link

heyqz commented Nov 18, 2021

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        s = set()
        for num in nums:
            if num in s: s.remove(num)
            else: s.add(num)
        return s

@CoreJa
Copy link

CoreJa commented Nov 18, 2021

思路

这才是承接136的题目,那个137根本就离谱的不着边

这个题目和136很相似,136是只出现一次的数字只有一个,通过异或的位运算可以巧妙的除掉所有其他数字,但此题出现的数字有两个,那当我们异或整个数组的时候,最终我们会得到两个不同的数异或的结果,而无法将其分开。

但是实际上这个结果是保留有特征的!举例来说,假设两个只出现一次的数是6和10,他们按位异或的结果是12。改写成二进制来看就是,b'00110'和b'01010'异或得到b'01100'。想想异或的原理能发现,结果中的1表示的是6和10的“不同”,即6和10的第3位一定是不同的。

有了这个结论,我们就可以利用这一点将数组分成两个部分,一部分的第三位位0,一部分的第三位为1。此时只出现一次的两个数字6和10一定会被分开,再分别对两个部分进行之前的异或操作即可。

这里补充一个快速找到异或结果中从右边开始第一个1的方法,即x&-x。由于计算机语言中-x是x原码按位取反+1,两者相与的结果是从右往左的第一个1到末尾。举例而言,6是b'0110',-6是b'1010',他们与的结果是b'0010'。

代码

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        ans = 0
        for num in nums:
            ans ^= num
        mid = ans & -ans
        ans_0, ans_1 = 0, 0
        for num in nums:
            if mid & num:
                ans_0 ^= num
            else:
                ans_1 ^= num
        return [ans_0, ans_1]

复杂度

TC: O(n)
SC: O(1)

@laofuWF
Copy link

laofuWF commented Nov 18, 2021

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        total = 0
        for n in nums:
            total ^= n
        
        mask = total & (-total)
        res = [0, 0]
        
        for n in nums:
            if n & mask != 0:
                res[0] ^= n
            else:
                res[1] ^= n
        
        return res

@mannnn6
Copy link

mannnn6 commented Nov 19, 2021

代码

class Solution {
    public int[] singleNumber(int[] nums) {
        int xorsum = 0;
        for (int num : nums) {
            xorsum ^= num;
        }
        int lsb = (xorsum == Integer.MIN_VALUE ? xorsum : xorsum & (-xorsum));
        int type1 = 0, type2 = 0;
        for (int num : nums) {
            if ((num & lsb) != 0) {
                type1 ^= num;
            } else {
                type2 ^= num;
            }
        }
        return new int[]{type1, type2};
    }
}

复杂度

时间复杂度: O(n)

空间复杂度: O(1)

@yan0327
Copy link

yan0327 commented Nov 19, 2021

思路:
由于一个数组只有两个出现一次的数,其余都是出现两次的数。
我们对整个数组遍历求异或,可以得到两个出现一次的数想异或的结果。
此时利用 x&-x 可以得到这个数最低有效位,可以判断其中一个数与这位相与为0,另外一位数与这位相与为1.
因此用num1,num2分别记录。如果异或等于1则与num1异或,否则与num2异或。
最终回到只出现一次的数组1

func singleNumber(nums []int) []int {
    num := 0
    for i:=0;i<len(nums);i++{
        num ^= nums[i]
    }
    lsb := num&-num
    type1,type2 := 0,0
    for i:=0;i<len(nums);i++{
        if nums[i]&lsb > 0{
            type1 ^= nums[i]
        }else{
            type2 ^= nums[i]
        }
    }
    return []int{type1,type2}
}

时间复杂度O(n)
空间复杂度O(1)

@shawncvv
Copy link

思路

位运算

代码

JavaScript Code

var singleNumber = function (nums) {
  let bitmask = 0;

  for (let n of nums) {
    bitmask ^= n;
  }

  bitmask &= -bitmask;

  const ret = [0, 0];

  for (let n of nums) {
    if ((n & bitmask) == 0) ret[0] ^= n;
    else ret[1] ^= n;
  }

  return ret;
};

复杂度

  • 时间复杂度:O(N)
  • 空间复杂度:O(1)

@BpointA
Copy link

BpointA commented Nov 19, 2021

思路

位运算

代码

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        x=0
        for i in nums:
            x=x^i
        t=0
        while x>>t&1==0:
            t+=1
        a=0
        b=0
        for i in nums:
            if i>>t&1==1:
                a=a^i
            else:
                b=b^i
        return [a,b]

        

@okbug
Copy link

okbug commented Nov 19, 2021

哈希表

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        unordered_map<int, int> map;
        vector<int> res;
        for (auto x : nums) map[x] ++;
        for (auto &[k, v]: map) {
            if (v == 1) {
                res.push_back(k);
            }
        }

        return res;
    }
};

@chun1hao
Copy link

var singleNumber = function(nums) {
    let set = new Set()
    for(let i of nums){
        if(set.has(i)) {
            set.delete(i)
        }else{
            set.add(i)
        }
    }
    return [...set]
};

@st2yang
Copy link

st2yang commented Nov 19, 2021

思路

  • 位运算

代码

  • python
class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xor = 0
        for i in nums:
            xor ^= i

        mask = 1
        while (mask & xor) == 0:
            mask <<= 1

        res = [0] * 2

        for i in nums:
            if i & mask == 0:
                res[0] ^= i
            else:
                res[1] ^= i
        
        return res

复杂度

  • 时间: O(n)
  • 空间: O(1)

@ZJP1483469269
Copy link

思路

位运算

代码

class Solution {
    public int[] singleNumber(int[] nums) {
        if(nums.length == 2) return nums;
        int x = 0;
        for(int i : nums){
            x ^= i;
        }
        int n = 0;
        for(int i = 0 ; i < 31 ;i++){
            if(((x >> i) & 1) == 1){
                n = i;
                break;
            }
        }
        int[] ans = new int[2];
        for(int i : nums){
            if(((i >> n) & 1) == 1) ans[0] ^= i;
            else ans[1] ^= i;
        }
        return ans;
    }
}

复杂度分析

时间复杂度:O(n)
空间复杂度:O(1)

@shamworld
Copy link

var singleNumber = function(nums) {
     // 异或(相同为0,不同为1)
    // 会消除出现两次的数字,total =会保留只出现一次的两个数字(x 和 y)之间的差异
    let total = 0;
    for(const i of nums){
        total ^= i;
    }
    let diff = total & (-total);
    // 从total中分离x和y
    let x = 0;
    for (let num of nums) {
        if ((num & diff) != 0) {
            x ^= num;
        }
    }
    // 找到了x,则y = total^x
    return [x, total^x];
};

@Mrhero-web
Copy link

class Solution {
public int[] singleNumber(int[] nums) {
if(nums.length == 2) return nums;
int x = 0;
for(int i : nums){
x ^= i;
}
int n = 0;
for(int i = 0 ; i < 31 ;i++){
if(((x >> i) & 1) == 1){
n = i;
break;
}
}
int[] ans = new int[2];
for(int i : nums){
if(((i >> n) & 1) == 1) ans[0] ^= i;
else ans[1] ^= i;
}
return ans;
}
}

@15691894985
Copy link

【day71】260. 只出现一次的数字 III

https://leetcode-cn.com/problems/single-number-iii/

思考::存入哈希表,再找value=1的,时间复杂度是1但是空间复杂度是key。位运算可巧妙降低空间复杂度:

  • 异或 xor 两个数相同为 0,所有找到只出现一次的数,将num中所有数进行异或操作,得到的必然是两个出现一次的数的异或值
  • 随便找一个x的bit为1的位置,1说明这两个出现一次的数在该位置的bit不同
  • 根据bit位为1将nums中的所有元素分为两类,一类为第l位为0的数,一类为第l位为1的数
  • 出现过两次的元素会在同一类中,出现过一次的元素会在不同类中,因此把每一类异或起来就得到出现的两个数
class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xor = a = b = 0
        length = len(nums)
        for i in nums:
            xor ^= i
        right_bit = xor & (-xor)#取最低位为1的数
        for i in nums:
            if right_bit & i:
                a ^= i
            else:
                b ^= i
        return [a, b]

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

@joriscai
Copy link

思路

  • 题意
    • 只有两个元素只出现一次
    • 其他都出现过两次
  • 根据位运算的异或运算,两数相同则为0
  • 方法是将两个元素分到两个组,这样分别对两组异或,两组的结果就是答案
    • 一次遍历后,剩下只出现一次两个元素的异或结果
    • 找出异或结果的第一个为1的位,0 xor 1 = 1。这个位可以区分两个元素
    • 最终再遍历,分成两组算出结果

代码

javascript

/*
 * @lc app=leetcode.cn id=260 lang=javascript
 *
 * [260] 只出现一次的数字 III
 */

// @lc code=start
/**
 * @param {number[]} nums
 * @return {number[]}
 */
var singleNumber = function(nums) {
  // 遍历一次
  // 根据题意,只有两个元素只出现一次,其他出现两次
  // 全部都异或,因为相同的会约掉,最终剩下 a xor b
  let xor = 0
  for (let i = 0; i < nums.length; i++) {
    const num = nums[i];
    xor ^= num
  }

  // 根据异或,两数对应位的数不同才能为1
  // 找出第一个为1的位,这样就可以将两个只出现过一次的元素,分别分到两个组
  let mask = 1
  while ((mask & xor) === 0) {
    mask <<= 1
  }

  const result = []
  // 重新再遍历一次
  for (let i = 0; i < nums.length; i++) {
    const num = nums[i];
    // 根据两组来遍历,每组只有一个只出现一次的元素
    // 最终两组全部异或后,只剩下出现一次的元素
    if ((num & mask) === 0) {
      result[0] ^= num
    } else {
      result[1] ^= num
    }
  }

  return result
};
// @lc code=end

复杂度分析

  • 时间复杂度:O(n),遍历两次,总的为2n。
  • 空间复杂度:O(1),异或结果、分组标记、两组的异或结果

@nonevsnull
Copy link

思路

  • 拿到题的直觉解法
    • bit也是弱项要多练;
    • XOR对相同的数会为0;不同的数不为零;题目里只有两个不同的数,如果全部来一遍,最后结果即为这两个不同的数XOR;即已知条件1:xor = x + y
    • x和y既然不同,那么在某个bit位一定是不同的,在这个位置上xor的值为1,使用<<=操作,找到这个不同的位置:已知条件2;
    • 根据这两个已知条件,遍历一遍原数组;将数组分两组做XOR:一组在[已知条件2]的位置的bit为0,另一组在该位置的bit为1。
    • x和y会分别位于这两个组,而其他的元素因为是两两相等的,因此会两两加入某一组,XOR后为0;两个组最后分别只剩下了x和y;

AC

代码

class Solution {
  public int[] singleNumber(int[] nums) {
    // difference between two numbers (x and y) which were seen only once
    int bitmask = 0;
    for (int num : nums) bitmask ^= num;

    // rightmost 1-bit diff between x and y
    int diff = 1;
    while((bitmask & diff) == 0){
        diff <<= 1;
    }

    int x = 0;
    // bitmask which will contain only x
    for (int num : nums) if ((num & diff) != 0) x ^= num;

    return new int[]{x, bitmask^x};
  }
}

复杂度

time: O(N)
space: O(1)

@peteruixi
Copy link

思路

  1. hashMap
  • 通过hashMap来存储nums中的每个值
  • �每个key存储对应出现次数
  • 最后便利找出答案
  1. XOR

代码

class Solution {
    public int[] singleNumber(int[] nums) {
        if(nums.length == 0){ return nums;}
        Map <Integer, Integer> freq = new HashMap<Integer, Integer>();
        for( int num: nums){
            freq.put(num, freq.getOrDefault(num,0)+1);
        }
        int[] ans = new int[2];
        int index= 0;
        for(Map.Entry<Integer, Integer> entry: freq.entrySet()){
            if(entry.getValue() == 1){
                ans[index++] = entry.getKey();
            }
        }
        return ans;
    }
}

复杂度分析

  • 时间复杂度 O(2N)
  • 空间复杂度 O(N)

@jaysonss
Copy link

思路

假设答案为x1,x2, 所有元素都异或可以得到x1和x2的异或值,我卡在这里就动不了了。

看了正确答案才知道解法:x1和x2必定有一个二进制位不同,所以抓住这点就可以把所有元素分为两类,并且两类中的元素除了x1和x2外都是成双成对的,所以两类的元素分别全异或就可以得到x1和x2

class Solution {
    public int[] singleNumber(int[] nums) {
        int xor = 0;
        for(int num:nums){
            xor ^= num;
        }

        //找到一个不同的二级制位
        int mask = 1;
        while((mask & xor) == 0){
            mask = mask << 1;
        }

        int[] ans = new int[2];
        for(int num : nums){
            if((num & mask) == 0){
                ans[0] ^= num;
            }else {
                ans[1] ^= num;
            }
        }
        return ans;
    }
}
  • 时间:O(n)
  • 空间:O(1)

@zszs97
Copy link

zszs97 commented Nov 19, 2021

开始刷题

题目简介

【Day 71 】2021-11-19 - 260. 只出现一次的数字 III

位运算

  • 分治

题目代码

代码块

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int xorsum = 0;
        for (int num: nums) {
            xorsum ^= num;
        }
        // 防止溢出
        int lsb = (xorsum == INT_MIN ? xorsum : xorsum & (-xorsum));
        int type1 = 0, type2 = 0;
        for (int num: nums) {
            if (num & lsb) {
                type1 ^= num;
            }
            else {
                type2 ^= num;
            }
        }
        return {type1, type2};
    }
};

复杂度

  • 时间复杂度 O(n)
  • 空间复杂度 O(1)

@guangsizhongbin
Copy link

guangsizhongbin commented Nov 19, 2021

func singleNumber(nums []int) (ans []int) {
    freq := map[int]int{}
    for _, num := range nums {
        freq[num]++
    }

    for num, occ := range freq {
        if occ == 1 {
            ans = append(ans, num)
        }
    }
    return
}

@chakochako
Copy link

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xorsum = 0
        for num in nums:
            xorsum ^= num
        
        lsb = xorsum & (-xorsum)
        type1 = type2 = 0
        for num in nums:
            if num & lsb:
                type1 ^= num
            else:
                type2 ^= num
        
        return [type1, type2]

@machuangmr
Copy link

代码

class Solution {
    public int[] singleNumber(int[] nums) {
        int xorsum = 0;
        for (int num : nums) {
            xorsum ^= num;
        }
        // 防止溢出
        int lsb = (xorsum == Integer.MIN_VALUE ? xorsum : xorsum & (-xorsum));
        int type1 = 0, type2 = 0;
        for (int num : nums) {
            if ((num & lsb) != 0) {
                type1 ^= num;
            } else {
                type2 ^= num;
            }
        }
        return new int[]{type1, type2};
    }
}

复杂度

  • 时间复杂度 O(n)
  • 空间复杂度 O(1)

@falsity
Copy link

falsity commented Nov 19, 2021

class Solution {
    public int[] singleNumber(int[] nums) {
        Map<Integer, Integer> freq = new HashMap<Integer, Integer>();
        for (int num : nums) {
            freq.put(num, freq.getOrDefault(num, 0) + 1);
        }
        int[] ans = new int[2];
        int index = 0;
        for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
            if (entry.getValue() == 1) {
                ans[index++] = entry.getKey();
            }
        }
        return ans;
    }
}

@brainlds
Copy link

位运算。运用到异或运算的性质: A ^ A = 0, 0 ^ A = A。因为题目中说数组中只有两个只出现一次的数字,先进行全数组异或运算,算出这两个只出现一次的数字的异或结果。之后对数组进行分组希望将这两个数字分到不同的组里。因为1 ^ 0 = 1,我们从低位开始往高位找异或运算结果中第一个出现1的数位,按照此数位进行分组,两个数字一定不在一组,其余数字在各组中一定成对出现,再次进行组内元素异或运算即可求得结果。

class Solution {
public int[] singleNumber(int[] nums) {
int[] res = new int[2];
int xor = 0;
// perform xor for all the nums, xor = unique1 ^ unique2
for (int num : nums) {
xor ^= num;
}

    int mask = 1;
    while ((xor & mask) == 0) {
        mask = mask << 1;
    }

    for (int num : nums) {
        if ((num & mask) == 0) {
            res[0] ^= num;
        }
        else {
            res[1] ^= num;
        }
    }
    return res;
}

}

@ymkymk
Copy link

ymkymk commented Nov 19, 2021

思路

哈希表

代码

``


class Solution {
    
    private static final Integer SINGLE_NUM = 1;

    public int[] singleNumber(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>(nums.length);
        Arrays.stream(nums).forEach(i -> map.put(i, map.getOrDefault(i, 0) + 1));
        int[] result = new int[2];
        int i = 0;
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue().equals(SINGLE_NUM)) {
                result[i++] = entry.getKey();
            }
        }

        return result;
    }
}

复杂度分析

时间复杂度:O(n)

空间复杂度:O(n)

@liuajingliu
Copy link

解题思路

位运算

代码实现

var singleNumber = function(nums) {
    let xorsum = 0;
    
    for (const num of nums) {
        xorsum ^= num;
    }
    let type1 = 0, type2 = 0;
    const lsb = xorsum & (-xorsum);
    for (const num of nums) {
        if (num & lsb) {
            type1 ^= num;
        } else {
            type2 ^= num;
        }
    }
    return [type1, type2];
};

复杂度分析

  • 时间复杂度:$O(N)$
  • 空间复杂度: $O(1)$

@biscuit279
Copy link

思路:哈希表

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        sta = {}
        ans = []
        for num in nums:
            if num in sta:
                sta[num] += 1
            else:
                sta[num] = 1
        for k,v in sta.items():
            if v==1:
                ans.append(k)
        return ans

时间复杂度:O(n)
时间复杂度:O(n)

@wangyifan2018
Copy link

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xornum = 0
        for num in nums:
            xornum ^= num
        
        lsb = xornum & (-xornum)
        type1, type2 = 0, 0
        for num in nums:
            if num & lsb:
                type1 ^= num
            else:
                type2 ^= num
        return [type1, type2]

@yibenxiao
Copy link

【Day 71】260. 只出现一次的数字 III

代码

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int ret = 0;
        for (int n : nums)
            ret ^= n;
        int div = 1;
        while ((div & ret) == 0)
            div <<= 1;
        int a = 0, b = 0;
        for (int n : nums)
            if (div & n)
                a ^= n;
            else
                b ^= n;
        return vector<int>{a, b};
    }
};

复杂度

时间复杂度:O(N)

空间复杂度:O(1)

@BreezePython
Copy link

思路

哈希计数

代码

from collections import Counter

class Solution:
    def singleNumber(self, nums):
        hash_nums = Counter(nums)
        return [x for x in hash_nums if hash_nums[x] == 1]

复杂度

  • 时间复杂度:O(N)
  • 空间复杂度:O(N)

@taojin1992
Copy link

Logic:

# Bruteforce Logic:

using hashset, linear traverse, 
if not seen, put into the set
else, remove from the set

return the set content

- Complexity:
Space: O(n + 2) -> O(n), n = nums.length
Time: O(n), n = nums.length

# Bitwise Logic:
XOR operation:
0 ^ number = number

a ^ b ^ a = b

a, b, c, d, a, b

linearly traverse to get: c ^ d
c != d
therefore, c ^ d != 0

There must be at least one bit == 1 in c ^ d

assume that bit == 1 is the n-th bit

divide number into 2 groups:
group 1 of numbers with n-th bit == 1      
group 2 of numbers with n-th bit == 0


--> if c in group 1, then d in group 2
--> if c in group 2, then d in group 1


xor &= -xor to get the lowest one bit that's different between c and d

linearly traverse the array: (note the parentheses)
if ((cur & xor) == 0) {
    // group 1
} else {
    // group 2
}

XOR in each group to find c and d (we don't care which case above it is)

Exclusive or in each group, c and d only appears once respectively in each group

- examples:

if we use the middle one, we can see that there are 2 sets of numbers that we can just use the simple single number to find out the single in each partition
1 = 001
1 = 001
5 = 101 ✅
2 = 010
2 = 010
3 = 011 ✅

if we use the leftmost one, we can still partition the array into the sets and do simple single number on it
1 = 001
2 = 010
1 = 001
3 = 011✅
2 = 010
5 = 101✅

* it reminds me of the mice-poison question from xifa to divide into 2 groups using bit

------------------------------------------------------------------------------------
0001
0110
----
0111    = 7

1001   = -7, 

-------
0001   = after xor & -xor, get the lowest bit that's different between c and d 

To convert a positive number into a negative number, using the two’s complement representation, invert all of the bits of the number and add 1. In this representation , the left-most bit is considered to be the sign-bit (without adding an extra bit), where 1 is a -ve and 0 is a +ve. 


if XOR = Integer.MIN_VALUE, -XOR = -Integer.MIN_VALUE
Integer.MIN_VALUE == -Integer.MIN_VALUE
https://softwareengineering.stackexchange.com/questions/348172/in-java-why-does-integer-min-value-integer-min-value

for this corner case, XOR &= -XOR -> XOR = Integer.MIN_VALUE
10000000000000000000000000000000
still holds. 

https://stackoverflow.com/questions/36684359/binary-representation-of-integer-min-value



- Complexity:
Time: O(n), n = nums.length
Space: O(2) -> O(1)

Code:

class Solution {
    public int[] singleNumber(int[] nums) {
        int xor = 0;
        for (int num : nums) {
            xor ^= num;
        }
        xor &= -xor; // to get the lowest bit different between two unique numbers
        int group1 = 0, group2 = 0;
        for (int cur : nums) {
            if ((cur & xor) == 0) { // group 1
                group1 ^= cur;
            }
            else { // group 2
                group2 ^= cur;
            }
        }
        return new int[]{group1, group2};
    }
    
    // bruteforce
    public int[] singleNumber1(int[] nums) {
        Set<Integer> numSet = new HashSet<>();
        for (int num : nums) {
            if (numSet.contains(num)) {
                numSet.remove(num);
            }
            else {
                numSet.add(num);
            }
        }
        
        int[] res = new int[2];
        int i = 0;
        for (int num : numSet) {
            res[i++] = num;
        }
        return res;
    }
}

@skinnyh
Copy link

skinnyh commented Nov 19, 2021

Note

  • Use XOR and divide the nums into two groups based on the 1 bit of XOR result. Then find the single number if each group.

Solution

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xor = 0
        res = [0, 0]
        for n in nums:
            xor ^= n
        xor &= -xor
        for n in nums:
            if xor & n == 0:
                res[0] ^= n
            else:
                res[1] ^= n
        return res

Time complexity: O(N)

Space complexity: O(1)

@Zhang6260
Copy link

JAVA版本

思路:主要考察的是位运算

class Solution {
   public int[] singleNumber(int[] arr) {

       int a =0;
       for(int i=0;i<arr.length;i++){
           a^=arr[i];
       }
       int t =1;
       while ((t&a)!=t){
           t=t<<1;
       }
       int x =0;
       int y =0;
       for(int i=0;i<arr.length;i++){
           if((arr[i]&t)==t){
               x^=arr[i];
           }else{
               y^=arr[i];
           }
       }
       int dp[] =new int[2];
       dp[0] =x;
       dp[1] =y;
       return dp;
   }
}

时间复杂度:O(n)

空间复杂度:O(1)

@Lydia61
Copy link

Lydia61 commented Nov 19, 2021

只出现一次的数字 Ⅲ

思路

  • 对所有数进行异或运算,并查找第一位为1的数位
  • 按照该数位对所有数进行分组异或运算
  • 则两组的结果分别为两个出现一次的数

代码

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xor = reduce(lambda x, y: x ^ y, nums)
        flag = 1
        while not flag & xor:
            flag <<= 1
        res = [0, 0]
        for n in nums:
            if n & flag:
                res[0] ^= n
            else:
                res[1] ^= n
        return res

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

@yj9676
Copy link

yj9676 commented Nov 19, 2021

class Solution {
    public int[] singleNumber(int[] nums) {
        Map<Integer, Integer> freq = new HashMap<Integer, Integer>();
        for (int num : nums) {
            freq.put(num, freq.getOrDefault(num, 0) + 1);
        }
        int[] ans = new int[2];
        int index = 0;
        for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
            if (entry.getValue() == 1) {
                ans[index++] = entry.getKey();
            }
        }
        return ans;
    }
}

@pan-qin
Copy link

pan-qin commented Nov 19, 2021

Idea:

for num that appear twice, when use xor on them, the result is 0; but for num that only appear once, xor will give a non-zero result. So we can use xor on all nums in the array, the result must be a non-zero number. We then want to find out the lowest non-zero bit, which can be implemented by & with 1 and shift left. Then all nums can be divided into two types, one is when & with bit, it is nonzero, and the other is & with bit and get a zero. Maintain two int, one int is used to xor with type1 and the other is used to xor type 2. Nums that appear twice will be categorized into the same group and than canceled in xor operation. So finally the two int is the two num that appear only once.

Complexity:

Time: O(n)
Space: O(1)

class Solution {
    public int[] singleNumber(int[] nums) {
        int xor=0;
        for(int n: nums)
            xor^=n;
        int bit=1;
        while((bit&xor)==0) {
            bit<<=1;
        }

        int type1=0, type2=0;
        for(int n:nums) {
            if((n&bit)!=0)
                type1^=n;
            else
                type2^=n;
        }
        return new int[]{type1,type2};
        
    }
}

@itsjacob
Copy link

Intuition

  • [tags] bit operations, bit-xor, bit-and
  • three properties of xor:
    • bit xor operation is commutative: a ^ b ^ c ^ d ^ a ^ b = a ^ a ^ b ^ b ^ c ^ d = (a ^ a) ^ (b ^ b) ^ c ^ d
    • xor of same numbers resets all bits to zero: a ^ a = 0
    • zero xor any number is that number itself: 0 ^ c = c

Implementation

class Solution
{
public:
  vector<int> singleNumber(vector<int> &nums)
  {
    // - bit xor operation is commutative, ie.,
    //   a ^ b ^ c ^ d ^ a ^ b = a ^ a ^ b ^ b ^ c ^ d = (a ^ a) ^ (b ^ b) ^ c ^ d
    // - xor of same numbers resets all bits to zero, ie.,
    //   a ^ a = 0
    // - zero xor any number is that number itself, ie., 0 ^ c = c

    // suppose c and d are the different numbers that only appear once
    // step 1: variable twoNums will have two non-zero bits that represent the two numbers
    int twoNums{ 0 };
    std::for_each(nums.begin(), nums.end(), [&twoNums](int &num) { twoNums ^= num; });

    // step 2: create a one-bit mask that can be used later to tell apart the two numbers
    int mask{ 1 };
    while ((mask & twoNums) == 0) {
      mask = mask << 1;
    }

    // step 3: variable mask is used to segment the two different numbers, same num will go to same category twice
    std::vector<int> res(2, 0);
    for (auto const &num : nums) {
      if ((num & mask)) {
        res[0] ^= num;
      } else {
        res[1] ^= num;
      }
    }
    return res;
  }
};

Complexity

  • Time complexity: O(n)
  • Space complexity: O(1)

@V-Enzo
Copy link

V-Enzo commented Nov 20, 2021

插个眼

昨天晚上比较忙,有事没做。

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {        
        int theXor = 0;
        for (int num : nums)   // 对nums数组中的所有数异或运算, 算出两个落单数的异或结果
            theXor ^= num;
        int mask = 1;
        while ((theXor & mask) == 0) /*  从低位开始向高位扫描, 找出两数的异或结果中的第1个1, 这恰好是两个落单数第一个不同的bit位  */
            mask <<= 1;
        /* 接下来, 想办法把所有数分为2组, 且每个组只有1个落单的数, 只需要找一个概率均为50%的条件即可, 比如判断数组中的每一个元素与mask最高位的同一个位置的二进制位是1还是0 */
        int group1 = 0, group2 = 0;
        vector<int> res;
        for (int num : nums)   
        {
            if (num & mask)
                group1 ^= num; 
            else group2 ^= num;
        }
        res = {group1, group2};
        return res;
    }
};

@hellowxwworld
Copy link

vector<int> singleNumber(vector<int>& nums) {        
    int ret = 0;
    for (auto& v : nums) {
        ret ^= v;
    }

    int firstOne = 1;
    while ((firstOne & ret) == 0)
        firstOne = firstOne << 1;

    int a = 0, b = 0;
    for (auto & v : nums) {
        if ((firstOne & v) == 0)
            a ^= v;
        else
            b ^= v;
    }

    return {a, b};
}

@Bingbinxu
Copy link

思路
1、所有异或找出两个数不同的位
2、以最小的位将两个数分为两组
3、通过异或得到两数
代码(C++)

实现语言: C++
C++ Code:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int bitTwoNum =0; 
        for(auto & it : nums)
            bitTwoNum =(bitTwoNum ^ it) ; 
        int rightMost = 1; 
        while(  !(bitTwoNum & rightMost ) )
        {
            rightMost = rightMost << 1; 
        }
       
        int bitOneNum =0; 
        for(auto & it : nums)
        {
            if( it & rightMost)
            {
                bitOneNum =(bitOneNum ^ it); 
            }
        }    
        return {bitOneNum , bitOneNum ^ bitTwoNum} ; 
        
    }
};

复杂度分析
时间复杂度: O(N)
空间复杂度: (1)

@Richard-LYF
Copy link

class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
freq = Counter(nums)
return [num for num, occ in freq.items() if occ == 1]

@for123s
Copy link

for123s commented Nov 21, 2021

思路

关键点

代码

  • 语言支持:C++

C++ Code:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int sum = 0;
        for(int i=0;i<nums.size();i++)
            sum ^= nums[i];
        int res1 = 0, res2=0;
        int temp = (sum==INT_MIN?sum:sum&(-sum));
        for(int i=0;i<nums.size();i++)
        {
            if(temp&nums[i])
                res1 ^= nums[i];
            else
                res2 ^= nums[i];
        }
        return {res1,res2};
    }
};

复杂度分析

令 n 为数组长度。

  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(1)$

@q815101630
Copy link

Xor

The point of this question is to understand that one number xor with itself will be canceled. Therefore we can xor all numbers at the first chance so that we are left with one number a XOR b. Then we know that a and b must be different so one bit of them must be different as well. Therefore, we can set a mask to detect the bit they differ and xor with all numbers based on the value on that bit!

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xor = 0 
        for i in nums:
            xor ^= i
        
        m = 1
        while m & xor == 0:
            m = m<<1
        a, b = 0, 0
        for num in nums:

            # for those numbers with 1 at bit m, it will later be reduced to only one number because it cancel itself by xor 
            if num & m != 0:
                a ^= num
            # for those numbers with 0 at bit m, it will later be reduced to only one number because it cancel itself by xor 
            else:
                b ^= num
        return [a,b]

Time :O(n)
Space: O(1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests