Analysis of 500 Golang common interview questions

1. Alternately print numbers and letters

Problem description
Use two goroutines to alternately print sequences, one goroutine prints numbers, and the other
goroutine prints letters. The final effect is as follows:

12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728

Problem-solving ideas
The problem is very simple, use the channel to control the progress of printing. Two channels are used to control the printing sequence of numbers and letters respectively. After the number printing is completed, the letter printing is notified through the channel. After the letter printing is completed, the number printing is notified, and then the work is repeated.

Source code reference

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(wait *sync.WaitGroup) {
str := "ABCDEFGHIJKLMNOPQRSTUVWXYZ" i := 0
for{
select {	
case <-letter:	
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)	
number<-true	
wait.Wait()	

Source code analysis

Two channels are used here for notification, letter is responsible for notifying the goroutine that prints the letter to print the letter, and number is used to notify the goroutine that prints the number to print the number.
wait is used to wait for the letters to be printed and exit the loop.

2. Determine whether the characters in the string are all different

Problem description
Please implement an algorithm to determine whether all the characters of a string [are they all different]. Here we require [do not allow the use of additional storage structures]. Given a string, please return a bool value, true means all characters are different, false means there are identical characters. Ensure that the characters in the string are [ASCII characters]. The length of the string is less than or equal to 【3000】.

Problem-solving ideas
There are several key points here. The first one is ASCII characters. There are 256 ASCII characters in total, 128 of which are commonly used characters and can be input on the keyboard. The numbers after 128 cannot be found on the keyboard.
Then it is all different, that is, the characters in the string are not repeated. Again, no additional storage structure is allowed, and the string is less than or equal to 3000.
This problem is easy to do if other additional storage structures are allowed. If not allowed, you can use the built-in method of golang.

Source code reference
Judging by the strings.Count function:

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
}

Judging by the strings.Index and strings.LastIndex functions:

func isUniqueString2(s string) bool {
    
     
if strings.Count(s,"") > 3000{
    
    
return	false
}
for k,v := range s {
    
     
if v > 127 {
    
    
return false
}
if strings.Index(s,string(v)) != k {
    
    
 return false
}
}
return true
}

Source code analysis
The above two methods can realize this algorithm.
The first method uses the built-in method strings.Count of golang, which can be used to determine the number of another string contained in a string.
The second method uses the built-in methods strings.Index and strings.LastIndex of golang to determine that the index of the specified string in another string is unknown, which are the first found position and the last found position respectively.

3. Flip the string

Problem description
Please implement an algorithm to flip a given string without using [extra data structure and storage space] (you can use a single process variable).
Given a string, please return a string, which is the reversed string. Ensure that the length of the string is less than or equal to 5000.

Problem-solving ideas
Flipping a string is actually flipping a string back and forth with the middle character as the axis, that is, assigning str[len] to str[0], and
assigning str[0] to str[len].

Source code reference

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-1-i], str[i]
}
return string(str), true
}

Source code analysis
takes 1/2 of the string length as the axis, and assigns values ​​before and after

4. Determine whether two given strings are consistent after sorting

Problem description
Given two strings, please write a program to determine whether the characters of one string can be changed into another string after rearrangement. Here it is stipulated that [case and case are different characters], and string key spaces are considered. Given a string s1 and a string s2, please return a bool, representing whether the two strings can be the same after rearrangement. Ensure that the length of both strings is less than or equal to 5000.

The idea of ​​solving the problem
must first ensure that the length of the string is less than 5000. Afterwards, it only needs to loop once to see if all the characters in s1 exist in s2.

Source code reference

func isRegroup(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 false
}
}
return true
}

Source code parsing
Here, the built-in method strings.Count of golang is still used to determine whether the characters are consistent.

5. String replacement problem

Problem Description
Please write a method to replace all spaces in a string with "%20". Assume that the string has enough space to store the new characters, and the real length of the string is known (less than or equal to 1000), and at the same time ensure that the string is composed of [uppercase and lowercase English letters]. Given a string as the original string, return the replaced string.

Problem-solving ideas
There are two problems, the first is only English letters, and the second is to replace spaces.

Source code reference

func replaceBlank(s string) (string, bool) {
    
     
if len([]rune(s)) > 1000 {
    
    
return s, false
}
for _, v := range s {
    
    
if string(v) != " " && unicode.IsLetter(v) == false {
    
     
return s, false
}
}
return strings.Replace(s, " ", "%20", -1), true
}

Source code parsing
Here, the golang built-in method unicode.IsLetter is used to determine whether a character is a letter, and then strings.Replace is used to replace spaces.

6. Robot coordinate problem

Problem description
There is a robot, give a series of instructions, L turns left, R turns right, F takes one step forward, B takes one step back, and finally asks the coordinates of the robot. At the beginning, the robot is located at 0 0, and the direction is positive Y. Repeat command n can be input: For example, R2(LF) is equal to command RLFLF. What are the final coordinates of the robot?

Problem-solving ideas
One of the difficulties here is parsing repetitive instructions. The main command is parsed successfully, and the calculation of coordinates is simple.

Source code reference

package main 
import (
"unicode"
)
const (
Left = iota 
Top
Right
Bottom
)
func main() {
    
    
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
}

Source code analysis
Here, three values ​​are used to indicate the current status of the robot, namely: x indicates the x coordinate, y indicates the y coordinate, and z indicates the current direction. The L, R commands will change the value z, and the F, B commands will change the values ​​x, y. Changes in the values ​​x, y are also affected by the current z value.

If it is a repeated instruction, save the number of repetitions and the repeated instruction and call it recursively.

Common Grammar Question 1

1. Can the following code run? Why.

type Param map[string]interface{
    
    } type Show struct {
    
    
Param
}
func main1() {
    
    
s := new(Show) s.Param["RMB"] = 10000
}

Analysis
found two problems:

  1. The main function cannot add numbers.
  2. The new keyword cannot initialize the Param property in the Show structure, so direct
    operations on s.Param will cause errors.

2. Please tell me what is wrong with the following code.

type student struct {
    
     
Name string
}
func zhoujielun(v interface{
    
    }) {
    
     
switch msg := v.(type) {
    
    
 case *student, student:
msg.Name
}
}

Analysis:
Golang stipulates that there is only one type list for case T1 of switch type, then the type of v in v := m.(type) is T1 type.

If it is case T1, T2, there are multiple types in the type list, then the type of v is still the type of multiple corresponding interfaces, that is, the type of m.

So the type of msg here is still interface{}, so it does not have a Name field, and an error will be reported during the compilation phase. For detailed explanation, see: https://golang.org/ref/spec#Type_switches

3. Write out the printed results.

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

Analysis:
According to the grammar of golang, methods, properties or structs starting with lowercase are private. Similarly, conversion of private properties cannot be launched when json is decoded or transcoded.

In the title, it is impossible to get the name value of People normally. Moreover, the private attribute name should not be tagged with json.

4. The following code is problematic, please explain the reason.

type People struct {
    
     
Name string
}
func (p *People) String() string {
    
     
return fmt.Sprintf("print: %v", p)
}
func main() {
    
    
p := &People{
    
    } 
p.String()
}

Analysis:
The String() string method in golang actually implements the String interface, which is defined in fmt/print.go:

type Stringer interface {
    
     
String() string
}

When using the print method in the fmt package, if the type implements this interface, it will be called directly. However, when printing p in the title, it will directly call the String() method implemented by p, and then a circular call will be generated.

5. Please find out the problem of the following code.

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

Analysis:
The scheduling time of goroutines in golang is uncertain. In the title, the first
goroutine that writes to a channel may not be called yet, or it may close the channel directly when it has been called but not finished writing, which may lead to writing failure. Since there is a panic error.

6. Please explain whether the following code is written correctly.

var value int32
func SetValue(delta int32) {
    
     
for {
    
    
v := value
if atomic.CompareAndSwapInt32(&value, v, (v+delta)) {
    
    
 break
}
}
}

Analysis:
The atomic.CompareAndSwapInt32 function does not need to be called in a loop.

7. Why does the following program explode abnormally after running.

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

Analysis:
There are a few questions:

  1. The parameter value of time.Sleep is too large, exceeding the limit of 1<<63 - 1.
  2. defer p.deferError() needs to be called at the beginning of the coroutine, otherwise the panic cannot be caught.

8. Please tell me where the following code is wrong

func main() {
    
    
abc := make(chan int, 1000) 
for i := 0; i < 10; i++ {
    
    
abc <- i
}
go func() {
    
    
for		a := range abc	
{
    
     fmt.Println("a: ", a)
}
}()
close(abc) fmt.Println("close") time.Sleep(
time.Second * 100)
}

Analysis:
The coroutine may not be started, but the pipeline is closed.

9. Please state the following code, why an error is reported during execution

type Student struct {
    
     
name string
}
func main() {
    
    
m := map[string]Student{
    
    "people": {
    
    "zhoujielun"}} 
m["people"].name = "wuyanzu"
}

Analysis:
The value of the map itself is not addressable, because the value in the map will move in memory, and the old pointer address will become invalid when the map changes. Therefore, if you need to modify the map value, you can change the non-pointer type value in the map to a pointer type, such as using map[string]*Student .

10. Please tell me what is wrong with the following code?

type query func(string) string
func exec(name string, vs ...query) string {
    
    
 ch := make(chan string)
fn := func(i int) {
    
     ch <- vs[i](name)
}
for i, _ := range vs {
    
     
go fn(i)
}
return <-ch
}
func main() {
    
    
ret := exec("111", func(n string) string {
    
     
return n + "func1"
}, func(n string) string {
    
     
return n + "func2"
}, func(n string) string {
    
     
return n + "func3"
}, func(n string) string {
    
     
return n + "func4"
})
fmt.Println(ret)
}

Analysis:
According to the execution efficiency of the four goroutines after startup, 111func4 is likely to be printed, but other 111func* may also be executed first, and exec will only return a message.

11. Why does the following code get stuck?

package main
import (
"fmt" 
"runtime"
)
func main() {
    
    
var i byte 
go func() {
    
    
for i = 0; i <= 255; i++ {
    
    
}
}()
fmt.Println("Dropping mic")
// Yield execution to force executing other goroutines runtime.Gosched()
runtime.GC() 
fmt.Println("Done")
}

Analysis:
In Golang, byte is actually aliased to uint8. So the above for loop will always be true, because i++ will overflow when i=255, and i <= 255 must be true.
That is, the for loop can never exit, so the above code can actually be equivalent to this:

go func() {
    
    
for {
    
    }
}

The goroutine being executed gives up the execution right of the current goroutine and schedules the execution of the following goroutine when the following situations occur:

  • I/O operations
  • Channel blocking
  • system call
  • run longer

If a goroutine takes too long to execute, the scheduler will put a flag (preempt) on its G object. When a function call occurs inside the goroutine, it will actively check this flag first. If it is true, it will let Execution power.

The goroutine started in the main function is actually an endless loop with no IO blocking, no Channel blocking, no system call, and no function call.

That is, it cannot voluntarily give up its execution right, even if it has been executed for a long time, the scheduler has marked preempt.

The GC action of golang requires all running goroutines to be stopped. Therefore, the program will be stuck in runtime.GC() waiting for all coroutines to exit.

Common Grammar Questions 2

1. Write the output of the following code.

package main import (
"fmt"
)
func main() {
    
    
defer_call()
}
func defer_call() {
    
    
defer func() {
    
     fmt.Println("打印前") }()
defer func() {
    
     fmt.Println("打印中") }()
defer func() {
    
     fmt.Println("打印后") }() 
panic("触发异常")
}

Analysis:
The implementation of the defer keyword is very similar to the go keyword, the difference is that it calls runtime.deferproc instead of runtime.newproc.

Where defer occurs, the instruction call runtime.deferproc is inserted, and then where the function returns, the instruction call runtime.deferreturn is inserted.

In the control structure of goroutine, there is a table to record defer. When runtime.deferproc is called, the expression that needs defer will be recorded in the table, and when runtime.deferreturn is called, it will be popped from the defer table in turn. and execute.

Therefore, the final output order of the title should be the reverse order of the defer definition order. A panic error does not terminate the execution of defer.

2. What is the problem with the following code and why?

type student struct {
    
     
Name string
Age	int
}
func pase_student() {
    
    
m := make(map[string]*student) 
stus := []student{
    
    
{
    
    Name: "zhou", Age: 24},
{
    
    Name: "li", Age: 23},
{
    
    Name: "wang", Age: 22},
}
for _, stu := range stus {
    
     
m[stu.Name] = &stu
}
}

Analysis:
In the for ... range syntax of golang, the stu variable will be reused, and each cycle will copy the value in the collection to this variable, so the map in the last m will store the last student of stus value.

3. What will the following code output and why?

func main() {
    
    
runtime.GOMAXPROCS(1) 
wg := sync.WaitGroup{
    
    } 
wg.Add(20)
for i := 0; i < 10; i++ {
    
     
go func() {
    
    
fmt.Println("i: ", i) 
wg.Done()
}()
}
for i := 0; i < 10; i++ {
    
     
go func(i int) {
    
    
fmt.Println("i: ", i)
 wg.Done()
}(i)
}
wg.Wait()
}

Analysis:
This output determines which G is prioritized by the scheduler. From the source code of the runtime, we can see that when a G is created, it will be prioritized into the runnext field of the next schedule as the G for the next priority schedule. Therefore, the first output is the last created G, which is 9.

func newproc(siz int32, fn *funcval) {
    
    
argp := add(unsafe.Pointer(&fn), sys.PtrSize)
gp := getg()
pc := getcallerpc() 
systemstack(func() {
    
    
newg := newproc1(fn, argp, siz, gp, pc)
_p_ := getg().m.p.ptr()
//新创建的G会调用这个方法来决定如何调度runqput(_p_, newg, true)
if mainStarted {
    
     
wakep()
}
})
}
...
if next {
    
     retryNext:
oldnext := _p_.runnext
//当next是true时总会将新进来的G放入下一次调度字段中
goto retryNext
}
if oldnext == 0 {
    
     
return
}
// Kick the old runnext out to the regular run queue. 
gp = oldnext.ptr()
}

4. What will the following code output?

type People struct{
    
    }
func (p *People) ShowA() {
    
     
fmt.Println("showA") 
p.ShowB()
}
func (p *People) ShowB() {
    
     
fmt.Println("showB")
}
type Teacher struct {
    
     
People
}
func (t *Teacher) ShowB() {
    
     
fmt.Println("teacher showB")
}
func main() {
    
    
t := Teacher{
    
    } 
t.ShowA()
}

Analysis:
The output results are showA and showB. There is no concept of inheritance in the golang language, only composition, no virtual methods, and no overloading. Therefore, *Teacher's ShowB will not override the composed People's method.

5. Will the following code trigger an exception? Please explain in details

func main() {
    
    
runtime.GOMAXPROCS(1) int_chan := make(chan int, 1)
string_chan := make(chan string, 1) 
int_chan <- 1
string_chan <- "hello" 
select {
    
    
case value := <-int_chan: 
fmt.Println(value)
case value := <-string_chan:
panic(value)
}
}

Parsing:
The result is executed randomly. Golang will fairly select one to execute when multiple cases are readable.

6. What does the following code output?

func calc(index string, a, b int) int {
    
     
ret := a + b
fmt.Println(index, a, b, ret) 
return ret
}
func main() {
    
     
a := 1
b := 2
defer calc("1", a, calc("10", a, b)) 
a = 0
defer calc("2", a, calc("20", a, b)) 
b = 1
}

Parsing:
The output is:

10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4

When defer is defined, it will calculate the parameters of the calling function, so the parameters 10 and 20 will be output first. Then execute in reverse order according to the defined order.

7. Please write the following input content

func main() {
    
    
s := make([]int, 5)
s = append(s, 1, 2, 3) 
fmt.Println(s)
}

Parsing:
The output is 0 0 0 0 0 1 2 3 . make specifies the length when initializing the slice, so when appending data, the data will be filled from the len(s) position.

8. What is wrong with the following code?


```go
type UserAges struct {
    
     
ages map[string]int 
sync.Mutex
}
func (ua *UserAges) Add(name string, age int) {
    
     
ua.Lock()
defer ua.Unlock() 
ua.ages[name] = age
}
func (ua *UserAges) Get(name string) int {
    
     
if age, ok := ua.ages[name]; ok {
    
    
return age
}
return -1
}

Analysis:
It may panic when executing the Get method.

Although sync.Mutex is used as a write lock, the map is not safe for concurrent reading and writing. Map is a reference type. When reading and writing concurrently, multiple coroutines access the same address through pointers, that is, access shared variables. At this time, there is competition for reading and writing resources at the same time. An error message will be reported: "fatal error: concurrent map read and map write".

Therefore, a lock is also required in Get, because this is only for reading, it is recommended to use the read-write lock sync.RWMutex.
``

9. What are the problems with the following iterations?

func (set *threadSafeSet) Iter() <-chan interface{
    
    } {
    
     
ch := make(chan interface{
    
    })
go func() {
    
    
set.RLock()
for elem := range set.s {
    
     
ch <- elem
}
close(ch) 
set.RUnlock()
}()
return ch
}

Analysis:
By default, the channel initialized by make is unbuffered, that is, it will block during iterative writing.

10. Can the following code be compiled? Why?

package main 
import (
"fmt"
)
type People interface {
    
     
Speak(string) string
}
type Student struct{
    
    }
func (stu *Student) Speak(think string) (talk string) {
    
     
if think == "bitch" {
    
    
talk = "You are a good boy"
} else {
    
    
talk = "hi"
}
return
}
func main() {
    
    
var peo People = Student{
    
    } 
think := "bitch" 
fmt.Println(peo.Speak(think))
}

insert image description here
insert image description here

Guess you like

Origin blog.csdn.net/qq_28581269/article/details/119153485