Go语言中的Map陷阱

Go语言中的Map陷阱

今天拿到了一个Go语言的题目,计算字符串中不重复最长子串长度,原题是:
https://leetcode.com/problems/longest-substring-without-repeating-characters/

Given a string, find the length of the longest substring without repeating characters.
Example 1:
Input: "abcabcbb"
Output: 3 
Explanation: The answer is "abc", with the length of 3. 
Example 2:
Input: "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.
Example 3:
Input: "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3. 
Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

做为初学者,我自己经过不懈努力,写了一个不是很好的算法,再次过程中,我对map的遍历有了进一步的认识,之前总认为遍历结果顺序不同,对程序没有什么影响,直到遇到这个问题。

package main

import (
   "fmt"
)
/*
算法描述:
s:目标字符串
maxLength:记录最长子串长度,最终要返回给主程序的值
counter:计数器,记录字符在字符串s中的位置
countMap:记录字符编码和字符位置,因为range字符串值为int32对应是字符的编码
执行过程:遍历s,字符编码给v,判断countMap中是否有v,如果没有,将v最为key
couter做为value存入countMap中;如果有,将不大于countMap[v]的值删除,再记录当前值,
计算countMap长度,如果不小与maxLength,maxLength等于countMap长度,counter加1
进入下一次循环。
*/
func lengthOfLongestSubstring(s string) int {
   maxLength := 0
   counter := 1
   countMap := make(map[int32]int)
   for _, v := range(s){
      if countMap[v] != 0{
         for key, value := range(countMap){
            if value <= countMap[v]{
               delete(countMap, key)
            }
         }
      }
      countMap[v] = counter
      if len(countMap) >= maxLength{
         maxLength = len(countMap)
      }
      counter += 1
   }
   return maxLength
}

func main() {
   var str string = "jbpnbwwd"
   length := lengthOfLongestSubstring(str)
   fmt.Println(length)
}

问题

逻辑上是没有什么问题,但是运行结果不理想,正确结果应该是4,但是有时候会出现5和6。
我反复推敲调试才发现多了个等于号,你们直到是哪个等于号吗,不妨遮住下面的内容思考一下。
在这里插入图片描述
Are you OK?(雷总名言借用一下)
就是这里。
在这里插入图片描述

解释:

因为map遍历是随即的,所以你不知道哪个值会被先拿出来。
如果按照顺序的话,肯定是没有问题的,先拿出小于的,小于的通通删掉,再是等于,等于的也删掉,后面再比较的时候countMap就不在countMap中了是默认值0了,也就不用比较了。
随即的话,就不一样了,如果等于提前被拿出来,删除后,再比较的话,countMap[v]=0了,即使countMap里面还有符合条件的也拿不出来了。这是算法上的失误。

解决办法:

1、将countMap[v]先赋值给一个变量,再用变量去比较;

func lengthOfLongestSubstring(s string) int {
   maxLength := 0
   counter := 1
   countMap := make(map[int32]int)
   for _, v := range(s){
      if countMap[v] != 0{
         tmp := countMap[v]
         for key, value := range(countMap){
            if value <= tmp{
               delete(countMap, key)
            }
         }
      }
      countMap[v] = counter
      if len(countMap) >= maxLength{
         maxLength = len(countMap)
      }
      counter += 1
   }
   return maxLength
}

2、将等于号去掉就可以了,就不多解释了。

func lengthOfLongestSubstring(s string) int {
   maxLength := 0
   counter := 1
   countMap := make(map[int32]int)
   for _, v := range(s){
      if countMap[v] != 0{
         for key, value := range(countMap){
            if value < countMap[v]{
               delete(countMap, key)
            }
         }
      }
      countMap[v] = counter
      if len(countMap) >= maxLength{
         maxLength = len(countMap)
      }
      counter += 1
   }
   return maxLength
}

在这里插入图片描述

总结:

我的算法执行效率比较低,有两层循环,但是基本满足要求。下面看一下大神的算法,不做解释,膜拜就行了。

func lengthOfLongestSubstring(s string) int {
	lastOccurred := make(map[rune]int)
	start := 0
	maxLength := 0
	for i, ch := range []rune(s) {
		if lastI, ok := lastOccurred[ch]; ok && lastI >= start {
			start = lastI + 1
		}
		if i-start+1 > maxLength {
			maxLength = i - start + 1
		}
		lastOccurred[ch] = i
	}
	return maxLength
}

应该没有什么版权的问题,除了最后用了点大神的代码,别的都是本人手码的,如果有版权、语法、错别字等问题,欢迎留言或E-mail(vnths@qq.com)给我,我看到后,会及时处理。
欢迎努力中的同学来访,MAIL ME! I’m Liby. Thank you!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值