leetcode:只出现一次的数

136:只出现一次的数

题目:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
求解方法:

  1. map平衡二叉树。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为o(nlogn)o(nlogn),空间复杂度为o(n)o(n).
  2. unordered_map散列表。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为o(nlogn)o(nlogn),空间复杂度为o(n)o(n).
  3. 异或位运算。将数组中所有元素进行异或运算,返回最后异或的结果即可。时间复杂度为o(n)o(n),空间复杂度为o(1)o(1).

注意事项:

  1. 利用异或的原因:

0a=aaa=0:ab=ba:(ab)c=a(bc) 0 异或 a = a \\ a 异或 a = 0 \\ 交换律:a 异或 b = b 异或 a \\ 结合律:(a异或b)异或c = a异或(b异或c)

代码:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        return singleNumberWayIII(nums);
    }
    // way1:map统计;时间复杂度为o(nlogn),空间复杂度为o(n).
    int singleNumberWayI(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // count by map
        map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        int res = -1;
        // return when count = 1
        for(map<int,int>::iterator it = m.begin();it != m.end(); it++){
            if(it->second == 1){//important:不是*it->second而是it->second
                res = it->first;
                break;
            }
        }
        return res;
    }
    // way2:unordered_map;时间复杂度为o(nlogn),空间复杂度为o(n).
    int singleNumberWayII(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // count by unordered_map
        unordered_map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        // return when count = 1
        int res = -1;
        for(unordered_map<int,int>::iterator it = m.begin(); it != m.end(); it++){
            if(it->second == 1){//important:不是*it->second而是it->second
                res = it->first;
                break;
            }
        }
        return res;
    }
    // way3:异或位运算;时间复杂度为o(n),空间复杂度为
    int singleNumberWayIII(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        int res = 0;
        for(int num:nums){
            res ^= num;
        }
        return res;
    }
};

137:只出现一次的数II

题目:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
求解方法:

  1. map平衡二叉树。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为o(nlogn)o(nlogn),空间复杂度为o(n)o(n).
  2. unordered_map散列表。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为o(nlogn)o(nlogn),空间复杂度为o(n)o(n).
  3. 数组去重后数据和的3倍与原数组和的差除以2时间复杂度为o(nlogn)o(nlogn),空间复杂度为o(n)o(n)。原因是假设数组共3k+13k+1个数,其和记作sum1sum1,去重剩余k+1k+1个数,其和记作sum2sum2.去重后数组和的三倍即3sum23sum23k+33k+3个数的和,减去3n+13n+1个数的和,剩余的就是只出现一次的数的两倍。即:
    res=(3sum2sum1)2res = \frac{(3*sum2-sum1)}{2}
  4. 三进制不进位加法。上一题中利用异或运算,异或也称作二进制不进位加法,此题只要利用三进制不进位加法即可。时间复杂度为o(n)o(n),空间复杂度为o(1)o(1).

注意事项:

  1. 第三种利用数组和的方法,在求和的时候和药用long类型的数据,否则会越界.
  2. set和map<int,int>以及unordered_map<int,int>的迭代器的使用的不同方法。

代码:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
		return singleNumberWayIV(nums);
	}
    // way1:map统计;时间复杂度为o(nlogn),空间复杂度为o(n).
    int singleNumberWayI(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // count by map
        map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        int res = -1;
        // return when count = 1
        for(map<int,int>::iterator it = m.begin();it != m.end(); it++){
            if(it->second == 1){//important:不是*it->second而是it->second
                res = it->first;
                break;
            }
        }
        return res;
    }
    // way2:unordered_map;时间复杂度为o(nlogn),空间复杂度为o(n).
    int singleNumberWayII(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // count by unordered_map
        unordered_map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        // return when count = 1
        int res = -1;
        for(unordered_map<int,int>::iterator it = m.begin(); it != m.end(); it++){
            if(it->second == 1){//important:不是*it->second而是it->second
                res = it->first;
                break;
            }
        }
        return res;
    }
    // way3:sum of array of no duplication and sum of array;时间复杂度为o(nlogn),空间复杂度为o(n).
    int singleNumberWayIII(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // get sum of array and remove the duplications meanwhile
        set<int> s;
        long sum1 = 0;//important:long类型
        for(int num:nums){
            sum1 += num;
            s.insert(num);
        }
        // get sum of no duplications
        long sum2 = 0;
        for(set<int>::iterator it = s.begin(); it != s.end(); it++){
            sum2 += (*it);//important:是*it不是it
        }
        return (3*sum2 - sum1)/2;
    }
    // way4:三进制不进位加法;时间复杂度为o(n),空间复杂度为o(1).
    /*(1)用 one 记录到当前处理的元素为止,二进制1出现“1次”(mod 3 之后的 1)的有哪些二进制位;
      (2)用 two 记录到当前计算的变量为止,二进制1出现“2次”(mod 3 之后的 2)的有哪些二进制位。
      (3)当 one 和 two 中的 某一位 同时为1时表示该二进制位上1出现了3次,此时需要清零。即用二进制模拟三
		进制运算。
	  (4)最终 one 记录的是最终结果。
    */
    int singleNumberWayIV(vector<int>& nums) {
        int one = 0,two = 0, three = 0;
        for(int num:nums){
            one = one ^ num & ~two;
            two = two ^ num & ~one;
        }
        return one;//important:返回的是one(因为需要返回的是出现一次的数)
    }
    
};

260:只出现一次的数III

题目:
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
求解方法:

  1. 排序。(1)判断第一个元素和最后一个元素;(2)判断中间元素,只出现一次的元素的特点是既不等于前面的数也不等于后面的数。时间复杂度为o(nlogn)o(nlogn),空间复杂度为o(n)o(n).
  2. unordered_map散列表。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为o(nlogn)o(nlogn),空间复杂度为o(n)o(n).
  3. 位运算1。(1)通过异或运算找到只出现一次的两个元素的异或值记作mask = a^b,a和b因为不同,所以a和b的二进制中至少存在一位不同,即mask中至少存在一个1;(2)通过mask = mask & (-mask)找到mask中最右边的1;(3)通过mask对这两个数分组,找到两个数并更新结果。时间复杂度为o(nlogn)o(nlogn),空间复杂度为o(n)o(n)
  4. 位运算2。(1)通过异或运算找到只出现一次的两个元素的异或值记作mask = a^b,a和b因为不同,所以a和b的二进制中至少存在一位不同,即mask中至少存在一个1;(2)通过mask = mask & (~mask+1)找到mask中最右边的1;(3)通过mask对这两个数分组,找到两个数并更新结果。。时间复杂度为o(n)o(n),空间复杂度为o(1)o(1).

注意事项:

代码:

class Solution {
public:
	vector<int> singleNumber(vector<int>& nums) {
		return singleNumberWayIV(nums);
	}
    //way1:sort+check;时间复杂度为o(nlogn),空间复杂度为o(1);
    /*
        (1)判断第一个元素和最后一个元素;
        (2)判断中间元素,只出现一次的元素的特点是既不等于前面的数也不等于后面的数。
    */
    vector<int> singleNumberWayI(vector<int>& nums) {
        int n = nums.size();
        vector<int> res;
        if(n == 0){
            return res;
        }
        //sort
        sort(nums.begin(),nums.end());
        
        
        // check the first number
        if(nums[0] != nums[1]){
            res.push_back(nums[0]);
        }
        // check the last number
        if(nums[n-1] != nums[n-2]){
            res.push_back(nums[n-1]);
        }
        // check the middle element
        for(int i=1; i<n-1;i++){
            if(nums[i]!=nums[i-1] && nums[i]!=nums[i+1]){
                res.push_back(nums[i]);
            }
        }
        return res;
    }
    // way2:unordered_map;时间复杂度为o(nlogn),空间复杂度为o(n).
    vector<int> singleNumberWayII(vector<int>& nums) {
        int n = nums.size();
        vector<int> res;
        if(n == 0){
            return res;
        }
        // count
        unordered_map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        // push to the res when count = 1
        for(unordered_map<int,int>::iterator it = m.begin(); it != m.end(); it++){
            if(it->second == 1){
                res.push_back(it->first);
            }
        }
        return res;
    }
    // way3:位运算;use n = n & (-n) to find the first 1 from right;时间复杂度为o(n),空间复杂度为o(1).
    vector<int> singleNumberWayIII(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(2,0);
        if(n == 0){
            return res;
        }
        // 异或
        int mask = 0;
        for(int num:nums){
            mask ^= num;
        }
        // find first 1 from right
        mask = mask & (-mask);
        
        // find the result
        for(int num:nums){
            if((mask & num) == mask){
                res[0] = res[0] ^ num;
            }else{
                res[1] = res[1] ^ num;
            }
        }
        return res;
    }
    // way4:位运算;use n = n & (~n+1) to find the first 1 from right;时间复杂度为o(n),空间复杂度为o(1).
    vector<int> singleNumberWayIV(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(2,0);
        if(n == 0){
            return res;
        }
        // 异或
        int mask = 0;
        for(int num:nums){
            mask ^= num;
        }
        // find first 1 from right
        mask = mask & (~mask+1);
        
        // find the result
        for(int num:nums){
            if((mask & num) == mask){
                res[0] = res[0] ^ num;
            }else{
                res[1] = res[1] ^ num;
            }
        }
        return res;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章