程序员面试金典 - CM1 确定字符互异

确定字符互异

http://www.nowcoder.com/questionTerminal/9618c2a9e8a14c3e82954ee14168f592

题目中要求不能使用额外的存储结构,因此会限制很多空间上的用法。 首先想到的就是在时间上的暴力法。
针对每一个字符, 我们都可以做到 从 当前字符的下一个位置来进行判断是否与当前字符相同,如果相同则返回 false, 重复上面步骤, 直到判断完所有字符没有相同的,就返回 true。

a  e  i  o  u
↑  → 判断每一个是否与当前字符相同
a  e  i  o  u
   ↑  → 判断每一个是否与当前字符相同
...
    public boolean checkDifferent(String iniString) {
        // write code here
        // 暴力法
        if(iniString == null || iniString.length() == 0)
            return false;
        for (int i = 0; i < iniString.length(); i++) {
            // 每一个位置都从当前位置的下一个位置进行判断 因为前面的已经判断过 不许重复判断
            for (int j = i+1; j < iniString.length(); j++) {
                if(iniString.charAt(i) == iniString.charAt(j))
                    return false;
            }
        }
        return true;
    }

分析: 时间复杂度 由于嵌套了两层循环 图片说明
空间复杂度 O(1).

如果 将 String 字符串换成char[] 数组, 这样就可以使用很多优化的方法。 这里自己进行了一些拓展,并不符合本题要求,仅供参考。

注意:使用 char 数组来处理,就又可以提出多种解法。

第一种 使用 堆排序,使用大根堆 小根堆都可以, 思路如下,

思路: 构建堆之后, 在进行堆排序的过程中可以进行判断相邻的两个字符是否相等,如果相等: chars[j] == chars[j+1]就返回 false, 如果完成排序就说明没有相同的字符, 就可以返回 true。

以 a e i o u a 为例子

图片说明

代码

public boolean heapSortWithChars(char[] chars){
        if(chars == null || chars.length == 0)
            return false;
        // 构建堆
        int length = chars.length;
        for(int i = length/2-1; i >= 0; i--){
            buildHeap(chars, i, length);
        }
        // 排序
        for (int j = length-1; j >= 0 ; j--) {
            char temp = chars[j];
            chars[j] = chars[0];
            chars[0] = temp;
            // 进行判断
            if(j < length-1 && chars[j] == chars[j+1]){
                return false;
            }
            buildHeap(chars, 0, j);
        }
        return true;
    }

    public void buildHeap(char[] chars, int i, int length){
        char temp = chars[i];  // 先暂存当前元素
//        System.out.println("chars["+i+"] :"+chars[i]);
        for(int k = 2*i+1; k < length; k=2*k+1){
            // 先判断 左右谁大
            if(k+1 < length && chars[k] < chars[k+1]){
                k++;
            }
            if(chars[k] > temp){
                chars[i] = chars[k];
                i = k;
            }else {
                break;
            }
        }
        chars[i] = temp;
    }

分析:时间复杂度 O(nlogn) 堆排序 、 空间复杂度 O(n) 使用了char数组。

第二种 就是使用 寻找 最长非重复子串 的方法来进行处理

思路: 如果 最后得到的非重复最长子串 长度 等于 源字符串长度 那么就说明 没有重复的字符,就返回 true, 否则就返回 false。 另外, 在判断的过程中 其实不一定要等到遍历完整个字符串才能判断, 如果中间有重复子串,可以提前进行判断。
可以定义 pre 指向非重复子串的 前一个位置 ,比如 aabc 那么 pre 就指向 第一个 a ,即pre = 0 (初始为-1)。
开辟一个 256 数组map, 其中map[i] 表示 i 最近出现的位置, 初始化为 -1。

代码

// 使用 不重复的最长字符串
    public boolean checkDifferent3(String s){
        if(s == null || s.length() == 0)
            return false;

        char[] chars = s.toCharArray();
        int[] map = new int[256];
        // map[i] 表示 i 最近出现的位置
        for (int i = 0; i < 256; i++) {
            map[i] = -1;
        }
        // pre 指向 非重复串的前一个位置   比如 aabc pre 指向第一个 a
        int pre = -1;
        int len = 0;

        for (int i = 0; i < chars.length; i++) {
            // 取pre 或者map[chars[i]] 作为最新的起始点
            pre = Math.max(pre, map[chars[i]]);
            // 获取 当前长度值
            int curLen = i - pre;
            // 查看是否是最长的长度值
            len = Math.max(len, curLen);
            map[chars[i]] = i;
            // 提前判断 重复情况 "剪枝"
            if(pre != -1)
                return false;
        }
        return true;
    }

分析: 时间复杂度 O(n), 空间复杂度 O(n)
其中 非重复子串部分思想, 参考了 左程云的《程序员代码面试指南》。
以上为个人的理解,如有错误,欢迎指正和讨论。

全部评论
利用最长非重复子串的方法中,可以不用计算长度吧。直接用pre!=-1作为判断标准。如果遍历完成了还没有遇到pre!=-1的情况说明整个字符串没有相同的字符,返回true就好了。
点赞 回复 分享
发布于 2020-09-29 17:07
题目不是要求不用额外的存储结构吗
点赞 回复 分享
发布于 2021-04-29 00:36

相关推荐

点赞 评论 收藏
分享
9 4 评论
分享
牛客网
牛客企业服务