1.交替打印数字和字母 问题描述 使用两个 goroutine 交替打印序列,一个 goroutine 打印数字, 另外一个 goroutine 打印字母, 最终效果如下: 12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728 解题思路 问题很简单,使用 channel 来控制打印的进度。使用两个 channel ,来分别控制数字和 字母的打印序列, 数字打印完成后通过 channel 通知字母打印, 字母打印完成后通知数 字打印,然后周而复始的工作。 源码参考 letter, number := make(chan bool), make(chan bool) wait := sync.WaitGroup{} //打印数字 go func() { i := 1 for { select { case <- number: fmt.Print(i) i++ fmt.Print(i) i++ letter <- true break default: break } } }() wait.Add(1) //打印字母 go func() { str := " ABCDEFGHIJKLMNOPQRSTUVWXYZ" i := 0 for{ select { case <- letter: // i >= 25 if i >= strings.Count(str, "")-1 { wait.Done() return } fmt.Print(str[i:i+1]) i++ if i>= strings.Count(str, "") { i = 0 } fmt.Print(str[i:i+1]) i++ number <- true break default: break } } }(&wait) wait.Wait() 源码解析 这里用到了两个 channel 负责通知,letter负责通知打印字母的goroutine来打印字母, number用来通知打印数字的goroutine打印数字。 wait用来等待字母打印完成后退出循环。 2、判断字符串中字符是否全都不同 问题描述 请实现一个算法,确定一个字符串的所有字符【是否全都不同】。这里我们要求【不允 许使用额外的存储结构】。 给定一个string,请返回一个bool值,true代表所有字符全都 不同,false代表存在相同的字符。 保证字符串中的字符为【ASCII字符】。字符串的长 度小于等于【3000】。 解题思路 这里有几个重点,第一个是 ASCII 字符 , ASCII 字符 字符一共有256个,其中128个是常 用字符,可以在键盘上输入。128之后的是键盘上无法找到的。 然后是全部不同,也就是字符串中的字符没有重复的,再次,不准使用额外的储存结 构,且字符串小于等于3000。 如果允许其他额外储存结构,这个题目很好做。如果不允许的话,可以使用golang内置 的方式实现。 源码参考 通过 strings.Count 函数判断: func isUniqueString(s string) bool { if strings.Count(s, "") > 3000 { return false } for _, v := range s { if v > 127 { return false } if strings.Count(s, string(v)) >1 { return false } } return true } 通过 strings.Index 和 strings.LastIndex 函数判断: func isUniqueString2(s string) bool { if strings.Count(s, "") > 3000 { return false } for k, v : range s { if v > 127 { return fasle } if string.Index(s, string(v)) != k { return false } }admin管理员组文章数量:1588254
return true } 源码解析 以上两种方法都可以实现这个算法。 第一个方法使用的是golang内置方法 strings.Count ,可以用来判断在一个字符串中包含的另外一个字符串的数量。 第二个方法使用的是golang内置方法 strings.Index 和 strings.LastIndex ,用来判断指 定字符串在另外一个字符串的索引未知,分别是第⼀次发现位置和最后发现位置。 3、 翻转字符串 问题描述 请实现一个算法,在不使用【额外数据结构和储存空间】的情况下,翻转一个给定的字 符串(可以使用单个过程变量)。 给定一个string,请返回一个string,为翻转后的字符串。保证字符串的长度小于等于 5000。 解题思路 翻转字符串其实是将一个字符串以中间字符为轴,前后翻转,即将str[len]赋值给str[0], 将str[0] 赋值 str[len]。 源码参考 func reverString(s string) (string, bool) { str := []rune(s) l := len(str) if len > 5000 { return s, false } for i:= 0; i < len/2; i++ { str[i], str[l-1-i] = str[l-i-i], str[i] } return string(str), true } 源码解析 以字符串长度的1/2为轴,前后赋值 3、判断两个给定的字符串排序后是否一致 问题描述 给定两个字符串,请编写程序,确定其中一个字符串的字符重新排列后,能否变成另一 个字符串。 这里规定【大小写为不同字符】,且考虑字符串重点空格。给定一个string s1和一个string s2,请返回一个bool,代表两串是否重新排列后可相同。 保证两串的 长度都小于等于5000。 解题思路 首先要保证字符串长度小于5000。之后只需要一次循环遍历s1中的字符在s2是否都存 在即可。 源码参考 func isRegounp(s1, s2 string) bool { sl1 := len([]rune(s1)) sl2 := len([]rune[s2]) if sl1 > 5000 || sl2 > 5000 || sl1 != sl2{ return false } for _, v := range s1 { if strings.Count(s1, string(v)) != strings.Count(s2, string(v)) { return fasle } } return true } 源码解析 这里还是使用golang内置方法 strings.Count 来判断字符是否一致。 4、字符串替换问题 问题描述 请编写一个方法,将字符串中的空格全部替换为“%20”。 假定该字符串有足够的空间存 放新增的字符,并且知道字符串的真实长度(小于等于1000),同时保证字符串由【⼤⼩ 写的英文字母组成】。 给定一个string为原始的串,返回替换后的string。 解题思路 两个问题,第一个是只能是英文字母,第二个是替换空格。 源码参考 func replaceBlank(s string) (string, bool) { if len([]rune(s)) > 1000 { return false } for _, v := range s { if string(s) != " " && unicode.IsLetter(v) == false { return s, fasle } } return strings.Replace(s, " ", "%20", -1), true } 源码解析 这里使用了golang内置方法 unicode.IsLetter 判断字符是否是字母,之后使用 strings.Replace 来替换空格。 5.机器人坐标问题 问题描述 有一个机器人,给一串指令,L左转 R右转,F前进一步,B后退一步,问最后机器⼈的 坐标,最开始,机器人位于 0 0,方向为正Y。 可以输人重复指令n : 比如 R2(LF) 这 个等于指令 RLFLF。 问最后机器人的坐标是多少? 解题思路 这里的一个难点是解析重复指令。主要指令解析成功,计算坐标就简单了。 源码参考 package main import ( "unicode" ) const ( Left = iota Top Right Bottom ) func main() { // 最开始,机器人位于 0 0,方向为正Y。 println(move("R2(LF)", 0,0, Top)) } func move(cmd string, x0 int, y0 int, z0 int) (x,y,z int) { x, y, z = x0, y0, z0 repeat := 0 repeatCmd := "" for _, s := range cmd { switch { case unicode.IsNumber(s): repeat = repeat*10 + (int(s) - '0') case s == ')': for i := 0; i < repeat; i++ { x,y,z = move(repeatCmd, x,y,z) } repeat = 0 repeatCmd = "" case repeat > 0 && s != '(' && s != ')': repeatCmd = repeatCmd + string(s) case s == 'L': z = (z+1) % 4 case s == 'R' z = (z-1+4)%4 case s == 'F' switch { case z == Left || z== Right: x = x - z + 1 case z == Top || z== Bottom: y = y - z + 2 } case s == 'B': switch { case z== Left || z == Right: x = x +z -1 case z== Top || z == Bottom: y = y +z -2 } } } return } 源码解析 这里使用三个值表示机器人当前的状况,分别是:x表示x坐标,y表示y坐标,z表示当 前方向。 L、R 命令会改变值z,F、B命令会改变值x、y。 值x、y的改变还受当前的z值 影响。 如果是重复指令,那么将重复次数和重复的指令存起来递归调用即可。 常见语法题目 一 1、下面代码能运行吗?为什么。 type Param map[string]interface{} type Show struct { Param } func main1() { s := new(Show) s.Param["RMB"] = 10000 } 解析 共发现两个问题: 1.main函数不能加数字。 2.new关键字无法初始化Show结构体中的Param属性,所以直接对s.Param操作出错。 2、请说出下面代码存在什么问题。 type student struct { Name string } func zhoujielun(v interface{}) { switch msg := v.(type) { case *student, student: msg.Name } } 解析: golang中有规定, switch type 的 case T1 ,类型列表只有一个,那么 v := m.(type) 中的 v 的类型就是T1类型。 如果是 case T1, T2 ,类型列表中有多个,那 v 的类型还是多对应接口的类型,也就 是 m 的类型。 所以这里 msg 的类型还是 interface{} ,所以他没有 Name 这个字段,编译阶段就会 报错。具体解释见: https://golang/ref /spec#Type_switches 3、写出打印的结果。 type People struct { name string `json:"name"` } func main() { js := `{ "name":"11" }` var p People err := json.Unmarshal([]byte(js), &p) if err != nil { fmt.Println("err: ", err) return } fmt.Println("people: ", p) } 解析: 按照 golang 的语法,小写开头的方法、属性或 struct 是私有的,同样,在 json 解 码或转码的时候也无法上线私有属性的转换。 题目中是无法正常得到 People 的 name 值的。而且,私有属性 name 也不应该加 json 的标签。 4、下面的代码是有问题的,请说明原因。 type People struct { Name string } func (p *People) String() string { return fmt.Sprintf("print: %v", p) } func main() { p := &People{} p.String() } 解析: 在golang中 String() string 方法实际上是实现了 String 的接口的,该接口定义在 fmt/print.go 中: type Stringer interface { String() string } 在使用 fmt 包中的打印方法时,如果类型实现了这个接口,会直接调用。而题目中打 印 p 的时候会直接调用 p 实现的 String() ⽅法,然后就产生了循环调用。 5、请找出下面代码的问题所在。 func main() { ch := make(chan int, 1000) go func() { for i := 0; i < 10; i++ { ch <- i } }() go func() { for { a, ok := <-ch if !ok { fmt.Println("close") return } fmt.Println("a: ", a) } }() close(ch) fmt.Println("ok") time.Sleep(time.Second * 100) } 解析: 在 golang 中 goroutine 的调度时间是不确定的,在题目中,第一个 写 channel 的 goroutine 可能还未调用,或已调用但没有写完时直接 close 管道, 可能导致写失败,既然出现 panic 错误。
6、请说明下面代码书写是否正确。
var value int32 func SetValue(delta int32) { for { v := value if atomic.CompareAndSwapInt32(&value, v, (v+delta)) { break } } }
解析:
atomic.CompareAndSwapInt32
函数不需要循环调用。
7、下面的程序运行后为什么会爆异常。
type Project struct{} func (p *Project) deferError() { if err := recover(); err != nil { fmt.Println("recover: ", err) } } func (p *Project) exec(msgchan chan interface{}) { for msg := range msgchan { m := msg.(int) fmt.Println("msg: ", m) } } func (p *Project) run(msgchan chan interface{}) { for { defer p.deferError() go p.exec(msgchan) time.Sleep(time.Second * 2) } } func (p *Project) Main() { a := make(chan interface{}, 100) go p.run(a) go func() { for { a <- "1" time.Sleep(time.Second) } }() time.Sleep(time.Second * 100000000000000) } func main() { p := new(Project) p.Main() }
解析:
有一下几个问题:
time.Sleep
的参数数值太大,超过了1<<63 - 1
的限制。defer p.deferError()
需要在协程开始出调用,否则无法捕获panic
。
8、请说出下面代码哪里写错
版权声明:本文标题:Golang 常见面试题目解析 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1728025889a1142664.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论