力扣刷题笔记 136. list中只出现一次的数字 难度-简单,但是绝大部分人都不会最后一种方法

这是一道简单的题目,但是想真正做好,还是挺有意思的。

题目 链接:https://leetcode-cn.com/problems/single-number
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

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

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


一眼看去先忽略了最后一句 " 不使用额外空间 "
从头到尾遍历一遍

class Solution {
    public int singleNumber(int[] nums) {
        ArrayList<Integer> element = new ArrayList<Integer>();
    
        for(int x: nums){
            if(!element.contains(x)){
                element.add(x);
            }else{
                element.remove(new Integer(x));
            }
        }

        return element.get(0);
    }
}

一顿操作猛如虎,轻易通过提交,一看方案排名傻了眼。
在这里插入图片描述
刷题的大佬们的方案都是更优的方案。就让我们来寻找一下更优的方案吧。
在这里插入图片描述

方案一

改用 hashset, 因为hashset的查找比起arraylist更加快速。
具体的试验看此链接

class Solution {
    public int singleNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for (int n : nums) {
            if (!set.contains(n)) {
                set.add(n);
            } else {
                set.remove(n);
            }
        }

        for (int n : set) {
            return n;
        }

        return 0;
    }
}

在这里插入图片描述
方案一可以进一步改进为以下方式,减少对set的操作。

class Solution {
    public int singleNumber(int[] nums) {
    int sumOfSet = 0, sumOfNums = 0;
    Set<Integer> set = new HashSet();

    for (int num : nums) {
      if (!set.contains(num)) {
        set.add(num);
        sumOfSet += num;
      }
      sumOfNums += num;
    }
    return 2 * sumOfSet - sumOfNums;
  }
}

方案二

我们可以对原数组进行排序,对原来的数组进行排序。然后对比相邻的两个数字

class Solution {
    public int singleNumber(int[] nums) {
         Arrays.sort(nums);
        int i = 0;
        while (i < nums.length) {
            if(i==nums.length-1) return nums[i]; // 比较完毕到最后一个数字还没找到
            if(nums[i]!=nums[i+1]){
                return nums[i];
            }
            i = i + 2;
        }
        return 0;

    }
}

在这里插入图片描述


最佳方案


使用 位运算

https://www.runoob.com/java/java-operators.html

举例: A = 0011 1100 B = 0000 1101

举例符号意义
A&B = 0000 1100如果相对应位都是1,则结果为1,否则为0
A | B = 0011 1101|如果相对应位都是 0,则结果为 0,否则为 1
A ^ B = 0011 0001^如果相对应位值相同,则结果为0,否则为1
~A= 1100 0011按位取反运算符翻转操作数的每一位,即0变成1,1变成0。
A << 2得到240,即 1111 0000<<按位左移运算符。左操作数按位左移右操作数指定的位数。
A >> 2得到15即 1111>>按位右移运算符。左操作数按位右移右操作数指定的位数。
A>>>2得到15即0000 1111>>>按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。

由于题目中所说,两两相同,只有一个数字不同,所以会有两两数字在 ^ 操作后变成 0000000…
最终会有 2n 个数字互相抵消变成 0000… 最后只剩下格格不入的答案。

class Solution {
    public int singleNumber(int[] nums) {
    int num=nums[0];
    for(int i=1;i<nums.length;i++){
        num=num^nums[i];
    }
    return num;
    }
}

在这里插入图片描述


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值