leetcode 387. First Unique Character in a String

目录

一、问题描述

二、代码实现

1、两遍遍历

2、一遍遍历


 

https://leetcode.com/problems/first-unique-character-in-a-string/

返回给定字符串第一个只出现一次的字符的下标,不存在则返回-1

 

一、问题描述

测试用例:

s = "leetcode"
return 0.

s = "loveleetcode",
return 2.

字符串只有小写字母。

 

二、代码实现

1、两遍遍历

class Solution {

    public int firstUniqChar3(String s) {
        int[] pos = new int[26]; //记录26个字母第一次出现的位置,如果没有出现或者已经重复则为-1
        Arrays.fill(pos, -1);
        Set<Character> occurence = new HashSet<>(); 
        
        //O(n)
        for(int i=0; i<s.length(); i++) {
            char ch = s.charAt(i);
            if(!occurence.contains(ch)) {
                occurence.add(ch);
                pos[ch - 'a'] = i;
            } else {
                pos[ch - 'a'] = -1;
            }
        }
        
        //O(m)  m是字符的范围,这里是26
        int minIndex = Integer.MAX_VALUE;
        for (int i = 0; i < 26; i++) {
            if (pos[i] != -1) {
                minIndex = Math.min(minIndex, pos[i]);
            }
        }
        
        return minIndex == Integer.MAX_VALUE ? -1 : minIndex;
    }    

    public int firstUniqChar2(String s) {
        //1、得到每个字母的出现次数信息
        LinkedHashMap<Character, Integer> map = new LinkedHashMap<>();
        for (int i=0; i<s.length(); i++) {
            char ch = s.charAt(i);
            map.put(ch, map.getOrDefault(ch, 0) + 1);
        }
        
        //2、获取第一个只出现一次的字母的下标
        for (Character key : map.keySet()) {
            if (map.get(key).equals(1)) {
                return s.indexOf(key);
            }
        }
        
        return -1;  //所有字符都重复,不存在只出现一次的字符
    }

    public int firstUniqChar1(String s) {
        if (s == null) {
            return -1;
        }
        
        //1、得到每个字母的出现次数信息
        int[] freq = new int[26];  //题目告诉我们只有小写字母
        for(int i=0; i<s.length(); i++) {
            freq[s.charAt(i) - 'a']++;    
        }
        
        //2、获取第一个只出现一次的字母的下标
        for(int i=0; i<s.length(); i++) {
            if(freq[s.charAt(i) - 'a'] == 1) {
                return i;
            }
        }
            
        return -1;
    }
}

2、一遍遍历

当字符串长度非常长时,并且第一个只出现一次的字符在很靠后的位置才出现时,两遍遍历的时间效果其实不是很好,这里用一遍遍历来改进一下。

class Solution {
    
    //使用数组实现
    public int firstUniqChar5(String s) {
        if (s==null || s.length()==0) {
            return -1;
        }
        if (s.length() == 1) {
            return 0;
        }
        
        int len = s.length();
        char[] arr = s.toCharArray();
        int[] count = new int[256];     //计算字母出现次数,实际使用字符是Unicode而不是ASCII,所以256是不够的,最好还是使用HashMap
        int slow = 0, fast = 1;         //快指针用于遍历字符串,慢指针指向可能只出现一次的字母
        count[arr[slow]]++;
        while (fast < len) {
            count[arr[fast]]++; //添加已经访问的标志
            
            //如果慢指针不是独立的字母,则将其移动到下一个可能只出现一次的字母
            while (slow<len && count[arr[slow]]>1) {
                slow++; 
            }
            
            //没有只出现一次的字母
            if (slow >= len) {
                return -1; 
            }
            //还没有被快指针访问到:这一段代码有些时候可以让快指针减少剩余待访问的元素
            // if (count[arr[slow]] == 0) { 
            //     fast = slow; //重新定位快指针
            //     count[arr[slow]]++; 
            // }
            
            fast++; //快指针指向下一个待访问的字母
        }
        
        return slow;
    }
      
    //使用集合实现
    //一次遍历,当字符串很长并且字符个数很多(不止26个)的时候可以执行得很快,这里慢了些
    //此外,这种方式在字符以数据流的方式到来的时候非常好
    public int firstUniqChar4(String s) {
        Map<Character, Integer> map = new LinkedHashMap<>();    //存放可能只出现一次的字母
        Set<Character> set = new HashSet<>();                   //存放已经遍历过的所有字母
        for (int i = 0; i < s.length(); i++) {
            if (set.contains(s.charAt(i))) {    //如果已经访问过,则从map中删除它
                //if (map.get(s.charAt(i)) != null) {
                    map.remove(s.charAt(i));
                //}
            } else {
                map.put(s.charAt(i), i);
                set.add(s.charAt(i));           //添加访问标记
            }
        }
        
        //map.entrySet()返回一个Map.Entry<K, V>对象的集合————Set<Map.Entry<K, V>>
        //遍历一个集合需要Iterator,得到下一个Map.Entry对象则需要迭代器的next()方法
        //得到Map.Entry对象之后,则可以调用其getKey()方法、getValue()获得键、值信息
        return map.size() == 0 ? -1 : map.entrySet().iterator().next().getValue();
    }

}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值