剑指 Offer !!56 - II. 数组中数字出现的次数 II

剑指 Offer 56 - II. 数组中数字出现的次数 II
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

 

示例 1:

输入:nums = [3,4,3,3]
输出:4
示例 2:

输入:nums = [9,1,7,9,7,9,7]
输出:1

在这里插入图片描述

通用版:
假设数组中,只有一个数出现1次,其余数都出现k次。问:这个“出现一次”的数是谁?

思路:
首先,将数组中每一位数转成二进制,
然后,对每一个二进制位做加法,第i位二进制记录到counts[i]中;
接着,counts[i]对k取余数,即counts[i]%k,(思考一下,此时那些出现k次的数的二进制表示的第i位,加和到counts[i]中,经过这次%k,他们的痕迹必然消失,所以此后count[i]就是那个“只出现一次”的数的二进制表示的第i位);
最后,将counts这个二进制数组 转换成对应十进制数即可。

下面是我参考了力扣K神之后自己写的代码,还有K神的代码:
区别在于:counts[i]的定义不同。

class Solution {
// 我自己写的,count[0]表示高位,count[31]表示低位
    public int singleNumber1(int[] nums) {
        int[] count = new int[32];
        for(int num:nums){
            for(int j=31;j>=0;j--){
                count[j] += (num&1);
                num>>>=1;
            }
        }
        
        int res=0;
        for(int j=0;j<32;j++){
            res=2*res+(count[j]%3); //注意这里是二进制转十进制
        }
        return res;
    }



     public int singleNumber(int[] nums) {
     // 大佬写的,count[31]表示高位,count[0]表示低位
         int[] counts = new int[32];
         for(int num:nums){
             for(int j=0;j<32;j++){
                 counts[j] += (num&1);
                 num>>>=1;
             }
         }
         int res=0;
         for(int i=0;i<32;i++){   
         // 注意这两句话不要写反了,可以想想:i=31时,res应该记录最低位;而如果写反了,res记录完counts[0],还会左移一位,此时最低位是0,这显然不对。          
             res<<=1;
             res|=(counts[31-i]%3);
             
         }
         return res;
     }
}

下面是《程序员代码面试指南》中解法
思路和上面相差不大,这里是把数组中每一个数转成k进制,每一位加和得到eo,
然后,对k取余数(注意考虑到“先求和再取余,等于,先取余再求和”,代码中这部分是一边求和一边取余),这一步之后那些“出现k次”的数在eo中必然没有留下痕迹,也就是说eo是我们要找那个“只出现一次”的数的k进制表示;
所以,将eo的k进制表示转化为十进制,即为最终结果。

  public int singleNumber(int[] nums) {
         return onceNum(nums,3);
     }
     public int onceNum(int[] nums, int k){
         int[] eo = new int[32];
         for(int i=0;i!=nums.length;i++){
             setExclusiveOr(eo,nums[i],k);
         }
         return getNumFromKSysNum(eo,k);
     }
     public void setExclusiveOr(int[] eo, int value, int k){
         int[] valueK = getKSysNumFromNum(value,k);
         for(int i=0;i<eo.length;i++){
             eo[i]=((eo[i]+valueK[i])%k); //先求和再取余,等于,先取余再求和
         }
     }
     public int[] getKSysNumFromNum(int num,int k){
         int[] res = new int[32];
         for(int i=0;i<32;i++){
             res[i]=(num%k); // 0位是最低位
             num/=k;
         }
         return res;
     }
     public int getNumFromKSysNum(int[] num,int k){
         int res=0;
         for(int i=0;i<32;i++){
             res = k*res+num[31-i];

         }
         return res;
     }

注:中间转成k进制的函数也可以写成下面的形式

 public int[] getKSysNumFromNum(int num,int k){
         int[] res = new int[32];
         int i=0;
         while(num!=0){
             res[i++]=(num%k); // 0位是最低位
             num/=k;
         }
         return res;
     }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值