剑指offer-43.数组中唯一出现一次的数(278)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_38332722/article/details/100586855

43.数组中唯一出现一次的数(278)

  • 题目描述:在一个数组中,除一个数字只出现一次,其他的数字都出现三次,找出只出现一次的数字。

  • 思路:只有一个数字出现一次,其他的数字都出现三次,那么对数组中的二进制的每位求和,每一位要么是3的倍数,要么比大1,当是3的倍数时,只出现一次的数 的对应位为0,否则为1。10 ,10,10,01(二进制)将对应位求和之后为:3,1;然后再对求和之后的每一位对3求余数:0,1 。则0,1 为只出现一次的数的二进制。

    相关数字对应二进制表示:10 = 1010      4 = 0100     5 = 0101
    1)申请一个长度为32(因为int32bit)int型数组bitsum,用于记录整型数字每一位出现1的次数。
    bitsum = 00000000 00000000 00000000 00000000
    
    2)将每个数字计入到bitsum[]10-> 00000000 00000000 00000000 00001010
    4 -> 00000000 00000000 00000000 00001110
    5 -> 00000000 00000000 00000000 00001211
    5 -> 00000000 00000000 00000000 00001312
    4 -> 00000000 00000000 00000000 00001412
    4 -> 00000000 00000000 00000000 00001512
    5 -> 00000000 00000000 00000000 00001613
    
    3)bitsum的每一个元素对3取模:
    bitsum = 00000000 00000000 00000000 00001010
    
    4)将bitsum作为一个32bit的整数:
    result = 00000000 00000000 00000000 00001010 = 10
    
  • 代码

    package _43.数组中唯一出现一次的数;
    
    import java.util.Arrays;
    
    /**
     * 题目描述:在一个数组中,除一个数字只出现一次,其他的数字都出现三次,找出只出现一次的数字。
     * @author Administrator
     *
     */
    public class NumberAppearOnce {
    
    	public static int findNumberAppearOnce(int[] data){
    		if(data == null || data.length < 3) return -1;
    		//1.计算数组中每一位的和
    		int[] bit = new int[32];
    		int flag = 1;
    		for(int i = 0; i < data.length; i++){
    			flag = 1;
    			for(int j = 31; j >= 0; j--){
    				if((data[i]&flag) != 0){// !=0:表示该位 为1;不能用==1来判断,1010&1000=1000=8
    					bit[j] += 1;
    				}
    				flag <<= 1;
    			}
    		}
    		System.out.println(Arrays.toString(bit));
     		//2.每一位的和3求余数,求余之后的二进制就是只出现一次的数字
    		int result = 0;
    		for(int i = 0; i < bit.length; i++){
    			result <<= 1; //乘以2(例如:124 = (1*10+2)*10+4)
    			result += bit[i] % 3;
    		}
    		//3.返回结果
    		return result;
    	}
    	
    	public static void main(String[] args) {
    		int[] data = {10,4,5,3,5,4,3,3,4,5};
    		int[] data1 = {0,-4,5,3,5,-4,3,3,-4,5};
    		int[] data2 = {-10,-4,5,3,5,-4,3,3,-4,5};
    		int appearOnce = findNumberAppearOnce(data);
    		int appearOnce1 = findNumberAppearOnce(data1);
    		int appearOnce2 = findNumberAppearOnce(data2);
    		System.out.println(appearOnce);
    		System.out.println(appearOnce1);
    		System.out.println(appearOnce2);
    	}
    }
    

总结:

(1)所有元素都出现了2次,只有一个只出现了1次。

​ 将所有的数去异或(|)运算,最终结果就为只出现一次的整数。因为亦或有如下两条关键性质:
​ (1)ab=ba,意味着对一个无序数组进行亦或等价于对排序后的数组进行疑惑
​ (2)aba=b,意味着出现两次的数字可以抵消掉。
​ 因此,将数组中所有元素进行亦或,即可得到那个只出现一次的一个数字。。

(2)所有的元素都出现了2次,只有两个数只出现了1次。

​ 两个不一样的数值异或,结果的二进制中至少有一个1。将所有的数做异或运算之后(结果就为这两个不一样的数做异或运算),根据 1 出现的位置,将该位置为 0 的分为一组,为 1 的分为一组,这样一定可以将这两个不一样的数分开,而对于相同的数,一定是分在一组的。然后再分别对两组数据进行异或运算,分别得出两组数据中不一样的那个数。

(3)所有的元素都出现了3次,只有一个只出现了1次。

​ 如果一个数字出现3次,那么它的二进制表示的每一位(0或1)也出席3次,如果把所有出现3次的数字的二进制表示的每一位都分别加起来,那么每一位的和都能被3整除。例如:01、01、01;三个0,三个1 。

​ 如果只出现一次的那个数某一位为0的话,那么该位的和就能被3整除;如果那一位为1的话,那么该位就不能被3整除,余数为1 。

​ 结论:将元素的每一位求和并保存(32位的数组),然后让每一位和对3求余,最终整型数组中表示的二进制数就为只出现一次的数的二进制。

(4)所有的元素都出现了K次,只有一个只出现了1次。

​ 和(3)一样的思路,将3改为k即可。

(5)所有的元素都出现了K次,只有一个出现了M次(0<m<k)

​ 沿用(3)的思路,将元素的所有的位分别求和之后,对k求余,余数要么为0,要么为m(>0),而第三题是求余之后要么为0,要么为1 。只用改动(3)中求余结果的代码即可。

//2.每一位的和3求余数,求余之后的二进制就是只出现2次的数字(k=3;m=2)
		int result = 0;
		for(int i = 0; i < bit.length; i++){
			result <<= 1; //乘以2(例如:124 = (1*10+2)*10+4)
			result += (bit[i] % 3) > 0 ? 1 : 0;
		}

猜你喜欢

转载自blog.csdn.net/qq_38332722/article/details/100586855