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

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

题目 链接: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);
    }
}

一顿操作猛如虎,轻易通过提交,一看方案排名傻了眼。
在这里插入图片描述
刷题的大佬们的方案都是更优的方案。就让我们来寻找一下更优的方案吧。
力扣刷题笔记 136. list中只出现一次的数字 难度-简单,但是99%的人都不会最后一种方法_第1张图片

方案一

改用 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;
    }
}

在这里插入图片描述


你可能感兴趣的:(LeetCode刷刷刷,算法,java,leetcode,数据结构,快速排序)