Contents

面试-面试

阿里云实习

两个gorountine交替打印1-100

CSDN 洗澡水加冰

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
	"fmt"
	"sync"
)

func main() {
	ch1, ch2 := make(chan struct{}), make(chan struct{})
	wg := sync.WaitGroup{}
	wg.Add(2)
	num := 100

	fmt.Println("start print:")

	go func() {
		defer wg.Done()
		for i := 1; i <= num; i += 2 {
			<-ch1
			fmt.Println("one: ", i)
			ch2 <- struct{}{}
		}
		// 如果这里没有这一步,会直接死锁
		<-ch1
		close(ch2)
	}()

	go func() {
		defer wg.Done()
		// for i := 2; i < num; i += 2 {  // 如果在这里没有= ,会直接死锁
		for i := 2; i <= num; i += 2 {
			<-ch2
			fmt.Println("two: ", i)
			ch1 <- struct{}{}
		}
		close(ch1)
	}()

	ch1 <- struct{}{} // 启动的信号
	wg.Wait()
	fmt.Println("stop the work!")
}

美团后端实习

一面(5.6)

  • 项目讲解
  • OSI七层模型有哪些层,其中的协议有哪些:讲了传统5层,OSI 7层和TCP/IP 5层,OSI7层分别为哪7层,应用层那3层没答全(应用层,表示层,会话层)
  • TCP,UDP区别
  • TCP流量控制(滑动窗口)
  • go map底层实现
  • Mysql的4种隔离等级
  • mysql索引:讲了分类
  • mysql如何判断sql命中索引:explain关键字,返回表的key字段的值来判断
  • 索引失效的场景:答了对索引字段进行运算或函数调用的情况,没有等值判断的情况,最左边是任意匹配的字符串前缀索引的情况。没答索引区分度小的情况,优化器进行代价运算
  • Redis的数据结构和功能:答了逻辑结构和物理结构以及逻辑结构对应的物理结构实现
  • 算法题:215. 数组中的第K个最大元素:答了可以用大顶堆来实现,但是面试官说用快排,其实时间复杂度大顶堆更小O(NlogK),快排时间复杂度是O(NlogN),因为太久没写快排(平时都是直接用sort包的函数),写的有点慢而且最后存在边界问题,有4个测试用例没过,后面自己在leetcode重写了一下
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func findKthLargest(nums []int, k int) int {
    partition:=func(l,r int)int{
        tmp:=nums[l]
        for l<r{
            for l<r&&nums[r]<=tmp{
                r--
            }
            nums[l]=nums[r]
            for l<r&&nums[l]>=tmp{
                l++
            }
            nums[r]=nums[l]
        }
        nums[l]=tmp
        return l
    }
    l,r:=0,len(nums)-1
    for l<r{
        //fmt.Println(l,r)
        t:=partition(l,r)
        if t<k-1{
            l=t+1
        }else if t>k-1{
            r=t-1
        }else{
            l=t
            break
        }
    }
    return nums[l]
}

腾讯后端实习

电话面(5.9)

  • 项目讲解
  • P4编程流程是什么,P4语法熟悉的怎么样了(简历有写)
  • slice和array的区别与联系,slice重组,slice的扩容机制,slice作为函数形参
  • go里面哪些数据可以比较,struct是否可比较
  • go map遍历顺序,底层结构,渐进式rehash,如何按插入顺序遍历map,for k,v:=range 修改v,值为结构体时m[k]能修改结构体的字段吗,值为结构体指针呢
  • context包
  • channel读写特性,读关闭channel,讲了CSP思想,为什么是线程安全的
  • sync.Mutex和RWMutex底层原理
  • defer执行过程,defer里面panic会发生什么,主动调用panic和异常触发的panic有什么区别(中断,信号)
  • top怎么按cpu利用率排序,top怎么原理。ps中进程状态有哪些
  • 僵尸进程和孤儿进程
  • 哪些端口被监听用哪个命令(netstat,ss),TCP的状态有哪些
  • OSI七层模型
  • http版本,及有哪些改进,长连接字段
  • https协议
  • 二叉搜索树的什么遍历是有序的
  • 排序二叉树找到最近公共祖先(我以为是二叉树找二叉树的最近公共祖先了ㄒoㄒ)
  • topK数据,直接小顶堆,堆的底层数据结构

初试(5.11)

  • 项目介绍
  • redis为什么吞吐量高
  • 数据量大,写操作多,读操作少,需要读取历史数据的场景:redis不设过期时间,缓存一致性通过改数据库+改缓存,添加版本号
  • rest风格
  • go内存管理(只答了GC)
  • 性能压测,如果并发不够怎么办
  • 业务代码瓶颈如何查看(答了链路追踪)
  • 无状态服务(jwt)
  • 非对称加密和对称加密,对称加密运用的场景(协商对称密钥)
  • 算法题,手撕LRU(在自己的ide上写的,差点翻车,之前写了好几次了还是容易出错)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package main

import (
	"container/list"
	"fmt"
)

type kv struct {
	key   int
	value int
}

type LRU struct {
	k   int
	max int
	li  list.List
	m   map[int]*list.Element
}

func (l *LRU) put(key, newvalue int) {
	if tmp := l.m[key]; tmp != nil {
		l.li.MoveToFront(tmp)
		tmp.Value.(*kv).value = newvalue
	} else {
		ele := &kv{key, newvalue}
		e := l.li.PushFront(ele)
		l.m[key] = e
		if l.k < l.max {
			l.k++
		} else {
			b := l.li.Back()
			delete(l.m, b.Value.(*kv).key)
			l.li.Remove(b)
		}
	}
}

func (l *LRU) get(key int) int {
	tmp := l.m[key]
	if tmp == nil {
		return -1
	}
	l.li.MoveToFront(tmp)
	return tmp.Value.(*kv).value
}

func NewLRU(size int) *LRU {
	return &LRU{
		k:   0,
		max: size,
		li:  list.List{},
		m:   make(map[int]*list.Element),
	}
}

func main() {
	cache := NewLRU(2)
	cache.put(1, 1)
	cache.put(2, 2)
	fmt.Println(cache.get(1)) // 返回 1
	cache.put(3, 3)           // 该操作会使得关键字 2 作废
	fmt.Println(cache.get(2)) // 返回 -1 (未找到)
	cache.put(4, 4)           // 该操作会使得关键字 1 作废
	fmt.Println(cache.get(1)) // 返回 -1 (未找到)
	fmt.Println(cache.get(3)) // 返回 3
	fmt.Println(cache.get(4)) // 返回 4
}

复试(5.17)

  • 研究生阶段的研究方向
  • 项目介绍
  • 这里提出将电话面和一面留下的问题重新给面试官讲一下,但是面试官说之前技术面的差不多了,这次只聊天
  • 问有没有涉及机器学习的内容,了解联邦学习,点出了腾讯微众银行的FATE框架,但是不准备用到自己的研究方向,因为效率太慢了无法应对快速变化的攻击
  • 协作或者沟通上的问题怎么解决,比如项目中和其他人有分歧
  • 其他人不配合自己怎么办
  • 比较有挑战性的项目选一个讲一下
  • 大家职责不明确的时候怎么办
  • 讨论过多也会有负面效应,可能会出现蛤蟆坑怎么办
  • 你在开放性讨论中成为过焦点吗
  • 职责不明确时会去做职责外的事吗
  • 进项目组时如何快速定位自己的角色,快速融入一个团队
  • 实习时间
  • 研三是否会需要返校做一些事
  • 导师是否允许

hr面(5.19)

  • 自我介绍
  • 考研还是保研的
  • 学硕还是专硕
  • 学制
  • 毕业要求
  • 实验室项目
  • 实习经历介绍
  • 学生组织经历介绍
  • 更喜欢技术还是管理
  • 毕业打算
  • 家庭信息
  • 发展城市意向和职业规划
  • 学校是否允许
  • 实习时间
  • 入职后续流程
  • 反问

百度秋招提前批

一面(7.17)

  • 程序中为什么要考虑初始化:

在编程语言中,对数据结构进行初始化是非常重要的,主要有以下几个好处:

避免未定义的行为:在大多数编程语言中,未初始化的变量可能含有随机值或垃圾值,这可能会导致程序的行为难以预测。初始化数据结构可以确保所有的元素都有一个已知和可预测的初始状态。

减少错误和bug:许多错误和bug都是由于未初始化的数据引起的。通过初始化,我们可以防止这类错误的发生。

优化性能:在某些情况下,初始化数据结构可以提高程序的性能。比如,在知道将要存储多少元素的情况下,预先分配数组或列表的大小,可以减少之后的动态扩展操作,提高性能。

提升代码可读性:显式初始化数据结构的代码更易于理解,因为它使得数据的初期状态更明显。

更好的类型检查:在编译时期,初始化数据结构使得编译器可以进行更加准确的类型检查,这有助于在早期阶段发现类型相关的错误。

  • go里面怎么进行初始化
  • C++智能指针和唯一指针
  • go defer执行顺序
  • go GMP模型
  • go 原子操作,底层原理,为什么不使用乐观锁
  • TCP连接状态码(听岔了,以为是http返回码)
  • MySQL中常见的锁,如何优化锁的策略提升并发量

优化锁策略以提高并发量主要取决于你的具体应用场景,但是以下是一些常见的优化策略:

  1. 使用更精细的锁粒度:粗粒度的锁可以导致并发性能下降,因为它们可能会阻塞不必要的线程。相比之下,更细粒度的锁可以减少阻塞并提高并发性能。例如,考虑将全局锁替换为行级锁或元素级锁。
  2. 减少锁的持有时间:尽可能地减少锁的持有时间可以减少阻塞其他线程的时间,从而提高并发性能。例如,你可以在获取锁之后尽快完成必要的工作然后立即释放锁。
  3. 使用非阻塞锁:非阻塞锁(如乐观锁)通常允许更高的并发度,因为它们通常不会阻塞线程。但是,非阻塞锁可能会引入更复杂的错误处理逻辑,因为操作可能需要重试。
  4. 避免死锁:死锁会导致线程无限期地等待,从而降低并发性能。通过避免循环依赖、保持锁的顺序性获取和使用超时等手段,可以避免死锁的发生。
  5. 使用读写锁:对于读多写少的场景,可以考虑使用读写锁。读写锁允许多个读者并发地读取数据,而写者在写入数据时会阻塞其他的读者和写者。这比使用互斥锁通常可以提供更高的并发度。
  6. 锁剥离:如果一个大的数据结构在任何操作时都被一个锁保护,那么任何读取或写入该数据结构的操作都会阻塞所有其他操作。通过将锁细分,可以允许更多的并行操作。
  7. 使用适合的并发数据结构和库:许多编程语言都提供了并发数据结构和库,这些数据结构和库已经为并发访问进行了优化。
  8. 避免锁竞争:通过设计避免过多线程竞争同一把锁。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func combinationSum2(candidates []int, target int) [][]int {
    onpath:=make([]int,0,len(candidates))
    sum:=0
    ans:=[][]int{}
    var backtrack func(i int)
    backtrack=func(i int){
        if sum==target{
            tmp:=make([]int,len(onpath))
            copy(tmp,onpath)
            ans=append(ans,tmp)
            return
        }
        if sum>target||i>=len(candidates){return}
        onpath=append(onpath,candidates[i])
        sum+=candidates[i]
        backtrack(i+1)
        sum-=candidates[i]
        onpath=onpath[:len(onpath)-1]
        backtrack(i+1)
    }
    backtrack(0)
    return ans
}

第二次一面(7.17)

  • 简历实习内容拷打
  • sync.Once()用法和场景
  • 原子操作和锁的区别

原子操作(Atomic Operation)和锁(Lock)是两种并发编程中常用的同步技术,它们都能够在多线程或者多协程环境中保护共享数据的一致性,防止出现数据竞争(Race Condition)问题。不过,它们的使用场景和工作方式是不一样的:

原子操作:原子操作是不可分割的操作,它确保了在执行过程中不会被其他线程或协程打断。在Golang中,sync/atomic 包提供了一些原子操作的函数,如 AddInt32, AddInt64, LoadPointer, CompareAndSwapInt32 等。这些操作都是线程安全的,可以在没有锁的情况下被多个goroutine同时安全地访问。原子操作通常用于一些简单的读写操作,比如统计数量、设置标志位等。它的优势在于效率高(不需要加锁和解锁),但它并不适合用于复杂的逻辑操作,比如需要多步操作的情况。

锁:锁是一种更通用的同步手段,适合保护一段复杂的操作,这段操作可能包含多步操作并需要在一个原子事务中完成。Golang 的 sync 包提供了 Mutex(互斥锁)和 RWMutex(读写锁)两种类型的锁。当一个goroutine获得了一个锁,其他试图获取这个锁的goroutine就会被阻塞,直到这个锁被释放。因此,锁可以保护一个代码区域,使得一次只有一个goroutine能够执行这个区域内的代码。

在实际开发中,应根据实际的需求和场景选择使用原子操作还是锁。简单的情况下,原子操作更快、更轻量。复杂的情况下,锁能提供更强大的保护能力

  • 代码审查,考察for range原理
  • 浏览器输入uri到页面显示经过了什么过程
  • 算法题:3数之和(我直接写了n数之和的通用解法)

字节后端实习

一面(7.21)

  • panic关键字,没有recover的后果
  • map并发读写会有什么问题
  • io多路复用(select,poll,epoll)
  • 常用linux命令,查看内存,查看服务器日志,过滤
  • TCP流量控制
  • https密钥协商
  • 常见的对称加密算法(当时没想起来,其实密码学课上讲了DES,AES)

常见的对称加密算法:

  1. AES (Advanced Encryption Standard):现在最常用的对称加密算法,被广泛认为是最安全的对称加密算法。AES有多种密钥长度,如128位、192位和256位。
  2. DES (Data Encryption Standard):一种早期的对称加密算法,现已被证明存在安全漏洞,已被AES取代。
  3. 3DES (Triple DES):为了增加DES的安全性而设计的。它是对每个数据块应用三次DES加密。然而,3DES比DES慢,且不如AES安全。
  4. RC4, RC5, RC6:由Ron Rivest开发的一系列加密算法。其中,RC4是一种流式加密算法,RC5和RC6是块加密算法。
  5. Blowfish:一种块加密算法,设计简单,加密速度快,适用于小型微处理器和嵌入式系统。
  6. Twofish:Blowfish的接班者,也是一种块加密算法。它的设计思路是“简单而快速”。

非对称加密算法基于的困难数学问题

  1. 整数因数分解问题:最著名的非对称加密算法RSA就是基于这个问题的。给定一个非常大的合数,找到其所有的素数因子是一项在计算上非常困难的任务。然而,一旦我们知道这些因子,就可以快速检查它们是否正确。因此,这是一个“困难的问题”和“容易的问题”之间的明显不对称性。
  2. 离散对数问题:给定一个有限域中的元素g和h,找出一个整数x使得g^x = h (模p) 是困难的。这是Diffie-Hellman密钥交换和ElGamal加密算法背后的数学问题。
  3. 椭圆曲线离散对数问题:这是在椭圆曲线上版本的离散对数问题。给定椭圆曲线上的两点P和Q,寻找一个整数n使得nP = Q 是困难的。这是椭圆曲线加密(ECC)背后的数学问题。
  • kafka使用场景,kafka性能高的原因,rebalance
  • 算法题,去除链表中的重复数字(我以为是去掉重复的数字但是留下一个,差点翻车,其实是去掉所有重复的数字)

二面(7.21)

  • go-zero项目拷打
  • 什么是服务熔断

服务熔断的工作原理类似于电路熔断器,它会对服务的调用结果进行检查,如果连续调用失败达到一定阈值(如失败率超过50%),那么熔断器将会开启,之后的一段时间内,所有对该服务的调用都将直接返回错误,而不会进行真正的调用。在这段时间过后,熔断器将进入半开启状态,尝试放行部分流量进行调用,如果调用成功,那么熔断器将关闭,恢复正常调用;如果调用仍然失败,那么熔断器继续开启。通过这种方式,服务熔断能够防止服务的持续失败影响到其他服务,提高系统的整体稳定性

  • 讲一下grpc服务
  • 注册中心如何测试服务可用
  • CAP定理
  • golang如何判断两个字符串切片相等
  • 三色标记法(说反了,面试官没有刁难,呜呜呜)
  • mysql的事务隔离级别
  • mysql查询命中索引会进行几次io

最坏情况下(所有数据页都没有在内存中)对于树高为d的索引,我们需要进行d+1次io,前d次查索引,最后一次io根据索引到的主键查数据行(覆盖索引可以优化最后一次io)

  • undo log 和 redo log
  • 主从一致性如何保证,异步复制和半同步复制
  • 输入url到页面加载出来的过程
  • linux服务器如何查看端口占用情况
  • zset的底层数据结构(第一次说对了,是整数集合和跳表,但是后面改错了,改成了整数集合和哈希表(set的底层结构))
  • 算法题,01矩阵中最大全1正方形面积

三面(7.21)

  • 入职时间,换实习原因
  • 项目拷打
  • git底层实现原理,常用命令
  • 项目用的数据库
  • 腾讯实习的内容
  • influxdb为什么快
  • kafka的rebalance,offset如何动的
  • go程序开发流程
  • go测试
  • 支付流程
  • 算法题,一次股票买卖(直接贪心就行,不过觉得太简单了,最后把最复杂的多次股票买卖的状态转移方程写出来给面试官看了)
 |