leetcode 136|137|260. Single Number 1|2|3

本文介绍了三种寻找数组中唯一单例元素的算法解决方案,包括单例出现一次、三次及两次的情况。利用位操作技巧实现线性时间和常数空间复杂度的要求。

136. Single Number

Given an array of integers, every element appears twice except for one. Find that single one.

Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?


不能用额外空间,所以map也不行

本题非常巧妙的运用 异或运算符^

class Solution {
public:
    int singleNumber(vector<int>& nums) 
    {
        int ret = 0;
        for(int i = 0; i < nums.size(); i++)
            ret ^= nums[i];
        return ret;   
    }
};

137. Single Number II

Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

本题真的非常精妙!将每一个数看成32位二进制数,32位每一位相加再%3,剩下的就是那一个单数的二进制表达。

class Solution {
public:
    int singleNumber(vector<int>& nums) 
    {
        int count[32]={0};
        int ret=0;
        
        for(int i=0;i<32;i++)
        {
            for(int j=0;j<nums.size();j++)
            {
                count[i]+=(nums[j]>>i)&1;//一定不能用求余数,因为余数可能为-1,一定要用这种 位操作
            }
            count[i]=count[i]%3;
            ret|=count[i]<<i;//也可以为ret+=count[i]<<i;  |(或)位操作
        }
        return ret; 
    }
};


260. Single Number III

Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.

For example:

Given nums = [1, 2, 1, 3, 2, 5], return [3, 5].

Note:

  1. The order of the result is not important. So in the above example, [5, 3] is also correct.
  2. Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?
way-1:map

way-2:位操作
思路: 
我们知道如果一个数组中只有一个元素是出现一次,其他元素是出现两次的解题思路。只需要把所有的元素异或之后,那么异或的结果就是那个最终的单个的元素。这个思路很简单,就是把每个数理解为对应的二进制位,那么那些出现两次的元素,他们在1出现的所有位置,1都出现两次,异或完就还是0。而对于那个单出现一次得元素,它每个二进制位对应的数字出现一次。所以最终的异或结果就相当于把出现两次的元素全部去除。 
有了上面的基本的思路,我们可以将数组分成两个部分,每个部分里只有一个元素出现一次,其余元素都出现两次。那么使用这种方法就可以找出这两个元素了。不妨假设这两个元素是x,y,而且x!=y,那么最终所有的元素异或的结果就是res = x^y.很显然,res!= 0,如果res=0,那么x=y,与题意不符。既然res!=0,那么我们可以找出其二进制表示中的某一位是1,我们从低位往高位找,找到第一个二进制位是1的位置。对于原来的数组,我们可以根据这个位置是不是1就可以将数组分成两个部分。x,y在不同的两个子数组中。而且对于其他成对出现的元素,要么在x所在的那个数组,要么在y所在的那个数组。假设不是的话,那说明这一对数在这一位上一个是0一个是1,很显然不符它们是一对相等数的条件。


class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) 
    {
         //way-1
         /*
         map<int,int> mm;
         for(int i=0;i<nums.size();i++)
            mm[nums[i]]++;
         vector<int> ret;
         for(auto it=mm.begin();it!=mm.end();it++)
         {
             if(it->second==1)
                ret.push_back(it->first);
         }
         return ret;
         */
         
         //way-2
         int res = 0;
         for (auto num : nums)
             res ^= num;

         int flag = 1;
         while(1)
         {
             if (res & flag)
                break;
             flag <<= 1;
         }
         int res1 = 0, res2 = 0;
         for(auto num : nums)
         {
             if (num & flag)
                res1 ^= num;
             else
                res2 ^= num;
         }
         vector<int> ret = {res1,res2};
         return ret;
    }
};





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值