56_剑指offer_java_数组中数字出现的次数

目录

 

题目一

题目描述

测试用例

解题思路

参考解题

题目二

题目描述

测试用例

解题思路

参考解题


题目一

 

题目描述

数组中只出现一次的两个数字。

一个整型数组里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

 

测试用例

功能测试(数组中有多对重复的数字;数组中没有重复的数字)

 

解题思路

1、小技巧任何数异或自己为0,并且0异或任何数等于其数本身。 即,a ⊕ a = 0 、b ⊕ 0 = b;

2、假设数组只有一个出现一次的数字:从头到尾异或数组的元素,根据1,相同元素抵消为0,剩下的结果为只出现一次的元素;

3、想办法把题目中的数组分成两个子数组,子数组都满足2中的条件

神奇的技巧

1)首先得到题目中的数字依次异或的结果;如{2,2,3,3,4,5,5,6} 异或结果为0010;

2)倒数第二位为1,根据这个标准,即倒数第二位是不是1,将原数组分为两组:{2,2,3,3,6}  、 {4,5,5},这就得到满足2中的两个子数组了。

原因:出现两次的数字一定分配到同一个子数组,不同的两个数字也会分到不同子数组。

 

参考解题

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    // 子问题: 1、数组中只有一个出现一次的数字,找出这个数字;
    //         2、将有两个出现一次数字的数组划分为两个子数组,转换为子问题1
    
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        // 异常
        if(array == null || array.length  < 2){
            num1[0] = 0;
            num2[0] = 0;
        }
        
        // 所有数字异或结果
        int resultOfXor = 0;
        for(int i = 0 ; i < array.length; ++i ){
            resultOfXor  ^= array[i];
        }
        
        // 异或结果右边第一个为1的位数
        int indexOf1 = FindFirstBitsIs1(resultOfXor);
        
        // 分组,该位数为1的为一组,不为1的为一组,然后分别异或
        
        num1[0] = 0;
        num2[0] = 0;
        for(int j = 0 ; j < array.length ; ++j){
            if( isBit1(array[j], indexOf1) ){
                num1[0] ^= array[j];
            }else{
                num2[0] ^= array[j];
            }
        }
        
    }
    
    // ---在整数num的二进制中找到最右边是1的位------
    public int FindFirstBitsIs1(int num){
        int index = 0 ;
        // int类型二进制有几位?32位
        while((num & 1) == 0 && index < 32){
            num = num >> 1;
            index++;
        }
        return index;
    }
    
    // ---判断整数num从右边开始的第indexOf1位是不是1-----
    public boolean isBit1(int num, int indexOf1){
        num = num >> indexOf1;
        return ((num & 1) == 1); // 1 & 1 = 1
    }
}

 

题目二

 

题目描述

数组中唯一只出现一次的数字

在一个数组中除一个数字值出现一次之外,其他数字都出现了三次,请找出那个只出现一次的数字。

 

测试用例

功能测试(唯一值出现一次的数字分别为0、正数、负数;重复出现三次的数字分别是0、正数、负数)

 

解题思路

 

3个相同的数字异或结果还是该数字,并不能使用异或很好的解题。转换思路,使用位运算。

概述:把数组所有数字(二进制表示)的每一位相加,如果该位能够被3整除,表示只出现一次的m在该位为0,否则在该位为1。举例,比如{2,2,2,3,3,3,4}

2 10
2 10
2 10
3 11
3 11
3 11
目标数m(4)100
intSum163
intSum该位能否被3整除不能
intSum该位对3取余100
目标数m该位是什么100

 

参考解题

package Sword;
/**
 * 
 * @author tyler
 *
 */

public class AppearOnceOther3times {
	
	public int  findNumberAppearOnce(int[] array) throws Exception {
		// 异常	
		if (array == null || array.length == 0) {
			throw new Exception("非法输入");
		}
		
		// 求intSum数组
		int[] bitSum = new int[32];
		for (int  i = 0;  i < array.length; ++i) {
			// 外循环,遍历数组每一个数字
			for (int j = 31; j >= 0; --j) {
				// 内循环,判断数组中某个数的各个位是否为1,是则对intSum加1
				if ((array[i] & 1) == 1) {
					bitSum[j]++;	
				}
				
				array[i] = array[i] >> 1;
			}
		}
		
		// 根据规律,生成目标数字
		int result = 0;
		for (int i = 31; i >= 0; --i) {
			result = result >> 1;
			result += bitSum[i] % 3;
		}
		
		return result;
				
	}
	
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值