Golang面試題解析 4
31. 算法
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(Utf8Index("桃園龜山最美麗", "龜山"))
fmt.Println(strings.Index("桃園龜山最美麗", "章"))
fmt.Println(strings.Index("", "章"))
fmt.Println(Utf8Index("33ws桃園龜山最美麗", "龜山"))
}
func Utf8Index(str, substr string) int {
asciiPos := strings.Index(str, substr)
if asciiPos == -1 || asciiPos == 0 {
return asciiPos
}
pos := 0
totalSize := 0
reader := strings.NewReader(str)
for _, size, err := reader.ReadRune(); err == nil; _, size, err = reader.ReadRune() {
totalSize += size
pos++
// 匹配到
if totalSize == asciiPos {
return pos
}
}
return pos
}
// 2
// -1
// -1
// 6
32.Singleton
https://github.com/kimi0230/DesignPatternGolang/tree/master/Singleton
package main
import "sync"
type singleton struct{}
var ins *singleton
var mu sync.Mutex
// 方法ㄧ 加鎖: 雖然解決並發的問題,但每次加鎖是要付出代價的
func GetIns() *singleton {
mu.Lock()
defer mu.Unlock()
if ins == nil {
ins = &singleton{}
}
return ins
}
//方法二 雙重鎖:避免了每次加鎖,提高代碼效率
func GetIns1() *singleton {
if ins == nil {
mu.Lock()
defer mu.Unlock()
if ins == nil {
ins = &singleton{}
}
}
return ins
}
//sync.Once
var once sync.Once
func GetIns2() *singleton {
once.Do(func() {
ins = &singleton{}
})
return ins
}
sync.Once 是 Golang package 中使方法只執行一次的對象實現,作用與 init 函數類似。但也有所不同。
init 函數是在文件包首次被加載的時候執行,且只執行一次
sync.Onc 是在代碼運行中需要的時候執行,且只執行一次 當一個函數不希望程序在一開始的時候就被執行的時候,我們可以使用 sync.Once 。 https://zhuanlan.zhihu.com/p/44360489
33.編譯執行下面代碼會出現什麼?
- 在沒有修改channel的狀態時, 此時沒有一個簡單通用的方式可以去檢查channel 是否關閉
- 關閉一個 close的 channel會發生 panic.
- 將值發送到一個關閉的channel 會發生panic.
- 不要在接收端關閉 channel
- 如果有多個發送端, 也不要在發送端關閉 channel
- 只有一個發送端, 才在發送端關閉 channel
- 不要關閉已關閉的 channel
- 不要送值到已關閉的 channel The Channel Closing Prirciple
https://play.golang.org/p/ajo3CD5uCZ6 往已經關閉的channel寫入數據會panic的
package main
import (
"fmt"
"time"
)
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)
}
/*
ok
close
panic: send on closed channel
goroutine 18 [running]:
main.main.func1(0xc0000bc000)
/tmp/sandbox420965894/prog.go:12 +0x43
created by main.main
/tmp/sandbox420965894/prog.go:10 +0x5c
*/
34.編譯執行下面代碼會出現什麼?
package main
import "fmt"
type ConfigOne struct {
Daemon string
}
func (c *ConfigOne) String() string {
return fmt.Sprintf("print: %v", c)
}
func main() {
c := &ConfigOne{}
c.String()
}
如果類型實現String(),%v和%v格式將使用String()的值。因此,對該類型的String()函數內的類型使用%v會導致無限遞歸。 編譯報錯:
runtime: goroutine stack exceeds 1000000000-byte limit fatal error: stack overflow
35.程式題 : 反轉整數
反轉整數 反轉一個整數 例子1: x = 123, return 321 例子2: x = -123, return -321, 輸入的整數要求是一個 32bit 有符號數,如果反轉後溢出,則輸0.
https://play.golang.org/p/DkiQgA393c7
package main
import (
"fmt"
"math"
)
func reverse(x int32) int32 {
var num int64
x64 := int64(x)
for x64 != 0 {
num = num*10 + x64%10
x64 = x64 / 10
//fmt.Println("num: ",num)
}
// 使用 math 包中定義好的最大最小值
if num > math.MaxInt32 || num < math.MinInt32 {
return 0
}
return int32(num)
}
func main() {
fmt.Println(reverse(321))
}
36.程式題 : 合併重疊區間
合併重疊區間 給定一組 區間,合併所有重疊的 區間。 例如: 給定:[1,3],[2,6],[8,10],[15,18] 返回:[1,6],[8,10],[15,18]
https://go.dev/play/p/VEFkw-N0_yS
package main
import (
"fmt"
"sort"
)
type Interval struct {
Start int
End int
}
func merge(intervals []Interval) []Interval {
if len(intervals) <= 1 {
return intervals
}
// 先由小排到大 起始位子
sort.Slice(intervals, func(i, j int) bool {
return intervals[i].Start < intervals[j].Start
})
//fmt.Println("intervals", intervals)
res := make([]Interval, 0)
swap := Interval{}
for k, v := range intervals {
if k == 0 {
swap = v
continue
}
if v.Start <= swap.End {
// 如果當下的開始比上一個結束小時, 將結束設回當下的結束
swap.End = v.End
} else {
res = append(res, swap)
swap = v
}
//fmt.Println("res:", res)
}
res = append(res, swap)
return res
}
func main() {
inter := []Interval{
{2, 6},
{1, 3},
{8, 10},
{15, 18},
}
fmt.Println(merge(inter))
}
37. 輸出什麼 : 每個中文字,3個byte
https://go.dev/play/p/FEhwHNcMXRS
package main
import (
"fmt"
)
func main() {
fmt.Println(len("bj!"))
fmt.Println(len("你好"))
fmt.Println(len("你好bj!"))
}
/*
3
6
9
*/
每個中文字,3個byte
38.編譯並運行如下代碼會發生什麼?
map的value本身是不可尋址的,因為map中的值會在內存中移動
package main
import "fmt"
type Test struct {
Name string
}
var list map[string]Test
func main() {
list = make(map[string]Test)
name := Test{"xiaoming"}
list["name"] = name // 出錯!
list["name"].Name = "Hello"
fmt.Println(list["name"])
}
編程報錯cannot assign to struct field list["name"].Name in map 因為list[“name”]不是一個普通的指針值,map的value本身是不可尋址的,因為map中的值會在內存中移動,並且舊的指針地址在map改變時會變得無效。 定義的是var list map[string]Test,注意哦Test不是指針,而且map我們都知道是可以自動擴容的,那麼原來的存儲name的Test可能在地址A,但是如果map擴容了地址A就不是原來的Test了,所以go就不允許我們寫數據。你改為var list map[string]*Test試試看。
解法
https://play.golang.org/p/pxA9bQDrztB
package main
import "fmt"
type Test struct {
Name string
}
var list map[string]*Test // 改成 *Test
func main() {
list = make(map[string]*Test)
name := &Test{"kimi"}
list["name"] = name
list["name"].Name = "Hello"
fmt.Println(list["name"])
}
make(map[string]string, 3)的len是0
https://go.dev/play/p/be1yY3UfVcQ
a := make([]int, 3)
fmt.Println(len(a)) // 3
b := make(map[string]string, 3)
fmt.Println(len(b)) // 0
39.ABCD中哪一行存在錯誤?
https://play.golang.org/p/PU8krulJH67
func g(x *interface{}) 只能接受 *interface{}
package main
type S struct {
}
func f(x interface{}) {
}
func g(x *interface{}) {
}
func main() {
s := S{}
p := &s
f(s) //A
g(s) //B 錯
f(p) //C
g(p) //D 錯
}
/*
./prog.go:16:3: cannot use s (type S) as type *interface {} in argument to g:
*interface {} is pointer to interface, not interface
./prog.go:18:3: cannot use p (type *S) as type *interface {} in argument to g:
*interface {} is pointer to interface, not interface
*/
>
看Golang是強類型語言,interface是所有golang類型的父類, 類似Java的Object。函數中func f(x interface{})的interface{}可以支持傳入golang的任何類型,包括指針, 但是函數func g(x interface{}) 只能接受 interface{}
40.編譯並運行如下代碼會發生什麼?
package main
import (
"sync"
//"time"
)
const N = 10
var wg = &sync.WaitGroup{}
func main() {
for i := 0; i < N; i++ {
go func(i int) {
wg.Add(1)
println(i)
defer wg.Done()
}(i)
}
wg.Wait()
}
這是使用WaitGroup經常犯下的錯誤!請各位同學多次運行就會發現輸出都會不同甚至又出現報錯的問題。 這是因為go執行太快了,導致wg.Add(1)還沒有執行main函數就執行完畢了。
解法
https://play.golang.org/p/vpKDPDG9F6i
package main
import (
"sync"
//"time"
)
const N = 10
var wg = &sync.WaitGroup{}
func main() {
for i := 0; i < N; i++ {
wg.Add(1) // 寫在這
go func(i int) {
println(i)
defer wg.Done()
}(i)
}
wg.Wait()
}