智力题面试
面试智力题
一面
题目
请用尽可能少的代码实现一个函数,用于计算用户一个月共计交费多少港元。(代码请写的尽量清晰简洁,我们希望能够看到你的编码风格和习惯) 用户在的平台上进行交易,需要交平台使用费。平台使用费的梯度收费方案如下: 每月累计订单数 每笔订单(港元) 梯度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港元。
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
分成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. 归并
八股
数据库索引
分布式锁
简单谈谈项目
二面
题目
删除字符串的空格,要求去除首位空格,中间的空格合并成一个
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
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站弹幕系统
与前端交互部分:用户定位在视频中的某一秒中的时候,拉取视频ID后续N秒的所有弹幕给前端
后端实现部分:
只讨论一个视频ID的情况,设置一个弹幕量级系统,
如果未达到某个量级直接全量下发即可
达到某量级则进入到离线任务调度,进行弹幕优先级判断,在任务未结束的时候可以按照弹幕发布时间给前端,任务结束之后则按照优先级给前端,这样可以保证用户体验以及后端性能
超时时间控制?问的是如何判定一个rpc的超时时间
如果和外部的话会约定一个超时时间,一般在建议时间中+2S
一般分为三个等级
1. 调用基础服务 1S
2. 调用统计等 5S
3. 调用外部服务 10S
三面
题目
并发扣款问题,纯内存操作,解决方案
锁。可以扩展到CAS,读写锁之类的。操作系统原理是信号量
channel队列。排队
数组取最小、大值,要求最小比较次数,和求出期望比较次数,要求最少比较次数
https://blog.csdn.net/shuiziliu1025/article/details/50958190
期望比较次数比较重要: (3N)/2 次
DDD判断ipV4,不能使用库函数
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
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
}
查找元素比放在它左边的所有元素都大, 比它右边的元素都小
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
2. 公式计算
假设先手抛赢的概率为 p
则甲赢的概率为 p
乙赢的概率为 0.5p. 0.5为甲先手抛到反面
p+0.5p=1
2/3
100个人回答五道题,有81人答对第一题,91人答对第二题,85人答对第三题,79人答对第四题,74人答对第五题。答对三道题或三道题以上的人算及格,那么在这100人中至少有多少人及格呢?
计算不及格的即可
总共有500道题,总共答错:500-81-91-85-79-74=90
最多有90/3=30人不及格
所以最少有70人及格
持续抛1枚硬币,直到连续出现3次正面为止,求期望的抛硬币的次数是多少
算递推公式即可
假设 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/3
一副扑克54张牌,现在分成3份,每份18张,问大小王出现在同一份中的概率是多少?
首先分为 A B C 三堆
其中必有一堆有大王,那么只需要考虑小王是否和大王同一堆即可
17/ (17+18+18) = 17/53
分子为小王和大王在一起的情况,分母为所有分法
有A、B两桶颜料,两桶容量都一样,A桶颜色为红。B桶颜色为蓝色,用一勺子从A桶舀一勺颜料到B桶(过程假设没有损耗,颜料能均匀混合),然后再从B桶舀一勺颜料到A桶,求问A桶的蓝红颜料比例和B桶的红蓝颜料比例相比,是大于、等于还是小于的关系。
假设桶容量为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通电话,每通电话都是随机打的,问跨河打的有多少?
其中
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?
额,A直接把药放在箱子里,再加锁即可,送到B去。
B再把自己的锁加在箱子里。送给A
A解自己的锁后给B就行
现在你手里有两柱形状不均匀的香(这两柱香每分钟燃烧的长度是不确定的),已知他们正常燃尽都需要花费一个小时的时间,求如何确定一段15分钟的时间?
A点一段,B点两端,这样是30min
然后B烧完的时候A点另外一端,这样是15min
1000瓶药水里面只有1瓶是有毒的,毒发时间为24个小时,问需要多少只老鼠才能在24小时后试出那瓶有毒
其实问题是,如何给1000瓶毒药编码,我们可以尝试二进制编码
那么1000瓶毒药的编码总共需要log1000=10位
晚上有四个人需要过桥,但是只有一个手电筒,并且桥一次最多两个人,每个人通过桥所需的时间也不同,A、B、C、D过桥所需的时间分别为1、2、5、10分钟。请问如何过桥所需时间最短?
最佳的解决方案是将两个耗时最多的人一起过桥
第一次过桥:A和B一起过桥,需要2分钟,A再回来,所需1分钟,一共所需3分钟
第二次过桥:C和D一起过桥,需要10分钟,B再回来,所需2分钟,一共需12分钟
第三次过桥:A和B一起过桥,所需2分钟
一共所需17分钟。