博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Golang面试考题记录 ━━ 字符串中的第一个唯一字符 ,拓展:ASCII和strings字符串查找的用法
阅读量:4115 次
发布时间:2019-05-25

本文共 5275 字,大约阅读时间需要 17 分钟。

===问:

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

案例:

s = “leetcode”

返回 0.

s = “loveleetcode”

返回 2.

注意事项:您可以假定该字符串只包含小写字母。

===答:

方法一:

执行用时 :12 ms/16 ms, 击败了66.08%/62.57% 的用户
内存消耗 :5.8 MB, 击败了14.63%的用户

嵌套循环,依次遍历,在几个方法中并不是速度最慢的。

func firstUniqChar1(s string) int {
l := len(s) - 1 for i := 0; i <= l; i++ {
r := 1 for j := 0; j <= l; j++ {
if i != j && s[i] == s[j] {
r = 0 break } } if r == 1 {
return i } } return -1}

方法二:

执行用时 :144 ms, 击败了5.19% 的用户
内存消耗 :5.8 MB, 击败了14.63%的用户

利用go的方法strings.Count(),遍历一次,计算每个字母出现的个数,

遍历过程中一旦某个字母只有唯一一个,立刻停止循环返回其索引号

func firstUniqChar2(s string) int {
for i := range s {
c := strings.Count(s, string(s[i])) if c == 1 {
return i } } return -1}

方法三:

执行用时 :20 ms, 击败了60.58% 的用户
内存消耗 :5.7 MB, 击败了95.12%的用户

利用go的方法strings.LastIndexByte(),遍历当前元素与字符串中最后一个相同元素的索引号是否一致

如果相同,且是第一次检测,则说明是唯一元素,返回索引号
本方法需创建一个map,却消耗内存最低

func firstUniqChar3(s string) int {
v := make(map[int]int, 26) for i := range s {
c := strings.LastIndexByte(s, s[i]) if c == i && v[c] != 1 {
return i } v[c] = 1 } return -1}

方法四:

执行用时 :40 ms/36 ms, 击败了46.73%/54.62% 的用户
内存消耗 :5.7 MB, 击败了95.12%的用户

第一次遍历字符串中每个元素,用map记录元素出现的次数,没有使用go自带的strings.Count()方法

第二次遍历字符串每个元素,当发现该元素在map中的值为1则意味着该元素只出现一次,立刻返回索引号
本方法与方法二思路相同,但内存占用和执行效率提升N倍
本方法统计了所有的元素的出现次数,而方法三只获得最后一个元素的位置,因此本方法执行效率慢了一倍。

func firstUniqChar4(s string) int {
r := make(map[rune]int, 26) for _, v := range s {
r[v]++ } for i, v := range s {
if r[v] == 1 {
return i } } return -1}

方法五:

执行用时 :8 ms,击败了88.08% 的用户
内存消耗 :5.8 MB, 击败了14.63%的用户

取巧的方法,因为题目里说了都是小写字母,否则数组r的长度无法确定

又因为都是小写字母,因此v-97可得到0~26,正好用于数组索引
第一次遍历字符串计算元素出现次数
第二次遍历字符串遇到出现一次的元素返回其索引
方法二、方法四、方法五思路一致,都是统计元素出现次数,但方法五执行效率最高,方法四内存占用最少
推荐方法四,可以用于非小写字母的字符串统计,而方法五适用面太小

func firstUniqChar5(s string) int {
// ASCII数值:a-z:97-122,A-Z:65-90,0-9:48-57。 r := [26]int{
} for _, v := range s {
r[v-97]++ } for i, v := range s {
if r[v-97] == 1 {
return i } } return -1}

方法六:

执行用时 :12 ms, 击败了66.15% 的用户
内存消耗 :6 MB, 击败了7.32%的用户

本方法是将方法三中的map改为了切片,但效果不明显,执行效率增加,但是内存占用也增加了~~

这是因为当s为类似"aabbccddefggadfdaefdfaefaggjfghtrgregtrujuoiolokuhtrgrfe",在创建切片的时候就会创建一个同样长度的切片,否则如果使用append(),每次更改内存地址,相比速度会减慢,没有必要了。

func firstUniqChar6(s string) int {
v := make([]int, len(s)) for i := range s {
c := strings.LastIndexByte(s, s[i]) if c == i && v[c] != 1 {
return i } v[c] = 1 } return -1}

方法七:

执行用时 :12 ms, 击败了66.15% 的用户
内存消耗 :5.8 MB, 击败了14.63%的用户

想让方法六中的切片固定为一个数组,看看会不会有所提升于是创建了方法七

果然内存消耗是降低了,但执行效率并没什么改变
方法三中的map可变长度又能自定义键名,反倒省了很多事

func firstUniqChar7(s string) int {
r := [26]int{
} for i, v := range s {
c := strings.LastIndexByte(s, s[i]) if c == i && r[v-97] != 1 {
return i } r[v-97] = 1 } return -1}

===拓展:

这里需要拓展的知识点有两个:

字母和数字的ASCII值:

ASCII表格我们常用的记住下面的就行了,实际使用的时候结合实际情况,发挥创意:

0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(响铃)等;通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;ASCII值为8、9、10

和13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响 [1] 。

32~126(共95个)是字符(32是空格),其中48~57为0到9十个阿拉伯数字。

65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。

go自带的strings统计及字符位置的几个函数

参考:

  1. func Contains(s, substr string) bool这个函数是查找某个字符是否在这个字符串中存在,存在返回true
import ( "fmt" "strings")func main() {
fmt.Println(strings.Contains("widuu", "wi")) //true fmt.Println(strings.Contains("wi", "widuu")) //false}
  1. func ContainsAny(s, chars string) bool这个是查询字符串中是否包含多个字符
import ( "fmt" "strings")func main() {
fmt.Println(strings.ContainsAny("widuu", "w&d")) //true}
  1. func ContainsRune(s string, r rune) bool,这里边当然是字符串中是否包含rune类型,其中rune类型是utf8.RUneCountString可以完整表示全部Unicode字符的类型
import ( "fmt" "strings")func main() {
fmt.Println(strings.ContainsRune("widuu", rune('w'))) //true fmt.Println(strings.ContainsRune("widuu", 20)) //fasle}
  1. func Count(s, sep string) int这个的作用就是输出,在一段字符串中有多少匹配到的字符
import ( "fmt" "strings")func main() {
fmt.Println(strings.Count("widuu", "uu")) //1 fmt.Println(strings.Count("widuu", "u")) //2}
  1. func Index(s, sep string) int 这个函数是查找字符串,然后返回当前的位置,输入的都是string类型,然后int的位置信息
import ( "fmt" "strings")func main() {
fmt.Println(strings.Index("widuu", "i")) //1 fmt.Println(strings.Index("widuu", "u")) //3}
  1. func IndexAny(s, chars string) int 这个函数是一样的查找,字符串第一次出现的位置,如果不存在就返回-1
import ( "fmt" "strings")func main() {
fmt.Println(strings.IndexAny("widuu", "u")) //3}
  1. func IndexByte(s string, c byte) int,这个函数功能还是查找第一次粗线的位置,只不过这次C是byte类型的,查找到返回位置,找不到返回-1
import ( "fmt" "strings")func main() {
fmt.Println(strings.IndexByte("hello xiaowei", 'x')) //6}
  1. func IndexRune(s string, r rune) int,还是查找位置,只不过这次是rune类型的
import ( "fmt" "strings")func main() {
fmt.Println(strings.IndexRune("widuu", rune('w'))) //0}
  1. func IndexFunc(s string, f func(rune) bool) int这个函数大家一看就知道了,是通过类型的转换来用函数查找位置,我们来代码看下哈
import ( "fmt" "strings")func main() {
fmt.Println(strings.IndexFunc("nihaoma", split)) //3}func split(r rune) bool {
if r == 'a' {
return true } return false}
  1. func LastIndex(s, sep string) int 看到这个大家可能也明白了查找的是最后出现的位置,正好跟index相反
import ( "fmt" "strings")func main() {
fmt.Println(strings.LastIndex("widuu", "u")) // 4}
  1. func LastIndexAny(s, chars string) int这个跟indexAny正好相反,也是查找最后一个
import ( "fmt" "strings")func main() {
fmt.Println(strings.LastIndexAny("widuu", "u")) // 4}

转载地址:http://ctkpi.baihongyu.com/

你可能感兴趣的文章
Linux基础系列-定时器与时间管理
查看>>
Linux基础系列-可执行程序的产生过程
查看>>
Linux基础系列-Kernel 初始化宏
查看>>
Linux子系统系列-I2C
查看>>
<iOS>关于自定义description的一点用法
查看>>
Unix 命令,常用到的
查看>>
DLL中建立进程共享数据段需要注意的语法问题
查看>>
服务器端技术----Http请求的处理过程
查看>>
C语言-预处理指令2-条件编译
查看>>
C语言-预处理指令3-文件包含
查看>>
C语言-变量类型
查看>>
C语言-static和extern关键字1-对函数的作用
查看>>
C 语言-static和extern关键字2-对变量的作用
查看>>
【JavaScript 教程】浏览器—History 对象
查看>>
还不会正则表达式?看这篇!
查看>>
100道+ JavaScript 面试题,助你查漏补缺
查看>>
JavaScript深入理解之闭包
查看>>
如何禁止JavaScript对象重写?
查看>>
前端10大经典算法
查看>>
object-fit和object-position_定义图片和视频元素在容器内如何显示的css属性
查看>>