面试智力题

本文最后更新于:2024年5月30日 下午

智力题面试

面试智力题

一面

题目

请用尽可能少的代码实现一个函数,用于计算用户一个月共计交费多少港元。(代码请写的尽量清晰简洁,我们希望能够看到你的编码风格和习惯)
用户在的平台上进行交易,需要交平台使用费。平台使用费的梯度收费方案如下:
每月累计订单数 每笔订单(港元)
梯度1:1-5笔 => 30.00
梯度2:6-20笔 => 15.00
梯度3:21-50笔 => 10.00
梯度4:51笔及以上 => 1.00
假设一个用户,一个月交易了6笔订单,则在梯度1交费共计: 30港元*5=150港元,在梯度二交费:15港元,一共交费165港元。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func calcAmount(orderCount int) (amount int) {
type level struct {
min int
max int
fee int // 单价
}
levels := []level{
{min: 1, max: 5, fee: 30},
{min: 6, max: 20, fee: 15},
{min: 21, max: 50, fee: 10},
{min: 51, max: 0, fee: 1},
}
for _, l := range levels {
if orderCount >= l.min && (orderCount <= l.max || l.max == 0) {
amount += l.fee * (orderCount - l.min + 1)
break
} else {
amount += l.fee * (l.max - l.min + 1)
}
}
return amount
}

给25匹马,5个赛道,最少几次赛跑能够找到前3

1
2
3
4
5
6
7
分成5组:A B C D E
先跑5次得到排名
A1 B2 C3 D4 E1跑一次得到第一名.假设排名为A1 B2 C3 D4 E1,那么得到第一名A1
在把上面的第二、第三名, 得到第一名的A组的第二名再拉出来跑即可
A2 B2 C3

所以是7

给定两个排序数组,判断数组A是否是数组B的子集,只需要给思路,不需要写代码

1
2
3
1. 快慢指针
2. 二分
3. 归并

八股

数据库索引

分布式锁

简单谈谈项目

二面

题目

删除字符串的空格,要求去除首位空格,中间的空格合并成一个

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
func main() {
println(trimSpaces(" hello world! "))
println(trimSpaces(" hello world! "))
}

func trimSpaces(str string) string {
var slow, fast = 0, len(str) - 1
for slow < fast && str[slow] == ' ' {
slow++
}
for slow < fast && str[fast] == ' ' {
fast--
}
str = str[slow : fast+1]

slow, fast = 0, 0
arr := []byte(str)
for fast < len(arr) {
if arr[fast] != ' ' || (fast-1 > 0 && arr[fast-1] != ' ') {
arr[slow] = arr[fast]
slow++
fast++
} else {
fast++
}
}

return string(arr[:slow])
}

leetcode 151

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
func reverseWords(s string) string {
// 先去除首空格
var i int
arr := []byte(s)
for ; arr[i] == ' '; i++ {
}
arr = arr[i:]
i = len(arr) - 1
for ; arr[i] == ' '; i-- {
}

arr = arr[:i+1]
// 去除中间的空格
slow, fast := 0, 0
for fast < len(arr) {
if arr[fast] != ' ' || (fast-1 > 0 && arr[fast-1] != ' ') {
arr[slow] = arr[fast]
slow++
fast++
} else {
fast++
}
}
arr = arr[:slow]
// 全部反转
reverse(arr, 0, len(arr)-1)

// 再对每一个单词进行反转
slow, fast = 0, 0

for fast < len(arr) {
for fast < len(arr) && arr[fast] != ' ' {
fast++
}
reverse(arr, slow, fast-1)
slow = fast + 1
fast = fast + 1
}

return string(arr)
}

func reverse(str []byte, left, right int) {
for left < right {
str[left], str[right] = str[right], str[left]
left++
right--
}
}

八股&设计题

设计B站弹幕系统

1
2
3
4
5
与前端交互部分:用户定位在视频中的某一秒中的时候,拉取视频ID后续N秒的所有弹幕给前端
后端实现部分:
只讨论一个视频ID的情况,设置一个弹幕量级系统,
如果未达到某个量级直接全量下发即可
达到某量级则进入到离线任务调度,进行弹幕优先级判断,在任务未结束的时候可以按照弹幕发布时间给前端,任务结束之后则按照优先级给前端,这样可以保证用户体验以及后端性能

超时时间控制?问的是如何判定一个rpc的超时时间

1
2
3
4
5
6
如果和外部的话会约定一个超时时间,一般在建议时间中+2S

一般分为三个等级
1. 调用基础服务 1S
2. 调用统计等 5S
3. 调用外部服务 10S

三面

题目

并发扣款问题,纯内存操作,解决方案

1
2
锁。可以扩展到CAS,读写锁之类的。操作系统原理是信号量
channel队列。排队

数组取最小、大值,要求最小比较次数,和求出期望比较次数,要求最少比较次数

1
2
3
https://blog.csdn.net/shuiziliu1025/article/details/50958190

期望比较次数比较重要: (3N)/2

DDD判断ipV4,不能使用库函数

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
68
69
70
func main() {
println(strconv2Number("123"), 123)
println(strconv2Number("0"), 0)
println(isIpV4(""), false)
println(isIpV4("aaa"), false)
println(isIpV4("123.123.123.123.1"), false)
println(isIpV4("123.123.123"), false)
println(isIpV4("128.0.0.1"), false)
println(isIpV4("128.0.0.01"), false)
println(isIpV4("127.0.0.1"), true)
}

// 思路如下.
// 首先判空,判定格式
// 再判断每一个数字是否为0-255
func isIpV4(ip string) bool {
if ip == "" {
return false
}

// 判定是否刚刚好有三个.
// 判定是否为数字和.
var count int
for i := 0; i < len(ip); i++ {
if ip[i] != '.' && (ip[i] < '0' || ip[i] > '9') {
return false
}
if ip[i] == '.' {
count++
}
}
if count != 3 {
return false
}

// 获取三个数字
var strArr = make([]string, 0, 3)
var slow, fast int
for i := 0; i < len(ip); i++ {
if ip[i] == '.' {
strArr = append(strArr, ip[slow:fast])
slow = fast + 1
}
fast++
}
strArr = append(strArr, ip[slow:])

// 判定数字是否合法
for _, v := range strArr {
if len(v) > 1 && v[0] == '0' { // 数字0开头不合法
return false
}
number := strconv2Number(v)
if number < 0 || number > 255 {
return false
}
}

return true
}

func strconv2Number(str string) int {
arr := []byte(str)
var num int
for i := 0; i < len(arr); i++ {
num = num*10 + int(arr[i]-'0')
}
return num
}

tracing原理(我简历上写了)

其余收集到的

leetcode 116

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

func fractionToDecimal(numerator int, denominator int) string {
// 能够整除
if numerator%denominator == 0 {
return strconv.Itoa(numerator / denominator)
}

// 一定是存在小数位的
var s []byte
if numerator < 0 != (denominator < 0) {
s = append(s, '-')
}

// 求整数部分
numerator = abs(numerator)
denominator = abs(denominator)
s = append(s, strconv.Itoa(numerator/denominator)...)
s = append(s, '.')

// 求小数部分
indexMap := map[int]int{}
remainder := numerator % denominator
for remainder != 0 && indexMap[remainder] == 0 {
indexMap[remainder] = len(s)
remainder *= 10
s = append(s, '0'+byte(remainder/denominator))
remainder %= denominator
}

// 有循环节
if remainder > 0 {
insertIndex := indexMap[remainder]
s = append(s[:insertIndex], append([]byte{'('}, s[insertIndex:]...)...)
s = append(s, ')')
}

return string(s)
}

func abs(x int) int {
if x < 0 {
return -x
}
return x
}

查找元素比放在它左边的所有元素都大, 比它右边的元素都小

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
func findElements(arr []int) []int {
// 定义一个辅助rightMinArr数组,rightMinArr[i]表示在[i,len(arr)-1]的元素中的最小值
rightMinArr := make([]int, len(arr))
var min = math.MaxInt
for i := len(arr) - 1; i >= 0; i-- {
rightMinArr[i] = min
if arr[i] < min {
min = arr[i]
}
}

// 在从左往右走
var res = make([]int, 0, len(arr))
var max int = -math.MaxInt
for i := 0; i < len(arr); i++ {
min = rightMinArr[i]
val := arr[i]
if val > max && val < min {
res = append(res, val)
}

if val > max {
max = val
}
}
return res
}

两个人先收抛硬币,先抛到正面的赢,问先手抛赢的概率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
两种解决方案
1. 举例
总共有四种情况
正反 赢
反反 平
正正 赢
反正 输

2/3

2. 公式计算
假设先手抛赢的概率为 p
则甲赢的概率为 p
乙赢的概率为 0.5p. 0.5为甲先手抛到反面

p+0.5p=1
2/3

100个人回答五道题,有81人答对第一题,91人答对第二题,85人答对第三题,79人答对第四题,74人答对第五题。答对三道题或三道题以上的人算及格,那么在这100人中至少有多少人及格呢?

1
2
3
4
计算不及格的即可
总共有500道题,总共答错:500-81-91-85-79-74=90
最多有90/3=30人不及格
所以最少有70人及格

持续抛1枚硬币,直到连续出现3次正面为止,求期望的抛硬币的次数是多少

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
算递推公式即可

假设 e 为抛出正面的期望
则e2可由e1得出

e2 =
e1-> 0.5 正面 1
e1-> 0.5 反面 1+e2

所以e2 = e1 + (0.5 + 0.5(1+e2))
e2 = 2(e1+1)
同理
e3 =
e2-> 0.5 正面 1
e2-> 0.5 反面 1+e3

所以e3 = e2 + (0.5 + 0.5(1+e3))
e3 = 2(e2+1)

最后得到e2 = 6
e3 = 14

恰有两个小孩的家庭,若已知一家有一个男孩,则这家小孩都是男孩的概率为?

1
2
3
4
5
6
所有概率为
男男
女女
男女
女男
其中女女不可能。所以为 1/3

一副扑克54张牌,现在分成3份,每份18张,问大小王出现在同一份中的概率是多少?

1
2
3
4
首先分为 A B C 三堆
其中必有一堆有大王,那么只需要考虑小王是否和大王同一堆即可
17/ (17+18+18) = 17/53
分子为小王和大王在一起的情况,分母为所有分法

有A、B两桶颜料,两桶容量都一样,A桶颜色为红。B桶颜色为蓝色,用一勺子从A桶舀一勺颜料到B桶(过程假设没有损耗,颜料能均匀混合),然后再从B桶舀一勺颜料到A桶,求问A桶的蓝红颜料比例和B桶的红蓝颜料比例相比,是大于、等于还是小于的关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
假设桶容量为K,勺为1
第一次从A->B
A 此时的容量为 K-1
B 此时的容量为 k+1

B桶的蓝色比例为
1/K+1

B桶舀一勺的红色比例为
K/K+1


倒回A桶的红比例为
K/K+1/K = 1/K+1
其中k/k+1是红色的数量。K为总容量

所以比例一样

河两岸各有60w人和40w人,一天内会产生100w通电话,每通电话都是随机打的,问跨河打的有多少?

1
2
3
4
5
6
7
其中
AA = 0.6*0.6=0.36
AB = 0.6*0.4=0.24
BA = 0.4*0.6=0.24
BB = 0.4*0.4=0.16

故而跨地的有 48W

A、B两人分别在两座岛上。B生病了,A有B所需要的药。C有一艘小船和一个可以上锁的箱子。C愿意在A和B之间运东西,但东西只能放在箱子里。只要箱子没被上锁,C都会偷走箱子里的东西,不管箱子里有什么。如果A和B各自有一把锁和只能开自己那把锁的钥匙,A应该如何把东西安全递交给B?

1
2
3
额,A直接把药放在箱子里,再加锁即可,送到B去。
B再把自己的锁加在箱子里。送给A
A解自己的锁后给B就行

现在你手里有两柱形状不均匀的香(这两柱香每分钟燃烧的长度是不确定的),已知他们正常燃尽都需要花费一个小时的时间,求如何确定一段15分钟的时间?

1
2
A点一段,B点两端,这样是30min
然后B烧完的时候A点另外一端,这样是15min

1000瓶药水里面只有1瓶是有毒的,毒发时间为24个小时,问需要多少只老鼠才能在24小时后试出那瓶有毒

1
2
3
其实问题是,如何给1000瓶毒药编码,我们可以尝试二进制编码

那么1000瓶毒药的编码总共需要log1000=10位

晚上有四个人需要过桥,但是只有一个手电筒,并且桥一次最多两个人,每个人通过桥所需的时间也不同,A、B、C、D过桥所需的时间分别为1、2、5、10分钟。请问如何过桥所需时间最短?

1
2
3
4
5
6
7
最佳的解决方案是将两个耗时最多的人一起过桥

第一次过桥:A和B一起过桥,需要2分钟,A再回来,所需1分钟,一共所需3分钟
第二次过桥:C和D一起过桥,需要10分钟,B再回来,所需2分钟,一共需12分钟
第三次过桥:A和B一起过桥,所需2分钟

一共所需17分钟。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!