zoukankan      html  css  js  c++  java
  • 剑指offer系列——40.数组中只出现一次的数字i-ii

    Q:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
    T:
    1.排序后一个一个对比。
    2.使用hash。

    import java.util.*;
    public class Solution {
        public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
            Queue<Integer> arr = new LinkedList<Integer>();
            HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
            for (int i = 0; i < array.length; i++) {
                if (!map.containsKey(array[i])) {
                    map.put(array[i], 1);
                } else {
                    map.put(array[i], map.get(array[i]) + 1);
                }
            }
            for (int i = 0; i < array.length; i++) {
                if (map.get(array[i]) == 1) {
                    arr.add(array[i]);
                }
            }
            num1[0] = arr.poll();
            num2[0] = arr.poll();
            System.out.println(num1[0]);
            System.out.println(num2[0]);
        }
    }
    

    3.使用set。java的话可以使用。使用Set集合存储,Set集合不存储重复值,add()方法返回值为boolean类型,这一特点可以利用。
    Set集合的add方法,添加成功返回true,否则返回false,而Set集合不存储重复值,所以当要添加的数与集合中已存在的数重复时,不会再进行添加操作,返回false,这时再进行remove操作,将集合中已存在的那个与要添加的数相同的元素移除,这样将作为方法参数传递过来的整型数组遍历完后,到最后集合中就只剩下了那个只出现了一次的数字。

    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
            HashSet<Integer> set = new HashSet<Integer>();
     
            for (int i = 0;i < array.length;i++){
                if(!set.add(array[i])){
                    set.remove(array[i]);
                }
            }
     
            Object[] temp =set.toArray();
            num1[0] = (int) temp[0];
            num2[0] = (int) temp[1];
        }
    

    3.使用异或运算寻找。这个方法很巧妙。(感谢@披萨大叔)
    位运算中异或的性质:两个相同数字异或=0,一个数和0异或还是它本身。
    当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。

    依照这个思路,我们来看两个数(我们假设是AB)出现一次的数组。我们首先还是先异或,剩下的数字肯定是A、B异或的结果,因为其他数字全部都与自身异或结果为0了。
    这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字

        void FindNumsAppearOnce(vector<int> data, int *num1, int *num2) {
            if (data.size() < 2)
                return;
            if (data.size() == 2) {
                *num1 = data[0];
                *num2 = data[1];
            }
            int bitResult = 0;
            for (int i = 0; i < data.size(); i++)
                bitResult ^= data[i];
            //bitResult为AB异或的结果
            int index = 0;
            while (true) {
                //找到第一个1所在的位数,也是区分AB的指标
                if ((bitResult & 1) != 0)
                    break;
                bitResult >>= 1;
                index++;
            }
            *num1 = *num2 = 0;
            for (int i = 0; i < data.size(); i++) {
                //将AB分组,分组后异或,异或的最后结果就是AB
                if (isBit1(data[i], index))
                    *num1 ^= data[i];
                else
                    *num2 ^= data[i];
            }
        }
    
        bool isBit1(int target, int index) {
            int temp = target >> index;
            return (temp & 1) != 0;
        }
    

    Q:现在有一个整数类型的数组,数组中只有一个元素只出现一次,其余元素都出现三次。你需要找出只出现一次的元素
    注意:
    你需要给出一个线性时间复杂度的算法,你能在不使用额外内存空间的情况下解决这个问题么?
    A:
    若一个数出现三次,则其对应的二进制数每一位相加必为3或0。
    统计数组中所有元素的每一位,若为3的倍数,所求数的该二进制位对3取余为0,否则为1。
    举例:{1,1,1,2,2,2,3}
    01
    01
    01
    10
    10
    10
    11
    ——
    04%3 = 01
    40%3 = 10
    ——
    11

        public int singleNumber(int[] A) {
            int result = 0;
            for (int i = 0; i < 32; ++i) {
                int bits = 0;
                for (int value : A) {
                    bits += (value >> i) & 1;//依次获取A中每个元素的第i位,全部和1与后相加
                }
                //这里的|=和+=是一样的,因为当前只会得到在i为为0或1,右侧位全为0的值和i位为0但右侧不一定完全为0的值的和,用或和加都行,但用或会更快
                result |= (bits % 3) << i;
            }
            return result;
        }
    

    这个题还有种解法,可看 https://www.jianshu.com/p/c8612aef41af
    但实在太难了,没看懂。

  • 相关阅读:
    基础【五】字典的操作方法
    基础【四】列表的操作方法
    基础【三】字符串的操作方法
    基础【二】while循环及基本运算符
    基础【一】基础数据类型
    C++ string 深拷贝 与 浅拷贝
    多进程引用的动态链接库中的全局变量问题
    C++ 在类里面使用多线程技术
    openwrt 解决包依赖关系
    lua 的元表与元方法
  • 原文地址:https://www.cnblogs.com/xym4869/p/12331907.html
Copyright © 2011-2022 走看看