力扣-->#136. 只出现一次的数字-->数组-简单(位运算、异或、集合存储、两倍集合计算)

136. 只出现一次的数字

题目描述:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:
输入: [2,2,1]
输出: 1

示例 2:
输入: [4,1,2,1,2]
输出: 4

方法一:位运算
注意异或运算的三个特点:
利用异或运算的这三个特点,将数组中的所有元素进行异或,那么出现两次的元素就会互相异或为0,只剩下出现一次的元素,该元素与0进行异或,结果仍为该元素,由此可实现空间复杂度O(1)。具体代码如下:

执行结果:通过
执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.7 MB, 在所有 Java 提交中击败了26.43%的用户
通过测试用例:61 / 61

class Solution {
    public int singleNumber(int[] nums) {//异或
    //一个数与0异或=自己
    //一个数与自己异或=0
    //所以,一个数与自己异或后(为0)又与别的数异或一次,最终会得到那个别的数
        int single=0;//a^0=a    
        for(int num:nums){
            single^=num;
        }
        return single;
    }
}

官网中还给出了另外三中解答,分别是用集合存储、哈希表与两倍集合计算,当然相对而言异或这种方法更佳,简单易懂性能高。在此也将给出集合存储两倍集合计算解答方法的具体过程。

方法二:使用集合存储数字
利用集合判断数字是否存在,若不存在,则add(num);若存在,则remove(num);遍历完整个集合后,最终会剩下那个唯一出现一次的数字。
在这种解法中,遇到一个之前没遇过的问题,就是如何将集合Set中的值取出来,并且转换为int最终返回。一开始觉得很简单,但后来发现Set取值的方法是通过迭代器,迭代结果并不能直接强制转化为int类型;也尝试过使用set.toString最后再转化或截取的方法,但toSting后集合的表现形式是数组,所以需要将[ ]截取掉(去头去尾),截取之后正整数好办,但如果是-1就需要改变截取范围,但截取前又不知道是多少,这就形成了矛盾。最后发现可以直接迭代器访问下一个,问题解决,具体代码如下(注释部分为解决问题过程中用的):

执行结果:通过
执行用时:11 ms, 在所有 Java 提交中击败了14.58%的用户
内存消耗:38.8 MB, 在所有 Java 提交中击败了12.44%的用户
通过测试用例:61 / 61

class Solution {
    public int singleNumber(int[] nums) {//利用集合
        Set<Integer> single=new HashSet<Integer>();
        for(int num:nums){
            if(single.contains(num)){
                single.remove(num);
            }else{
                single.add(num);
            }
        }
        //single.toArray();//set.toString()之后也是呈现数组形式
        // Integer result=Integer.parseInt(single.toString().substring(1,2));//将数组中的俩[]去掉,适用于正整数
        //Integer result=Integer.parseInt(single.toString().substring(1,single.size()+2));//适用于-1
        return single.iterator().next();
    }
}

上面是我做测试的过程,具体代码如下:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class test {
    public static void main(String[] args) {
        Set<Integer> set=new HashSet<>();
        set.add(-1);
//        set.toArray();
//        set.toString();
        System.out.println(set.size());
        System.out.println(set);
        System.out.println(set.toString());
        System.out.println(set.toString().substring(1,set.size()+2));
        Integer result=Integer.parseInt(set.toString().substring(1,set.size()+2));
        System.out.println(result);
    }
}

方法三:两倍集合计算
这个方法虽然花了很长时间去琢磨,性能上也没有位运算的好,但毕竟是完全靠自己一点点摸索出来的,还是成就感和收获满满!!
思路:
1.利用集合的唯一性,循环判断,所数组中的值不在集合中,则将该值放入集合;若存在,则跳过;
2.一趟循环之后集合中有且仅有包含数组中的所有值一次,并在便利的过程中对数组中的所有值进行累加,得到数组中各个元素值之和sum;
3.之后遍历集合,获取到集合中所有元素之和setsum;
4.最后将setsum的两倍减去sum得到的差值即为仅出现一次的那个数的值。
注意点:
1.由于set集合有序不重复的特点,传入的数组为[4,1,2,1,2],传出的集合为[1, 2, 4],即需要注意数组的第一个元素并不一定就是集合中的第一个元素;
2.第14行代码中,利用set.iterator().next();方法逐一获取set集合中的值,例如数组[4,1,2,1,2],那么利用该方法获取值的最终结果为[1, 2, 4],即会从第一个元素开始获取;
3.在累加集合的过程中,每次获取到值之后,都需要利用set.remove(set.iterator().next());方法将集合中的头元素去掉,以获得集合中的下一个元素。

执行结果:通过
执行用时:636 ms, 在所有 Java 提交中击败了5.95%的用户
内存消耗:39.5 MB, 在所有 Java 提交中击败了5.02%的用户
通过测试用例:61 / 61

class Solution {
    public int singleNumber(int[] nums) {//两倍集合判断
        Set<Integer> set=new HashSet<>();
        int setsum=0;
        int sum=0;    //Set是有序的,nums中的第一个并不一定是set中的第一个
        for(int num:nums){
            if(!set.contains(num)){
                set.add(num);
            }
            setsum+=num;//计算数组数值之和
        }
        int size=set.size();
        while(size>0){
            sum+=set.iterator().next();
            set.remove(set.iterator().next());//之前忘记加这句,搞得一直在加第一个数
            size--;
        }
        return sum*2-setsum;
    }
}

以下是找bug的过程中使用的测试代码:

        int[] nums={4,1,2,1,2};
        Set<Integer> set=new HashSet<>();
        int setsum=0;
        int sum=0;
        for(int num:nums){
            if(!set.contains(num)){
                set.add(num);
                System.out.println("set="+set.toString());
            }
            setsum+=num;
            System.out.println("setsum="+setsum);

        }
        System.out.println("========");
        int size=set.size();
        System.out.println(size);
        System.out.println(set.toString());
        while(size>0){
            System.out.println("next="+set.iterator().next());
            set.remove(set.iterator().next());
//            sum+=set.iterator().next();
//            System.out.println("sum="+sum);
            size--;
        }
        System.out.println(sum*2-setsum);

平平无奇小白程序媛一枚,欢迎各位大佬交流指教,如有不正确的地方,欢迎留言改正,谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值