【力扣Java】第一个出现两次的字母(HashSet与<<左移)

目录

题目描述

思路与算法

方法一:哈希表

HashSet说明

解题代码

方法二:位运算 

左移运算符(<<)

右移运算符(>>)

解题代码


题目描述

给你一个由小写英文字母组成的字符串 s ,请你找出并返回第一个出现 两次 的字母。

注意

  • 如果 a 的 第二次 出现比 b 的 第二次 出现在字符串中的位置更靠前,则认为字母 a 在字母 b 之前出现两次。
  • s 包含至少一个出现两次的字母。

示例 1

输入:s = "abccbaacz"
输出:"c"
解释:
字母 'a' 在下标 0 、5 和 6 处出现。
字母 'b' 在下标 1 和 4 处出现。
字母 'c' 在下标 2 、3 和 7 处出现。
字母 'z' 在下标 8 处出现。
字母 'c' 是第一个出现两次的字母,因为在所有字母中,'c' 第二次出现的下标是最小的。

示例 2

输入:s = "abcdd"
输出:"d"
解释:
只有字母 'd' 出现两次,所以返回 'd' 。

提示

  • 2 <= s.length <= 100
  • s 由小写英文字母组成
  • s 包含至少一个重复字母

思路与算法

方法一:哈希表

我们可以使用一个哈希表记录每个字母是否出现过。

具体地,我们对字符串 s 进行一次遍历。当遍历到字母 c 时,如果哈希表中包含 c,我们返回 c 作为答案即可;否则,我们将 c 加入哈希表。

HashSet说明

HashSet 实现了Set接口

HashSet 实际上是HashMap

执行 Set set = new HashSet();

public HashSet() {
    map = new HashMap<>();
}

可以存放null值,但是只能有一个null

HashSet不能保证元素的存取顺序一致

不能有重复的元素

HashSet线程不安全

没有带索引的方法,所以不能通过普通for循环进行遍历

解题代码

class Solution {
    public char repeatedCharacter(String s) {
        Set<Character> seen = new HashSet<Character>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (!seen.add(ch)) {
                return ch;
            }
        }
        // impossible
        return ' ';
    }
}

方法二:位运算 

注意到字符集的大小为 26,因此我们可以使用一个 32 位的二进制数 seen 完美地存储哈希表。如果 seen 的第 i (0≤i<26) 位是 1,说明第 i 个小写字母已经出现过。

具体地,我们对字符串 s 进行一次遍历。当遍历到字母 c 时,记它是第 i 个字母,seen 的第 i (0≤i<26) 位是 1,我们返回 c 作为答案即可;否则,我们将 seen 的第 i 位置为 1。

左移运算符(<<)

右边空出的位用0填补
高位左移溢出则舍弃该高位

例如:10 的二进制为 0000 1010 ,那么10左移3为就是 0101 0000,结果就是80

10<<3 ==80

右移运算符(>>)

左边空出的位用0或者1填补。正数用0填补,负数用1填补。注:不同的环境填补方式可能不同;
低位右移溢出则舍弃该位

例如:20 的二进制为 0001 0100 ,那么10右移3为就是 0000 0010,结果就是2

20>>3 ==2

解题代码

class Solution {
    public char repeatedCharacter(String s) {
        int seen = 0;
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            int x = ch - 'a';
            if ((seen & (1 << x)) != 0) {
                return ch;
            }
            seen |= (1 << x);
        }
        // impossible
        return ' ';
    }
}

代码的关键步骤我们来解释一下, seen & (1 << x),首先 1 << x,为什么是 1 呢,1 的二进制是 001,也就代表从 a 开始左移,左移多少,由字符的决定,也就是如图的意思:

比如输入的字符是 “abcd”,循环到字符 a,seen 存储二进制 001,循环到字符 b,seen 存储二进制 011(|= 操作),依次类推。

& 操作的意思是,只有两个变量都是 1,值才为 1,如果某字符再次出现,比如字符 a 再次出现,则 001 & 011 的值为 001,值不为 0,则把字符 a 输出。

如何存储呢,我们用到 |,或运算任何一个变量为 1,则值为 1,比如 001 | 010 的值为 011,用于存储状态。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java小白。。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值