admin管理员组

文章数量:1588254

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                }         }
        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()
}

解析:

有一下几个问题:

  1. time.Sleep 的参数数值太大,超过了 1<<63 - 1 的限制。
  2. defer p.deferError() 需要在协程开始出调用,否则无法捕获 panic

8、请说出下面代码哪里写错

本文标签: 题目常见Golang