背景 本人因为要面字节的后端实习岗,在恐慌的驱使下搜刮了网上大量面经。上一个文里的是 Part1-4(计网;数据库;操作系统;python语言)。这个文里的是 Part5-9(散装知识;项目;算法题;智力题;数学题)。
我自己的面经放到 牛客 上了。可以补充着看一下。
- 这个文里的主要内容是算法/智力/数学题。
- 那个智力题我感觉找的还挺全的,最优解通解什么的也都安排上了~ (^_ ^)
5 散装知识
散装知识就是……
既不是计算机网络,又不是操作系统,还不是数据库 ٩(๑`—´๑)۶
5.1 哈希表
- HashMap(java)解决Hash冲突的方法。HashMap原理和扩容
- HashMap 底层是 hash 数组和单向链表实现,数组中的每个元素都是链表。
- 解决Hash冲突的方法:拉链法。当链表长度超过 8 时,链表转换为红黑树。
- 扩容:实际填充度大于loadFactor装载因子(默认值是0.75)。扩容时,调用 resize()方法将table长度变为原来的两倍(初始长度默认16)
- 哈希表是如何解决高并发的? 链表是如何形成环的?
- ReHash在并发的情况下可能会形成链表环 漫画:高并发下的HashMap - 小灰的文章 - 知乎
- 使用线程安全的 ConcurrentHashMap
- ConcurrentHashMap底层是怎么实现的
- hash冲突除了链表还有什么方法?
- 开放定址法
- 线性探测再散列(+1,+2,+3...+n)
- 二次探测再散列(+1,-1,+2,-2,+4,-4...+2^n, -2^n)
- 伪随机探测再散列(+3,-6,+8,-7,-10,+2,+4..一串包含值从1到M–1的permutation随机序列))
- 双散列探查法((hash1(key) + i * hash2(key)) % TABLE_SIZE, i为步长,若再次碰撞则+1)
- 再哈希法(一个要是算出来重复啦,再用另一个算法去算)
- 链地址法(Java hashmap就是这么做的)
- 建立一个公共溢出区
- 开放定址法
- 开放寻址法有什么缺点?
- 记录的数目不能超过桶数组的长度,如果超过就需要扩容,导致某次操作的时间成本飙升
- 使用探测序列需要计算,可能时间成本高
- 记录尺寸或总数规模很大时,空槽占用的空间会导致明显的内存浪费
- 删除记录时,比较麻烦。需要设置删除标记。导致额外的空间和操作。
- 优点:便于序列化
- 拉链法有什么优点
- 对于记录总数频繁可变的情况,处理的比较好(避免了动态调整的开销)
- 由于记录存储在动态分配的节点中,不会造成内存的浪费,适合那种记录本身尺寸(size)很大的情况,此时指针的开销可以忽略不计
- 删除记录时,比较方便,直接通过指针操作即可
- 缺点:相比结构紧凑的数据类型(比如数组),哈希表的跳转访问会带来额外的时间开销;不便于序列化。 拉链法&开放寻址对比总结:空间的问题;删除的问题;访问速度的问题;系列化的问题
5.2 面向对象
- Override和overload的区别
- 重载Overload:表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。和编译器有关,和OOP无关。
- 重写Override:表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类对象调用这个方法时将调用子类中的方法(即使是父类指针)。可以实现多态。
- Restful api
- REST代表Representational State Transfer(表象层状态转变),是一种API设计风格。面试官:你连RESTful都不知道我怎么敢要你? - 布莱恩特的文章 - 知乎
- 统一接口Uniform Interface:接口要包括URL(一种资源)和自描述信息(如何处理这种资源)。可读性强。使得客户端只需要关注实现接口就可以。
- 无状态性Stateless:客户端的每一次请求带有充分的信息(url,header,body)能够让服务端识别。好处是便于debug,坏处是浪费传输带宽。
- Client-Server:数据存储在Server端,Client端只需使用就行。两端彻底分离。使client端代码的可移植性变强,Server端的拓展性变强。
- 可缓存Cacheable:管理得当的缓存会部分地或完全地除去客户端和服务端之间的交互,进一步改善性能和延展性
- 系统分层Layered System:客户端通常无法得知自己是直接还是间接与端服务器进行连接。分层时同样要考虑安全策略。
- 如果要给软件加一个功能,是要用组合还是用继承
- 如果需求上一些功能是可自由组合的,那么应该用组合,强行用继承的话会产生组合爆炸(Combinatorial explosion)问题。
- 继承层次过深、过复杂,会影响到代码的可维护性。
- 继承主要有三个作用:表示is-a 关系,支持多态特性,代码复用。
- 从理论上讲,通过组合、接口、委托三个技术手段,可以替换掉继承。在项目中不用或者少用继承关系,特别是一些复杂的继承关系。
- 继承向上传递调用以实现复用,组合向内传递以实现委托。
public class Ostrich implements Tweetable, EggLayable {//鸵鸟
private TweetAbility tweetAbility = new TweetAbility(); //组合
private EggLayAbility eggLayAbility = new EggLayAbility(); //组合
//... 省略其他属性和方法...
@Override
public void tweet() {
tweetAbility.tweet(); // 委托
}
@Override
public void layEgg() {
eggLayAbility.layEgg(); // 委托
}
}
作者:沐晨
链接:https://zhuanlan.zhihu.com/p/93899632
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 多重继承 如何解决钻石模型
- 面向对象语言的三大特性
- 封装:隐藏对象的属性和实现细节,仅对外公开接口。目的是增强安全性和简化编程
- 继承:就是子类继承父类的特征(变量)和行为(函数)。可以代码重用(但也带来高耦合),最大的意义在于实现多态。
- 多态:子类对象可以被视作一个父类对象。根据程序运行时实际使用的类来达成不同的效果(执行子类自己重写的函数)。
5.3 Redis
为什么要用Redis?Redis为什么这么快? - 胖狗子的文章 - 知乎
- Redis的数据结构
- String 整数,浮点数或者字符串
- Set 集合
- Zset 有序集合(跳跃表实现)
- Hash 散列表
- List 列表
- Redis为什么快,为什么单线程
- 首先,采用了多路复用阻塞io机制;
- 然后,数据结构简单,操作节省时间;
- 最后,运行在内存中,自然速度快
- 单线程:Redis的瓶颈不是cpu的运行速度,而往往是网络带宽和机器的内存大小。单线程切换开销小,容易实现。
5.4 设计模式
- 用过哪些设计模式,我说单例、工厂、观察者、代理模式。(他想让我说策略模式、装饰模式和适配器模式,但这三个我都不知道)
- 装饰器模式
- 动态地给某个对象添加额外的行为,比生成子类更为灵活(因为装饰的是对象不是类)
- 抽象组件(Component): 定义被装饰对象的接口
- 具体组件(ConcreteComponent):定义被装饰器装饰的对象
- 抽象装饰器(Decorator): 维护对Component的引用
- 具体装饰器角色(ConcreteDecorator):向组件添加新的职责
- 设计模式了解吗,说一下策略模式
- 环境类(Context):持有一个具体的策略类的引用,提供给客户端调用。屏蔽高层模块对策略/算法的直接访问. 还可以提供一些公共功能或者是存储一些状态。
- 抽象策略类(Strategy): 策略的抽象,一般定义接口。
- 具体策略类(ConcreteStrategy):具体的抽象策略实现,实现接口。
- 优点:切换策略很方便,增加策略也方便。
- 缺点:每个策略都是一个类(数量增多),策略类都需要对外暴露(上层模块必须知道有哪些策略然后才能决定使用哪一个)
- 适配器模式
- 适配器模式可以让原本接口不匹配的两个类可以协同工作,起到转换的作用。
- 类的适配器模式:把适配的类的API转换成为目标类的API。继承extend原始的被适配类,实现implement目标的接口。
- 对象的适配器模式:也是把适配的类的API转换成为目标类的API。但是通过组合而非继承来完成。包含被适配类的对象,实现implement目标的接口。
5.5 Git
- git rebase git merge 的区别
5.6 Behaviour Question
- 看你简历有xxx,我们来聊一下
- 给我说说你现在都在关注哪些最新的技术好么
- 描述一个解决的问题,如何去解决的
- 描述一个未解决的问题,探索解决问题的途径
- 对一个陌生的工作,如何去开展工作
- 最近看了什么非技术的书和技术的书
- 会不会给自己比较大的压力
6 项目
显然项目这个引人而异,我是挑了几个和我的项目比较沾边的。
- 项目里的登录状态怎么做的
- django自带的auth模块
- token有哪些好处,相比较cookie+session
- Mvc,MTV
- 大量连接时sessionid可能重复?
- 可能会。但概率很小。
- django的orm
- 如何搜索数据库里的文章
# Django ORM: 搜索标题或内容包含指定字符串的文章,大小写不限
articles = articles.filter(
Q(title__icontains=search) |
Q(body__icontains=search))
# 这个Q是把条件包起来,因为filter直接逗号分隔的话,是AND。用了Q,和 | 运算符可以实现OR
- 标签-文章表太慢怎么办(not sure)
- 文章上加索引。标签重复度高,不能加索引
- 加缓存。一般用户按标签看文章都是看最上面的那几篇
- 大文件上传
7 算法
7.1 经典题目
最长上升子序列
这是一道考烂了的题
# 定义 arr[i]: 长度为i+1的连续上升子序列的尾部数字大小
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
arr = []
for n in nums:
# 特殊情况:一开始还没有数据的时候
if not arr:
arr.append(n)
continue
# 从后往前找到比当前值小的数,在它的基础上延长
i = len(arr)-1
while arr[i] >= n and i>=0: i -= 1
if arr[i] < n: # 因为找到小值而停止
if i+1 < len(arr): # 如果后面有人,尝试更新这个长度。尾部数字越小越好
arr[i+1] = min(arr[i+1], n)
else: # 如果后面没人,新开一个长度
arr.append(n)
else: # 没有找到比自己更小的,在0处停止
arr[0] = n
return len(arr)
# 使用二分查找优化至 O(NlogN)
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
arr = [] # arr[i]: 长度为i+1的连续上升子序列的尾部数字大小
for n in nums:
# 如果arr中没有比n大的数
if not arr or arr[-1]<n:
arr.append(n)
continue
# 找比n刚好大一点的数,二分查找
left,right=0,len(arr)-1
while left <= right:
mid = (left+right)//2
midval = arr[mid]
if midval > n:
right = mid-1
elif midval < n:
left = mid+1
else:
left = right = mid
break
# 经过分析,left所指的肯定就是比n大一点或等于n的
arr[left] = min(arr[left], n)
return len(arr)
剑指48.最长不含重复字符的子字符串
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s: return 0
left, right = 0, 1
# 左开右闭。right负责探索未知天地。
contain = set()
contain.add(s[left])
ans = 1
while right < len(s):
if s[right] not in contain:
contain.add(s[right])
ans = max(ans, len(contain))
right += 1
else:
# 如果发现重复,就扔掉左边字符,直到不重复为止
# 极端情况下,left=right,相当于扔空原有集合
contain.discard(s[left])
left += 1
return ans
接雨水
- 每面墙一旦出现,就把它和左边墙之间能填的缝隙全都使用水泥填上。(这里假装是水泥,比水更形象。浇过的水泥也会变成墙)
- 那么左边已经处理的部分,一定是先增加再减少的阶梯型,没有凹陷。对一面新出现的墙,它的左边由近及远是从比它矮的墙到比他高的墙
- 按层次浇筑水泥。一开始的瓶颈是左边的矮墙,随着一层层浇筑水泥,池底的高度也越来越高。最后的瓶颈是它自己作为右边的矮墙。
class Solution:
def trap(self, height: List[int]) -> int:
stack = []
ans = 0
for i, h in enumerate(height):
if h == 0:
continue
bar = 0 # 对于每一面新处理的墙,bar都是全新的
while stack:
i_, h_ = stack[-1]
if h_ <= h: # 如果栈顶<=当前的墙, 用当前的墙不断碰撞左边的墙,并且逐步提升bar
ans += (i-i_-1) * (h_ - bar)
bar = h_
stack.pop()
else: # 如果栈顶>当前的墙, 当前的墙成为了右边的矮子,根据当前bar加水。
# 当前bar是当前的墙和此时栈顶之间的墙的最大值,在第一步的不断碰撞中已经计算好了
ans += (i-i_-1) * (h - bar)
break
stack.append((i,h))
return ans
LRU实现
三数之和
一个字符串,找出出现第二多的字符
- 这应该只能哈希表计数吧?
鸡蛋掉落
这个题写的时候屡屡受挫,要注意:
- 思考的时候就要用题里给的符号,要么就全程用自己的符号。总之要统一!!! 这个题草稿纸上想的时候用的是f(N,d)思考,但题目的顺序是(K,N),结果各种顺序写反也看不出来,浪费好多不必要时间。
- Debug复杂的递归代码的时候,一下子打印一大片,分隔线也不好画,而且请相信即使分开了思路也是不可能follow的。。。另一种办法是按代码的逻辑功能分开,控制变量地测试。 例如"把二分替换成穷举"结果就由错变对了,说明二分代码有bug而其他代码无bug。不过这么做的前提是这个逻辑模块有“更愚蠢但可靠”的替代品。如穷举法。
- 这题一开始我去找极值点了,侥幸认为没有那么多水平线除非答案附近,而实际上导数为0的地方还挺多的,离真正的最小值也挺远的。。。这就导致midval=midrval的时候非常难以处理。正解是去搜索upper=downer的地方。可见一开始考虑不周后果不堪设想。
- 对于多次进入的递归函数,要考虑到输入参数的可能范围。鸡蛋是每次递归最多减一,因此可以保证在K=1的时候肯定已经被处理,不会有K=0的情况。如果出现N=0的情况一定是因为mid=1或mid=N。理论上前者有可能出现,因此还是处理一下比较好。
一个先递增后递减的数组找出独特元素的个数
先用set 需要O(n)的时间复杂度 然后写了个双指针 写完了也不用跑就结束了
实现循环队列 (leetcode 622)
- 使用一个 k+1 长度的数组存储
- 注意在MOD数组下标的时候也要按k+1取模啊!
二叉树转双向链表
爬楼梯 => 不允许到达7的倍数层
- 类似斐波那契数列。不允许到达7的倍数层就不算了呗。
树中两个点的最长路径和 (leetcode 124)
class Solution:
def maxPathSum(self, root: TreeNode) -> int:
ans = root.val
def work(node):
nonlocal ans
# ans: node+max(0,左边最长链长)+max(0,右边最长链长)
# work函数:返回以node为头结点的最长链长,可能为负数
if not node: return 0
leftmax = work(node.left)
rightmax = work(node.right)
#print("at node", root.val, "leftmax,rightmax=",leftmax, rightmax)
ans = max(ans, node.val + max(0,leftmax) + max(0,rightmax))
return max(leftmax, rightmax, 0) + node.val
work(root)
return ans
找比当前数大的下一个数(如1243,结果是1324)
- 注意严谨。爬山的时候等于也要爬。这样才能正确处理重复数字
- python中nums[nextdigit+1:].sort()行不通
class Solution: # LeetCode 31
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
这个函数也可以处理有重复值的情况~ 注意“爬山”时候的大于等于号~
"""
cur = len(nums)-1 # 从后往前找第一个跌落(<)的地方
while cur >= 1 and nums[cur-1] >= nums[cur]: cur -=1
if cur==0:
nums.reverse()
return
nextdigit = cur-1 # 即将增大进位的那个数位
cur = len(nums)-1 # 从后往前找最小的大于target的数
while nums[cur] <= nums[nextdigit]: cur -=1
nums[cur], nums[nextdigit] = nums[nextdigit], nums[cur]
nums[nextdigit+1:] = sorted(nums[nextdigit+1:])
求x的y次方,想出比直接for循环更好的方案
- 二进制快速幂
求众数
- 摩尔投票
找出一个未排序的整数数组中没有出现的最小的正整数
用列表下标做哈希。
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度
滑动窗口。
二维数组,按行递增,按列递增,问是否能找到某个数字
- 从左下角右上角找的简单办法。但面试官会让你写正常二分,哈哈。
- 矩阵分成4块。但不是按行列中心点切割。具体见题解方法3
一个IP地址转成int类型数据(IP地址是32个比特,int也是32个比特)
二叉树找两个节点的公共父节点
快排当有很多重复数时怎么优化
三路快排.这玩意必须得手写一遍!
一组数字怎么排列组合出的数最大
和字符串同理。自定义规则排序。
字符矩阵是否存在给定的字符串
- dfs,计算时间复杂度.剑指 Offer 12. 矩阵中的路径
非负整数字符串重新排列使得结果最小
# 剑指 Offer 45. 把数组排成最小的数
from functools import cmp_to_key # python大法好!!
def compare(x1, x2): # 如果x1更大返回正数,更小返回负数,相等返回0.
return int(x1+x2) - int(x2+x1)
class Solution:
def minNumber(self, nums: List[int]) -> str:
nums = [str(i) for i in nums]
nums.sort(key=cmp_to_key(compare))
return ''.join(nums)
给定字符串,找出最短的包含指定字符的子串
并说出算法的时间复杂度,证明算法的有效性。
- 应该和最长不重复子串一个思路,维护左右指针和一个集合
LeetCode 54 螺旋矩阵,做完提问怎样保证不会重复输出.
这个题有两种方法:分圈打印法,和四周“剥边”法。下面是分圈打印的代码。 保证不会重复输出也就是在left,right重合,或top,down重合的时候,不打印第二个即可。
class Solution: # 分圈打印法
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix: return []
m,n = len(matrix),len(matrix[0])
left,right = 0, n-1
top,down = 0, m-1
ans = []
def printCircle(top, left, down, right):
nonlocal ans
for i in range(left,right+1):
ans.append(matrix[top][i])
for j in range(top+1, down):
ans.append(matrix[j][right])
if top != down: # 这一部分可能被吞哦!
for i in range(right, left-1, -1):
ans.append(matrix[down][i])
if left != right: # 这一部分可能被吞哦!
for j in range(down-1, top, -1):
ans.append(matrix[j][left])
while left<=right and top <= down:
printCircle(top,left,down,right)
top, left, down, right = top+1, left+1, down-1, right-1
return ans
Leetcode103 二叉树的锯齿形层次遍历
Leetcode25 K 个一组翻转链表
找出第一个缺失的正整数
- 输入的正整数有n个的话,第一个缺失的正整数一定是在[1,n+1]这个区间里
- 扫描列表,记录[1,n+1]的出现状况
- 扫描数组,找到[1,n+1]中第一个没有出现的数
rand(1, 5)实现rand(1, 7)
- 调用两次rand5,生成1~25种组合
- 对1~21,使用%7+1,输出结果
- 否则,拒绝采样,重头再来一遍
7.2 大数据量
10亿个无序整数找出中位数。
由于是 10 亿个整数,因此无法在内存中处理。也就是说要采用合适的外排序算法。 这里可以使用桶排序,使用 n 个区间均匀的桶,遍历一遍整数,将它们存入对应区间的桶中,并统计桶中数字个数。这样就可以找到中位数所在的桶,如果桶中数字还是很多,再进行桶排序,否则进内存中排序即可。
内存只能存1000条数据。如何对5000条数据进行排序?
- 5k条分为5组,每组1k条
- 每组分别读入内存,排序后写入硬盘
- 从5组的每组中读入前200条,每次5选1,写入硬盘。当一组中的200条排完后,再从读该组200条,直到排序完成。
10万个ip,求出现频率最高的10个IP
- 先哈希分组成多个小文件
- 在每个小文件中求出现频率最高的10个IP
- 计算全局的频率最高的10个IP
1000 亿个无符号整数,找最大的 100 个
内存不够的情况下用什么方案?内存充足的情况下呢? partition 的方案不稳定,有什么稳定的方法吗?
- 内存不够堆排序(建立一个k=100大小的小顶堆,然后遍历数据,有大于根结点的值将堆根节点替换上)他说时间复杂度呢,我说 N * log K。
- 内存够用计数排序(无符号整数就那么多,1000亿肯定有重复的。计数排序的优点是O(n),缺点是内存占用太大。既然内存够用就随便用咯~)
- 桶排序可以认为是弱化版的计数排序,计数排序是桶的大小为1的桶排序。
7.3 信号量与锁
信号量实现哲学家进餐
- 方法一:给五个叉子编号为 0-4,每个哲学家必须先拿编号小的叉子。
- 方法二:允许最多4个哲学家去持有叉子,可保证至少有 1 个哲学家能吃上面。
- 方法三:让编号为奇数的哲学家先拿左边,编号为偶数的哲学家先拿右边。 三种思路 - 破坏循环等待 - ngcafai - 力扣题解
用条件变量实现读写锁
生产者消费者
手撕生产者、消费者模型,10个生产者,10个消费者,队列容量为30个 (下图是不while死循环的生产者消费者, 和原面经有所区别)
实现阻塞队列
且要求取的时候先取优先级最大的. 我先直接用优先队列,后面面试官让我再实现最大堆.(下方代码并没有实现最大堆)
import threading
class BoundedBlockingQueue(object): # LeetCode1188
def __init__(self, capacity: int):
self.capacity = capacity
self.q = []
self.con = threading.Condition()
def enqueue(self, element: int) -> None:
with self.con: # 自动acquire,release
while len(self.q) == self.capacity: # 满
self.con.wait()
self.q.append(element)
self.con.notify()
def dequeue(self) -> int:
with self.con:
while len(self.q) == 0: # 空
self.con.wait()
ans = self.q.pop(0)
self.con.notify()
return ans
def size(self) -> int:
return len(self.q)
8 智力题
这里是我千辛万苦攒的智力题,后来面试一个智力题都没问,好气 (= _=)
小白鼠喝毒药
1000瓶水,有一瓶是毒药,你有无限只小白鼠,喝了毒药一小时后毒发,怎样在一个小时内知道哪瓶水有毒?最少要用多少只小白鼠?1000瓶药水,1瓶有毒药,几只小白鼠能够找出? - 温笛的文章 - 知乎
这道题考察的是对2进制的理解。
- 鼠1: 喝个位编号为1的瓶子,鼠2:喝十位编号为1的瓶子……
- 如鼠1死了,鼠2没死,鼠3死了,那么就是101=5号瓶子有毒。那么就是101=5号瓶子有毒。
- N只老鼠的量程为2^N,1000只瓶子位于2^9 ~ 2^10,即10只小鼠可以测1000瓶水。
如果30分钟就会毒发呢?(可以测2轮)
- 类比三进制,在对应的位数,第一轮喝1的瓶子,死了填1,第二轮喝2的瓶子,死了填2,两轮都没死就填0.
烧香计时
两根可以烧一小时的香,确定45分钟
- A两端点燃,B一端点燃
- A烧灭后,B还剩半根香,正常来说还要30分钟烧完
- 两端点燃B, 15分钟后烧完
海盗分金币
五个海盗抢来了一百个金币。五个海盗都很贪婪但同时又都很明智。他们按照抽签的方法,排出一个次序。首先由抽到一号签的海盗说出一套分金的方案,如果5个人中有50%以上(不含50%)的人同意,那么便依照这个方案执行,否则这个提出方案的人将被扔到海里喂鱼,接下来再由抽到二号签的海盗继续说出一套方案,然后依次类推到第五个。如果你是抽到一号签的海盗,你计划提出一套什么样的方案,在保住小命的前提下,分得最多的金子?
对于这个问题要采用倒推方法:
- 如果只剩5号:5号自己独吞所有金币
- 如果只剩4号和5号:5号一定投反对票让4号喂鲨鱼,以独吞全部金币。这种情况对4号是死局。
- 如果只剩3,4,5号:3号自己有一票,只需要再争取1票即可通过。如果3号的方案通不过,就会变成两个人的情况。对4号是死局,对5号是利好。4号不需要金钱也会支持三号。分配方案:“100,0,0”
- 如果剩2,3,4,5号:2号自己有一票,需要争取2票即可通过。如果2号的方案通不过,就会变成3个人的情况。对3号是利好,对4号是获得0个金币,对5号是获得0个金币。因此2号可以通过 “98,0,1,1” 拉拢4号5号。
- 如果剩1,2,3,4,5号:1号自己有一票,需要争取2票即可通过。如果1号的方案通不过,就会变成4个人的情况。对2号是利好,对3号是0个金币,对4号是1个金币,对5号是1个金币。因此1号可以通过(97,0,1,2,0)或(97,0,1,0,2)拉拢3号,和4或5号。
- 如果剩0,1,2,3,4,5号(自己加的):0号自己有一票,需要争取3票即可通过。如果0号的方案通不过,就会变成5个人的情况。对1号是利好,对2号是0个金币,对3号是1个金币,对4号是2个或0个金币,对5号是2个或0个金币。因此1号可以通过(95,1,2,2,0)拉拢2,3,4号,或(95,1,2,0,2)拉拢2,3,5号。
老虎吃羊
100只老虎可以靠吃草活下来,但是它们更喜欢吃羊。每次只有一头老虎可以吃羊,但是吃了羊的那头老虎也会变成羊。假设所有老虎都很聪明,首先考虑保命然后再考虑吃羊,问羊是否会被吃?
- 1只老虎的时候:果断吃羊
- 2只老虎的时候:不能吃,不然自己就变成了1只老虎的情况里的那只羊
- 3只老虎的时候:吃。吃羊后会变成2只老虎的情况,而两只老虎的情况不吃羊。
以此类推,偶数只老虎的话,羊不会被吃,奇数只的话羊会被吃
螺丝与螺母
赛马
25匹马5个跑道。每场比赛每个跑道只允许一匹马,每次只能记录名次不能记录时间,不存在并列。问最少比多少次能选出最快的3匹马? 赛马64匹马,8个赛道,求前四名最少次数。面试官提示有的情况是11次,有的情况是10次。 腾讯算法面试——赛马问题 - 一帆诗的文章 - 知乎
12个球,一个质量不一样,最多称三次,如何称
神级通解:N 个乒乓球中有一个和其他的质量不同,用天平最少几次一定能称出来? - 知乎
分为3组,记为{1,2,3,4},{5,6,7,8},{9,10,11,12}
- [第一次]先称前两组,如果重量相同,我们知道8一定是正常球。然后[第二次]称{9,10},{8,11}。
- 若重量相同,说明9,10,11都是正品,12是次品。[第三次]把12和1称一下即可判断是轻了还是重了。
- {9,10}重,则9或10重,或者11轻。[第三次]比较9和10:如果一样则11轻,否则那个重的就是次品。
- {9,10}轻,则9或10轻,或者11重。[第三次]比较9和10:如果一样则11重,否则那个轻的就是次品。
- 如果前两组中,{1,2,3,4}重,则1-4中有某个重或者5-8某个轻。我们知道9-12都是正常球。[第二次]比较{1,2,5}(2个第一组的,1个第二组的)和{3,6,9}(第1,2,3组各一个)
- {1,2,5}重: 可能是1重或2重,或者6轻。[第三次]这时比较1、2,若不一样重,重的是次品。若一样重,6是次品。
- 一样重:说明1,2,5,3,6,9都是正常球。4重,或者7轻或8轻,[第三次]比较7,8。
- {1,2,5}轻:说明5轻或者3重,[第三次]比较3(或者5)和一个正常地即可(比如9)c)
- 如果前两组中,{5,6,7,8}重,同{1,2,3,4}重的情况分析。
A中5个球,B中7个球,甲乙两个人每次至少取一个球,且只能取同一个箱子的球。取到最后一个球的人输。问先手是否有必胜策略?
- 有。这个题就是手工dp。a,b<=1时分析很简单,略。 在a=b,a>=2的时候先手会输,其余时候先手必胜。
9 数学题
三门问题
有三个盒子,其中一个有奖品,参与者选中一个后,主持人打开另一个盒子没有奖品,问参与者是否换盒子,换与不换的中奖概率是?
给一个函数g,g以p的概率输出1,以1-p的概率输出0,设计一个函数f以等概率的情况输出0和1
拒绝采样法 :调用两次g, 如果结果是0/1,输出0,结果是1/0输出1,结果是1/1或者0/0则再重新算一次。
一个圆内随机三个点刚好在一个半圆的概率
圆内任取三点/四点在同一半圆内的概率是多少? - 心月狐的回答 - 知乎 灵感: 在圆周上随机取三个点,形成锐角三角形、直角三角形、钝角三角形的概率分别为? (答案:1/4 0 3/4)其实钝角三角形就是在同一个半圆上。
最后 点个赞再走呗 (≧▽≦)/ [疯狂暗示][疯狂暗示][疯狂暗示]