字符串中的第一个唯一字符

题目地址:https://leetcode-cn.com/problems/first-unique-character-in-a-string/

给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。

示例:

s = "leetcode"
返回 0 s = "loveleetcode"
返回 2

提示:你可以假定该字符串只包含小写字母。

解法一:双指针

双指针比较,每指定一个值都要全文扫描有无重复

public int firstUniqChar(String s) {
char[] arr = s.toCharArray();
int n = arr.length;
for(int i = 0; i < n; i++){
boolean flag = true;
//遍历每个值与当前值比较,有重复改false
for(int j = 0; j < n; j++){
if(i != j && arr[i] == arr[j]){
flag = false;
}
}
//遍历完没有和当前值相同的
if(flag){
return i;
}
}
return -1;
}

然后LeetCode的测试用例字符串也是真的长(只截取了部分下面还可以翻页),所以在n^2的情况下超时。

解法二:细节优化

上面的解法是有可优化的点的。我们去查找第一个只出现一次的,那么一个值找到相同的后我们就不必要再往后了遍历因为不需要看它有几个相同的,它不满足就应该看下一个值也就是应该加上break。

for(int j = 0; j < n; j++){
if(i != j && arr[i] == arr[j]){
flag = false;
break;
}
}

这一点是影响还是比较大的,第二点就是我们去判断遍历完了没有重复也就是找到了可以直接返回的处理我们除了定义flag在循环体标记,到循环去判断处理。其实我们去表达循环完后的处理也可以在循环体里面,也就是循环到最后了仍然不满足相等。

for(int j = 0; j < n; j++){
if(i != j && arr[i] == arr[j]){
break;
}else if(j == n-1){
return i;
}
}

这样和前面的就是同一个意思,并且少定义一个变量flag,并且最后我们的代码不会超时

public int firstUniqChar(String s) {
char[] arr = s.toCharArray();
int n = arr.length;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(i != j && arr[i] == arr[j]){
break;
}else if(j == n - 1){
return i;
}
}
}
return -1;
}

时间O(n^2),空间O(1)

解法三:Hash表

那么使用hash表存信息,那么就不用重复遍历了

public int firstUniqChar(String s) {
char[] arr = s.toCharArray();
int n = arr.length;
HashMap<Character, Integer> count = new HashMap<Character, Integer>();
//遍历存下所有信息
for (int i = 0; i < n; i++) {
char c = arr[i];
count.put(c, count.getOrDefault(c, 0) + 1);
}
//遍历查看key值为1的
for (int i = 0; i < n; i++) {
if (count.get(arr[i]) == 1)
return i;
}
return -1;
}

时间O(n),空间O(n),虽然如此但效率反而比双指针要差,这主要是它的这些方法。

解法四:数组

用Hash表能存,那用数组也应该是可以的,一样的key位索引值判断是不是1。同一个字母就是同一个地方对应值就加一。统计完之后遍历字符串按字符串的顺序去数组查率先等于1的就返回

public int firstUniqChar(String s) {
int[] chars = new int[26];
char[] arr = s.toCharArray();
for (char c : arr) {
chars[c - 'a'] += 1;
}
for (int i = 0; i < arr.length; ++i) {
if (chars[arr[i] - 'a'] == 1) {
return i;
}
}
return -1;
}

和解法二同样的一个思路选取数组这种数据结构,效率就直接上去了。

解法五:细节优化

上述数组解法在效率上仍然是有可优化点,因为我们去比较两个容器的时候谁短我们就遍历谁。更何况这里只需要拿值到另一个容器参考只需要一次遍历,那我们更应该遍历短的。那么当字符串长度小于26和上面一样遍历字符串到数组去记录,最后再遍历数组看结果,如果字符串长于26那么我们就遍历a-z这26个字母

int result = -1;
for (char i = 'a'; i <= 'z'; ++i) {
int begin = s.indexOf(i);
int end = s.lastIndexOf(i)
// 在字符串中存在该字符并且唯一
if (begin != -1 && begin == end {
// 不仅要唯一,且索引还要小。遍历完成拿到字符串最前的唯一
result = (result == -1 || result > begin) ? begin : result;
}
}

那么在字符串长度很大的情况下也只需要完整遍历26次就能找到首个唯一,完整代码如下:

public int firstUniqChar(String s) {
// 字符串长度不超过26
if (s.length() <= 26) {
int[] chars = new int[26];
char[] arr = s.toCharArray();
for (char c : arr) {
chars[c - 'a'] += 1;
}
for (int i = 0; i < arr.length; ++i) {
if (chars[arr[i] - 'a'] == 1) {
return i;
}
}
}
//只遍历26个字母
int result = -1;
for (char i = 'a'; i <= 'z'; ++i) {
int begin = s.indexOf(i);
int end = s.lastIndexOf(i);
if (begin != -1 && begin == end) {
result = (result == -1 || result > begin) ? begin : result;
}
}
return result;
}

总结

题目难度呢属于简单,双指针、hash表这样成对的解法就出来了,主要是通过此题去回顾一些注意点比如双循环的优化,循环中字符串的方法频繁的进出也是有一定的浪费,可以先拿数组出来操作会好一点。适合体会解题迭代的一个流程。

LeetCode初级算法之字符串:387 字符串中的第一个唯一字符的更多相关文章

  1. C#LeetCode刷题之#387-字符串中的第一个唯一字符(First Unique Character in a String)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3939 访问. 给定一个字符串,找到它的第一个不重复的字符,并返回 ...

  2. 前端与算法 leetcode 387. 字符串中的第一个唯一字符

    目录 # 前端与算法 leetcode 387. 字符串中的第一个唯一字符 题目描述 概要 提示 解析 解法一:双循环 解法二:Set法单循环 算法 传入测试用例的运行结果 执行结果 GitHub仓库 ...

  3. LeetCode初级算法--字符串02:字符串中的第一个唯一字符

    LeetCode初级算法--字符串02:字符串中的第一个唯一字符 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog. ...

  4. LeetCode初级算法的Python实现--字符串

    LeetCode初级算法的Python实现--字符串 # 反转字符串 def reverseString(s): return s[::-1] # 颠倒数字 def reverse(x): if x ...

  5. Java实现 LeetCode 387 字符串中的第一个唯一字符

    387. 字符串中的第一个唯一字符 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引.如果不存在,则返回 -1. 案例: s = "leetcode" 返回 0. s = ...

  6. Leecode刷题之旅-C语言/python-387 字符串中的第一个唯一字符

    /* * @lc app=leetcode.cn id=387 lang=c * * [387] 字符串中的第一个唯一字符 * * https://leetcode-cn.com/problems/f ...

  7. leecode刷题(13) -- 字符串中的第一个唯一字符

    leecode刷题(13) -- 字符串中的第一个唯一字符 字符串中的第一个唯一字符 描述: 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引.如果不存在,则返回 -1. 案例: s = & ...

  8. LeetCode 387: 字符串中的第一个唯一字符 First Unique Character in a String

    题目: 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引.如果不存在,则返回 -1. Given a string, find the first non-repeating charact ...

  9. LeetCode 387. First Unique Character in a String (字符串中的第一个唯一字符)

    题目标签:String, HashMap 题目给了我们一个 string,让我们找出 第一个 唯一的 char. 设立一个 hashmap,把 char 当作 key,char 的index 当作va ...

随机推荐

  1. flex-shrink值的计算

    flex-shrink为弹性盒模型中,当弹性项不断行,并且所有弹性项的宽度只和大于弹性盒模型的可分配空间时,弹性项的收缩程度. 找到英文资料对flex-shrink的定义描述: flex-shrink ...

  2. Android 架构组件-Lifecycle、LiveData、ViewModel

    Lifecycle Lifecycle组件包括LifecycleOwner.LifecleObserver,能方便监听Activity或者Fragment的生命周期. 步骤: 1.实现Lifecycl ...

  3. ESP8266 鼓捣记 - 从零制作一个温湿度计

    一.前言 经过上一篇文章 <ESP8266 鼓捣记 - 入门(环境搭建) >搭建好环境后,肯定不会满足于 Hello World ,想快速做一个实际有用的东西出来,我认为温湿度计就非常合适 ...

  4. HttpClient4.3 连接池参数配置及源码解读

    目前所在公司使用HttpClient 4.3.3版本发送Rest请求,调用接口.最近出现了调用查询接口服务慢的生产问题,在排查整个调用链可能存在的问题时(从客户端发起Http请求->ESB-&g ...

  5. 各种有趣vbs,bat脚本

    短信轰炸.vbs Dim btn,ie Set ie = WScript.CreateObject("InternetExplorer.Application") ie.Visib ...

  6. 不会吧,你连Java 多线程线程安全都还没搞明白,难怪你面试总不过

    什么是线程安全? 当一个线程在同一时刻共享同一个全局变量或静态变量时,可能会受到其他线程的干扰,导致数据有问题,这种现象就叫线程安全问题. 为什么有线程安全问题? 当多个线程同时共享,同一个全局变量或 ...

  7. MySQL如何实现万亿级数据存储?

    前言 业界对系统的高可用有着基本的要求,简单的说,这些要求可以总结为如下所示. 系统架构中不存在单点问题. 可以最大限度的保障服务的可用性. 一般情况下系统的高可用可以用几个9来评估.所谓的几个9就是 ...

  8. 巧妙运用Camtasia制作爱豆的动感影集

    对于追星族来说,收集爱豆的图片.视频是日常必做的事情,而对于进阶型的追星族来说,为爱豆自制各种精美的视频.影集等,会让自己追星之路显得更为充实. 我们可以借助Camtasia教程录制软件为爱豆制作各种 ...

  9. 什么是NTFS文件格式

    说到磁盘格式,想必大家对于NTFS格式并不陌生.我们使用的u盘等硬盘设备很多都应用了此格式.NTFS文件格式究竟是什么?它都有哪些特点?今天,小编将利用这篇文章为大家进行介绍. 一.什么是NTFS文件 ...

  10. 解决Tuxera NTFS for Mac软件安装问题

    在听到小凡的电话说"Tuxera NTFS for Mac软件安装失败,怎么办"的时候,小编心里真像有一万头草泥马在奔腾--苹果软件还能安装失败!? 挥手把一万头草泥马赶走,脑补着 ...