剑指offer -- 数组中数字出现的次数

题目一:数组中只出现一次的两个数字
一个整形数组里除了两个数字之外,其他数字都出现了两次。请找出这两个只出现了一次的数字。要求时间复杂度为O(n),空间复杂度为O(1)。

例如数组[2,4,3,6,3,2,5,5],只有4和6两个数字只出现了一次,其他数字都出现了两次,所以输出4和6。我们可能直接想不出什么好的解决方案,可以优先考虑这个数组中只有一个数字只出现了一次,其他数字都出现了两次,怎么找到这个数字?

这两道题目都在强调一个或者两个数字都只出现了一次,其他数字出现两次,我们知道异或运算的一个性质:任何一个数字异或它自己都等于0,相同为0相异为1,那么我们从头到尾依次异或数组中的每个数字,最终结果就是那个只出现依次的数字。因为成对出现的数字会被抵消,0和只出现一次的数字异或,结果就是那个只出现一次的数字。

解决了这个问题,我们回到原始的问题上,我们知道了如何得到一组数据中只有一个只出现了一次的数字,那么能否通过某种方法将这两个只出现一次的数字分到两个子数组中去,然后分别求出这两个子数组中只出现一次的数字。如果我们还是依次从头到尾异或数组里的每个数字。最后的结果就是那两个只出现一次的数字的异或结果。最后异或结果的二进制表示,如果数位上为0,则证明这两个数字在这位上相同,如果数位上为1,则证明这两个数字在这位上不相同,我们找到异或结果二进制表示右数第一位为1的位置,根据这位是否为1将整个数组分为两个子数组,这样两个只出现一次的数字一定会被分到两个子数组中。我们的问题也就得以解决了。

 vector<int> singleNumbers(vector<int>& nums) {
    
    
		vector<int>ans;
		int length = nums.size();
		if(length<2) return ans;
		int result = 0;
		int nums1 = 0;
		int nums2 = 0;

		for(int i = 0 ;i < length ;i++){
    
    
			result = result ^ nums[i];
		}

		unsigned int index = FirstBit_1(result);

		for(int i = 0 ;i < length;i++){
    
    
			if (indexis_1(nums[i],index))
				nums1 = nums1 ^ nums[i];
			else
				nums2 = nums2 ^ nums[i];
		}
		ans.push_back(nums1);
		ans.push_back(nums2);
		return ans;
	}
	
 unsigned int FirstBit_1(int result){
    
    
		int i = 0;
		for(i = 0;i < 32;i++){
    
    
			if((result & 1) == 1)
				break;
			else
				result = result >>1;
		}
		return i;
	}
	
 bool indexis_1(int num,int index){
    
    
		int number = num >> index;
		return number & 1;
	}

题目二:数组中唯一只出现一次的数字
在数组中除一个数字只出现一次外,其他数字都出现了三次,请找出那个只出现了一次的数字。

看完了上面的题目,我们可能会试想一下能不能接着使用同样的解决办法解决这个问题,可是3个相同的数字异或运算结果还是这个数字,我们不太可能应用异或运算解决这个问题,但是位运算的思路我们可以沿用。

如果一个数字出现了3次,那么它的二进制表示的每一位,(0或者1)也出现了3次。如果把所有出现3次的数字的二进制表示的每一位都分别加起来,那么每一位的和都能被3整除。我们把数组中所有数字的二进制表示的每一位都加起来,如果某一位的和能被3整除,那么那个只出现一次的数字的二进制表示在这位为0,否则为1。

  int singleNumber(vector<int>& nums) {
    
    
        int bitnums[32] = {
    
    0};
        int length = nums.size();

        for(int i = 0;i<length;i++){
    
    
            unsigned int k = 1;
            for(int j = 31;j >= 0;j--){
    
    
                int bit = nums[i] & k;
                if(bit != 0)
                bitnums[j] = bitnums[j] + 1;
                k = k << 1;
            }
        }

        int result = 0;
        for(int i = 0;i < 32;i++){
    
    
            result = result << 1;
            result = result + bitnums[i] % 3;
        }

    }

这种解法的时间效率是O(n),空间复杂度为O(1)。
当然我们也可以应用其他方法,比如将数组排序后寻找或者应用哈希表。但是排序的时间复杂度为O(nlgn)(基于比较的排序算法时间复杂度不会低于O(nlgn)最好的情况能达到这个下限),哈希表需要O(n)的空间来记录每个数字出现的次数。

Guess you like

Origin blog.csdn.net/scarificed/article/details/120670741