剑指Offer面试题(第三十二天)面试题56(1)、56(2)

* 面试题56:数组中数字出现的次数


     * 题目一:数组中只出现一次的两个数字


     * 一个整型数组中除两个数字之外,其他数字都出现了两次。
     * 请写程序找出这两个只出现一次的庶长子。
     * 要求时间复杂度O(n),空间复杂度O(1).
     * 例如:输入数组{2,4,3,6,3,2,5,5},因为只有4和6这两个数字只出现了一次,
     * 其他数字都出现了两次,所以输出4和6
     * 
     * 思路:
     * 异或运算: 任何一个数字异或它自己都等于0
     * 所以我们可以从头到尾依次异或数组中的每一个数字,
     * 那么最终的结果刚好是只出现一次的数字,因为出现两次的数字全部在异或运算中抵消了
     * 
     * 将数组分为两个子数组,
     * 在每个子数组中,包含一个只出现一次的数字,而其他数字都出现了两次
     * 若是能够这样拆分数组,那么按照上面的异或运算过程就能求出出现一次的数字
     * 
     * 每个子数组都是从头开始依次进行异或运算,因为有两个数字肯定不一样,那么异或的结果一定不为0
     * 也就是说这个结果数字二进制表示中至少有一位为1
     * 在结果数字中找到第一个为1的位的位置,记为第N位。现在以第N位是不是1为标准将原数组分成两个子数组
     * 
     * 第一个数组中每个数字的第N位都是1,第二个数组中每个数字的第N位都为0
     * 
     * 现在已经将原数组分为两个子数组,每个子数组都包含一个只出现一次的数字
     * 出现两次的数字都已经抵消 

package Test;

public class No56First_FindNumsAppearOnce {

	/*
	 * 面试题56:数组中数字出现的次数
	 * 题目一:数组中只出现一次的两个数字
	 * 一个整型数组中除两个数字之外,其他数字都出现了两次。
	 * 请写程序找出这两个只出现一次的庶长子。
	 * 要求时间复杂度O(n),空间复杂度O(1).
	 * 例如:输入数组{2,4,3,6,3,2,5,5},因为只有4和6这两个数字只出现了一次,
	 * 其他数字都出现了两次,所以输出4和6
	 * 
	 * 思路:
	 * 异或运算: 任何一个数字异或它自己都等于0
	 * 所以我们可以从头到尾依次异或数组中的每一个数字,
	 * 那么最终的结果刚好是只出现一次的数字,因为出现两次的数字全部在异或运算中抵消了
	 * 
	 * 将数组分为两个子数组,
	 * 在每个子数组中,包含一个只出现一次的数字,而其他数字都出现了两次
	 * 若是能够这样拆分数组,那么按照上面的异或运算过程就能求出出现一次的数字
	 * 
	 * 每个子数组都是从头开始依次进行异或运算,因为有两个数字肯定不一样,那么异或的结果一定不为0
	 * 也就是说这个结果数字二进制表示中至少有一位为1
	 * 在结果数字中找到第一个为1的位的位置,记为第N位。现在以第N位是不是1为标准将原数组分成两个子数组
	 * 
	 * 第一个数组中每个数字的第N位都是1,第二个数组中每个数字的第N位都为0
	 * 
	 * 现在已经将原数组分为两个子数组,每个子数组都包含一个只出现一次的数字
	 * 出现两次的数字都已经抵消 
	 * 
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No56First_FindNumsAppearOnce f = new No56First_FindNumsAppearOnce();
		int[] array = {2,4,3,6,3,2,5,5};
		int num1 = 0;
		int num2 = 0;
		int[] num = f.FindNumsAppearOnce(array,num1,num2);
		System.out.println("数组中只出现一次的两个数字是:"+num[0]+","+num[1]);
		
	}
	
	
	public int[] FindNumsAppearOnce(int[] array,int num1,int num2) {
		if(array == null || array.length == 0)
			return null;
		
		int res = 0;
		
		//从头到尾依次异或运算   异或结果res   
		for(int i = 0;i < array.length;i++)
			res ^= array[i];
		
		//根据异或结果res  判断出第一位为1的位置  记为第N位
		int  firstIndex = findFirstIndex(res);
		
		num1 = 0;
		num2 = 0;
		
		//根据第N位是否为1将原数组分为两个子数组
		for(int i = 0;i < array.length;i++) {
			//判断第N(firstIndex)位 是1还是0  
			//若返回true,则是1  则进入num1数组进行异或运算
			//若返回false,则是0  则进入num2数组进行异或运算
			if(isBit1(array[i],firstIndex))
				num1 ^= array[i];
			else
				num2 ^= array[i];
		}
		int[] num = {num1,num2};
		return num;
		
	}

	//判断二进制数中firstIndex位是否为1  若为1 返回true  若为0 返回false
	private boolean isBit1(int item, int firstIndex) {
		// TODO Auto-generated method stub
		boolean check = false;
		//将item右移firstIndex位,则将第firstIndex位移动到最后
		item = item>> firstIndex;
		//只有最后一位也为1时,才能与运算之后仍为1
		if((item & 1) == 1)
			check = true;
		return check;
	}

	//二进制数  从右到左找到第一个“1”  返回N值
	private int findFirstIndex(int res) {
		// TODO Auto-generated method stub
		int index = 0;
		
		//因为1 和 1与结果仍为1  所以这是在找第一个为1的位置  
		//并且要保证位数不能超过32位(因为int类型变量占4个字符=32位)
		while((1 & res) == 0 && index < 32) {
			res = res >> 1;
			index ++;
		}
		
		return index;
	}

}

* 面试题56:数组中数字出现的次数


     * 题目二;数组中唯一只出现一次的数字


     * 自一个数组中除一个数字只出现一次以外,其他数字都出现了三次。
     * 请找出那个只出现一次的数字
     * https://www.jianshu.com/p/258785bdf416
     *
     * 思路:位运算思想
     * 若一个数字出现了第三次,那么它的二进制标识的每一位(0或1)也出现了三次。
     * 若是将所有出现三次的数字的二进制表示的每一位都分别加起来,那么每一位都能被3整除
     * 那么就只有出现一次的数字不能被3整除,因而求出只出现一次的数字

package Test;

public class No56Second_FindNumberAppearOnce {

	/*
	 * 面试题56:数组中数字出现的次数
	 * 题目二;数组中唯一只出现一次的数字
	 * 自一个数组中除一个数字只出现一次以外,其他数字都出现了三次。
	 * 请找出那个只出现一次的数字
	 * https://www.jianshu.com/p/258785bdf416
	 *
	 * 思路:位运算思想
	 * 若一个数字出现了第三次,那么它的二进制标识的每一位(0或1)也出现了三次。
	 * 若是将所有出现三次的数字的二进制表示的每一位都分别加起来,那么每一位都能被3整除
	 * 那么就只有出现一次的数字不能被3整除,因而求出只出现一次的数字
	 * 
	 * 
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] array = {1,2,3,4,1,1,2,3,3,2};
		No56Second_FindNumberAppearOnce f = new No56Second_FindNumberAppearOnce();
		System.out.println("数组中唯一只出现一次的数字是:"+f.FindNumberAppearOnce(array));
				
	}

	
	private int FindNumberAppearOnce(int[] array) {
		// TODO Auto-generated method stub
		if(array == null && array.length < 3)
			return 0; 
		//存储一个int(长度为32位)的每一bit的状态
		int[] bitSum = new int[32];//记录每一位的和
		int k = 3;
		//每一位  异或运算的结果
		for(int i = 0;i < array.length;i++) {//控制array数组
			int indexOfBit1 = 1;
			for(int j = 31;j >= 0;j--) {//控制bitSum数组
				//indexOfBit1 : 1,10,100,1000,10000,100000,...
				//若与运算不为0  则表示该位值为1  则令其位计数加1
				if((array[i] & indexOfBit1) != 0)
					bitSum[j] += 1;
				//结束之后将数左移一位
				indexOfBit1 <<= 1;
			}
		}
		
		//将异或结果除以3   记录每一位的值
		int result = 0;
		for(int i = 0;i < 32;i++) {
			result <<= 1;
			result += bitSum[i]%k;
		}
				
		return result;
	}

}

猜你喜欢

转载自blog.csdn.net/weixin_43137176/article/details/89680119