腾讯互娱面经详解

先来唠唠

腾讯互娱面经详解-1图片

今天刷脉脉的时候, 发现百度副总裁璩静一个人竟然占了前三的两个热榜, 对于她的离职你怎么看?

言归正传, 本文的重点还是分享面经干货

今天分享的是一位朋友在腾讯互娱的面经, 他本人目前已经是收到offer了, 让我们来看看这个难度如何:

腾讯互娱面经详解-2图片

面试题详解

Go接口

接口在Golang中扮演着连接不同类型之间的桥梁,它定义了一组方法的集合,而不关心具体的实现。接口的作用主要体现在以下几个方面:

多态性:

接口允许不同的类型实现相同的方法,从而实现多态性。这意味着我们可以使用接口类型来处理不同的对象,而不需要关心具体的类型。

package main

import "fmt"

type Animal interface {
 Sound() string
}

type Dog struct{}

func (d Dog) Sound() string {
 return "Woof!"
}

type Cat struct{}

func (c Cat) Sound() string {
 return "Meow!"
}

func main() {
 animals := []Animal{Dog{}, Cat{}}

 for _, animal := range animals {
  fmt.Println(animal.Sound())
 }
}

在上面的示例中,我们定义了一个Animal接口,它包含了一个Sound()方法。然后,我们实现了Dog和Cat两个结构体,分别实现了Sound()方法。通过将Dog和Cat类型赋值给Animal接口类型,我们可以在循环中调用Sound()方法,而不需要关心具体的类型。这就体现了接口的多态性,不同的类型可以实现相同的接口方法。

解耦合:

接口可以将抽象与实现分离,降低代码之间的耦合度。通过定义接口,我们可以将实现细节隐藏起来,只暴露必要的方法,从而提高代码的可维护性和可读性。

package main

import "fmt"

type Printer interface {
 Print(string)
}

type ConsolePrinter struct{}

func (cp ConsolePrinter) Print(message string) {
 fmt.Println(message)
}

type FilePrinter struct{}

func (fp FilePrinter) Print(message string) {
 // 将消息写入文件
 fmt.Println("Writing message to file:", message)
}

func main() {
 printer := ConsolePrinter{}
 printer.Print("Hello, World!")

 printer = FilePrinter{}
 printer.Print("Hello, World!")
}

在上面的示例中,我们定义了一个Printer接口,它包含了一个Print()方法。然后,我们实现了ConsolePrinter和FilePrinter两个结构体,分别实现了Print()方法。通过将不同的结构体赋值给Printer接口类型的变量,我们可以在主函数中调用Print()方法,而不需要关心具体的实现。这样,我们可以根据需要轻松地切换不同的打印方式,实现了解耦合。

可扩展性:

package main

import "fmt"

type Shape interface {
 Area() float64
}

type Rectangle struct {
 Width  float64
 Height float64
}

func (r Rectangle) Area() float64 {
 return r.Width * r.Height
}

type Circle struct {
 Radius float64
}

func (c Circle) Area() float64 {
 return 3.14 * c.Radius * c.Radius
}

func main() {
 shapes := []Shape{Rectangle{Width: 5, Height: 10}, Circle{Radius: 3}}

 for _, shape := range shapes {
  fmt.Println("Area:", shape.Area())
 }
}

在上面的示例中,我们定义了一个Shape接口,它包含了一个Area()方法。然后,我们实现了Rectangle和Circle两个结构体,分别实现了Area()方法。通过将不同的结构体赋值给Shape接口类型的切片,我们可以在循环中调用Area()方法,而不需要关心具体的类型。这样,当我们需要添加新的形状时,只需要实现Shape接口的Area()方法即可,而不需要修改已有的代码。这就实现了代码的可扩展性。

接口的应用场景

  • API设计:接口在API设计中起到了至关重要的作用。通过定义接口,我们可以规范API的输入和输出,提高代码的可读性和可维护性。
  • 单元测试:接口在单元测试中也扮演着重要的角色。通过使用接口,我们可以轻松地替换被测试对象的实现,从而实现对被测代码的独立测试。
  • 插件系统:接口可以用于实现插件系统,通过定义一组接口,不同的插件可以实现这些接口,并在程序运行时动态加载和使用插件。
  • 依赖注入:接口在依赖注入中也有广泛的应用。通过定义接口,我们可以将依赖对象的创建和管理交给外部容器,从而实现松耦合的代码结构。
  • 空结构体的用途

    不包含任何字段的结构体,就叫做空结构体。

    空结构体的特点:

  • 零内存占用
  • 地址都相同
  • 无状态
  • 空结构体的使用场景

    • 实现set集合

    在 Go 语言中,虽然没有内置 Set 集合类型,但是我们可以利用 map 类型来实现一个 Set 集合。由于 map 的 key 具有唯一性,我们可以将元素存储为 key,而 value 没有实际作用,为了节省内存,我们可以使用空结构体作为 value 的值。

    package main
    
    import "fmt"
    
    type Set[K comparable] map[K]struct{}
    
    func (s Set[K]) Add(val K) {
       s[val] = struct{}{}
    }
    func (s Set[K]) Remove(val K) {
       delete(s, val)
    }
    
    func (s Set[K]) Contains(val K) bool {
       _, ok := s[val]
       return ok
    }
    
    func main() {
       set := Set[string]{}
       set.Add("程序员")
       fmt.Println(set.Contains("程序员")) // true
       set.Remove("程序员")
       fmt.Println(set.Contains("程序员")) // false
    }
    • 用于通道信号

    空结构体常用于 Goroutine 之间的信号传递,尤其是不关心通道中传递的具体数据,只需要一个触发信号时。例如,我们可以使用空结构体通道来通知一个 Goroutine 停止工作:

    package main  
    
    import (  
       "fmt"  
       "time"  
    )  
    
    func main() {  
       quit := make(chan struct{})  
       go func() {  
          // 模拟工作  
          fmt.Println("工作中...")  
          time.Sleep(3 * time.Second)  
          // 关闭退出信号  
          close(quit)
       }()  
    
       // 阻塞,等待退出信号被关闭  
        操作符带索引的列
    <li>range:只检索给定范围的行,使用一个索引来选择行。一般使用between、>、7->19->26,发现26比23大,就回到19</li>
    <li>再用第1层连接19->22->26,发现比23大,那么就插入到26之前,22之后</li>
    <p>上面这张图就是跳表的初步原理,但一个元素插入链表后,应该拥有几层连接呢?跳表在这块的实现方式是随机的,也就是23这个元素插入后,随机出一个数,比如这个数是3,那么23就会有如下连接:</p>
    <ul><li>第3层head->23->end</li>
    <li>第2层19->23->26</li>
    <li>第1层22->23->26</li></ul>
    <p>下面这张图展示了如何形成一个跳表</p>
    <p><img class="aligncenter size-full wp-image-604120" src="https://img.mryunwei.com/uploads/2024/05/20240511101753371.jpg" width="1080" height="1266" alt="腾讯互娱面经详解-3" title="腾讯互娱面经详解-3">图片</p>
    <p>在上述跳表中查找/插入23的过程为:</p>
    <p><img class="aligncenter size-full wp-image-604121" src="https://img.mryunwei.com/uploads/2024/05/20240511101753788.jpg" width="1023" height="334" alt="腾讯互娱面经详解-4" title="腾讯互娱面经详解-4">图片</p>
    <p>总结一下跳表原理:</p>
    <ul><li>每个跳表都必须设定一个最大的连接层数MaxLevel</li>
    <li>第一层连接会连接到表中的每个元素</li>
    <li>插入一个元素会随机生成一个连接层数值[1, MaxLevel]之间,根据这个值跳表会给这元素建立N个连接</li>
    <li>插入某个元素的时候先从最高层开始,当跳到比目标值大的元素后,回退到上一个元素,用该元素的下一层连接进行遍历,周而复始直到第一层连接,最终在第一层连接中找到合适的位置</li></ul>
    <p>使用场景:</p>
    <ul><li>对有序数据进行排序,例如新闻排行榜或游戏排行榜。</li>
    <li>对数据进行分组,例如将所有评分在3.0 到4.0 之间的电影分为一组。</li>
    <li>对数据进行去重,例如将所有重复的单词从文本中删除。</li></ul>
    <p>本文转载自微信公众号「王中阳Go」,作者「王中阳Go」,可以通过以下二维码关注。</p>
    <p><img class="aligncenter size-full wp-image-604122" src="https://img.mryunwei.com/uploads/2024/05/20240511101753923.jpg" width="150" height="150" alt="腾讯互娱面经详解-5" title="腾讯互娱面经详解-5"></p>
    <p>转载本文请联系「王中阳Go」公众号。</p>