Loading...

判定字符是否唯一的面试题想到

题目出处 力扣(LeetCode) https://leetcode-cn.com/problems/is-unique-lcci/

实现一个算法,确定一个字符串 s 的所有字符是否全都不同。

示例 1:

输入: s = "leetcode"
输出: false 
示例 2:

输入: s = "abc"
输出: true
限制:

0 <= len(s) <= 100
如果你不使用额外的数据结构,会很加分。

就是这么简单一道题,居然看到灰常多的解法。虽然题目提示尽量不用额外的数据机构,但实际开发中,内置的数据结构除了内存稍微多了那么一点点,我觉得并没什么不妥。但作为面试题来做就按出题者的意图来做吧。

java8 stream

执行效率2ms 内存36.4左右 一行代码就搞定

public static boolean isUnique(String astr) {
        return astr.chars().distinct().count() == astr.length();
    }

set去重一

执行效率0ms 内存36.4左右

public boolean isUnique(String astr) {
        HashSet<Character> set = new HashSet<>();
        for(char c : astr.toCharArray()){
            if(!set.add(c)) {
                return false;
            }
        }
        return true;
    }

set去重二

执行效率0ms 内存36.1左右

public boolean isUnique(String astr) {
        HashSet<Character> hashSet = new HashSet<>();
        for (int i = 0; i < astr.length(); i++) {
            hashSet.add(astr.charAt(i));
        }
        return hashSet.size() == astr.length() ;
    }

set去重三

执行效率0ms 内存36.1左右

public boolean isUnique(String astr) {
        HashSet<Character> set = new HashSet<>();
        for (int i = 0; i < astr.length(); i++) {
            if(!set.add(astr.charAt(i))) {
                return false;
            }
        }
        return true;
    }

set去重一、二大致相同,但第一种方法中astr.toCharArray()方法自然是多用了内存。而去重三和二比的话在某些情况有不同之处的

传统遍历一

执行效率0ms 内存36.2

## 当前字符是否在  是否包含在后续的字符中
public boolean isUnique(String astr) {
        for(int i=0;i<astr.length();i++){
           if(astr.substring(i+1).contains(String.valueOf( astr.charAt(i) )) )
               return false;
        }
        return true;
    }

传统遍历二

执行效率0ms 内存36.1

## 当前字符,从后往前找索引是否一致
 public boolean isUnique3(String astr) {
        for(int i=0;i<astr.length();i++){
            if(astr.lastIndexOf(astr.charAt(i))!=i)
                return false;
        }
        return true;
    }

位运算

属于大神级表演

  • int 32位 long 64位

  • 位运算
    long bitIndex = 1L << c; 表示将二进制的0001(这里是64位,前面的零省略了。)向左边移动c个位置,那么就可以得到唯一对应的站位
    low64 |= bitIndex; 表示和之前的那个拼接起来
    low64 & bitIndex; 与操作,如果没出现过,结果是0,如果出现过,则不会是0

  • ASCII码
    ASCII码是0-127 一共128个,可以代表所有的简单字符。

思路
由于ASCII码字符个数为128个,而且题目说了如果你不使用额外的数据结构,会很加分。因此可以使用两个64位的long变量来存储是否出现某个字符,二进制位1表示出现过, 0表示未出现过。具体代码如下

  public boolean isUnique(String astr) {
        long low64 = 0;
        long high64 = 0;

        for (char c : astr.toCharArray()) {
            if (c >= 64) {
                long bitIndex = 1L << (c - 64);
                if ((high64 & bitIndex) != 0) {
                    return false;
                }

                high64 |= bitIndex;
            } else {
                long bitIndex = 1L << c;
                if ((low64 & bitIndex) != 0) {
                    return false;
                }

                low64 |= bitIndex;
            }

        }

        return true;
    }

这里还看到如下一段阐述

如果我是面试官,我会希望看到什么?

可能有点偏题,毕竟这里主要就是做题的地方。。

如果我是面试官,会考虑主要考察什么,就我的工作经验看,大多数主要是招聘工程师的,面试者如果什么问题都没有,直接写个二重循环搞定,会首先给个50分,如果能写点判断字符串是否为null的,60分。

直接上手什么bitset,什么位运算的,我会先问他,题目中有没有交代字符串的字符一定是26个英文字母?如果是unicode环境,你是不是要准备2^16/8个字节的空间?在实际项目中,风险可控,结果可期更重要,绝大多数时候不在乎那点时间和资源。

所以我期望面试者不要急于解答,我希望他先问我问题:

字符串的字符范围,如果我告诉他,26个小写英文字母,那可能一开头直接判断如果字符长度>26, 直接返回False,做到这一点的,80分
如果我告诉他ascii字符集,然后他的代码里有边界检查,并且针对不同的范围有不同的侧重点,比如说ascii字符集,那也就是128个可能性,16个字节的位运算比较好
如果我告诉他是unicode,没有字符范围,老老实实排序再判断是比较符合我对工程师的要求的,因为算法性能稳定,没有额外资源要求,一眼看出没什么不可预见的风险,100分。
就是说,有些东西,没想到或者一时没想到根本不是问题,日常工作中稍微提示一下即可,但是缜密的思维对于程序员来说更重要。

一道看似平平无奇的面试题,解法多种多样。很多时候我们在工作中的时候,在大多数情况下只会写一种解决方案,甚至你也只会想一种解决方法,也不会去比较哪个更优秀。但如果真正的有这种思考的态度,或许做技术久了,差距就出来了

posted @ 2020-11-02 15:20  倒霉蛋蛋虾  阅读(188)  评论(0编辑  收藏  举报