1、LRU缓存机制
哈希表+双向链表,保证可以随时在头结点和尾节点进行数据插删除,同时记得更新dict哈希表中存储的key值
最新使用的程序插在双向链表的头部。
class DLinkedNode:
"""
双链表节点
"""
def __init__(self, key: int = 0, value: int = 0):
self.prev = None
self.next = None
self.key = key
self.value = value
class LRUCache:
def __init__(self, capacity: int):
self.size = 0
self.capacity = capacity
self.cache = dict()
self.head = DLinkedNode()
self.tail = DLinkedNode()
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key: int) -> int:
# 如果key不在cache中, 直接返回-1
if key not in self.cache:
return -1
# 从cache中获取key对应的节点
node = self.cache[key]
# 移动节点到链表的头部
self.move_to_head(node)
# 返回节点的值
return node.value
def put(self, key: int, value: int) -> None:
node = DLinkedNode(key, value)
# 如果key不在cache中
if key not in self.cache:
# 且此时缓存容量已满
if self.capacity == self.size:
# 移除链表中最后一个节点
last_node = self.remove_last()
# 同时从cache中弹出最后一个节点
self.cache.pop(last_node.key)
# 把新节点加入到链表的头部
self.add_to_head(node)
# 更新cache
self.cache[key] = node
else:
# 容量未满, 直接把新节点加入到链表头部
self.add_to_head(node)
# 同时更新缓存
self.cache[key] = node
# 缓存大小加一
self.size += 1
else:
# 如果key在cache中, 那就不用再考虑缓存容量的影响
# 把老节点cache和链表中移除
old_node = self.cache.pop(key)
self.remove_node(old_node)
# 再把新节点加入到cache和链表中
self.add_to_head(node)
self.cache[key] = node
def add_to_head(self, node: DLinkedNode):
"""
把节点加入到链表头部
"""
next_node = self.head.next
self.head.next = node
node.prev = self.head
node.next = next_node
next_node.prev = node
def remove_node(self, node: DLinkedNode):
"""
从链表中移除指定节点
"""
node.prev.next = node.next
node.next.prev = node.prev
def move_to_head(self, node: DLinkedNode):
"""
把节点移动到链表头部
"""
self.remove_node(node)
self.add_to_head(node)
def remove_last(self) -> DLinkedNode:
"""
移除链表中最后一个节点并返回
"""
last_node = self.tail.prev
self.tail.prev.prev.next = self.tail
self.tail.prev = self.tail.prev.prev
return last_node
2、抢红包算法实现
二倍均值法
方法实现的原理是:每次以 [最小值,红包剩余金额 / 人数 * 2] 的区间进行取值。
假设红包金额为 88.88,红包数量为 8 个,理想情况下,方法的实现效果:
第一个人领取红包金额区间为 [0.01, 88.88 / 8 * 2],即是 [0.01, 22.22] 之间随机获取金额数。假设取平均值 11.11,则剩余金额 77.77;
第二个人领取红包金额区间为 [0.01, 77.77 / 7 * 2],即是 [0.01, 22.22] 之间随机获取金额数。假设取平均值 11.11,则剩余金额 66.66;
以此类推…
import random
def get_packet(amount,number):
res =[]
person_num = number
cur_amount = amount*100
for _ in range(number-1):
money = random.randint(1,cur_amount//person_num*2)
cur_amount -= money
person_num -= 1
res.append(money/100)
res.append(cur_amount/100)
return res
res = get_packet(88.88, 8)
for amount in res:
print('红包金额:{}'.format(amount))
3、最小生成元
如果x+x的各个数字之和得到y,就是说x是y的生成元。给出n(1<=n<=100000),求最小生成元。无解输出0.例如,n=216,121,2005时的解分别是198,0,1979.
利用打表法,提前把1到100000的最小生元给存下来,你要知道谁的最小生成元,直接输出就行了。
#include<stdio.h>
#define N 10005
int a[N]
int main()
{
int n;
scanf("%d",&n);
for(int i = 1;i<=10000;i++)
{
int x = i,y =i;
while(y>0)
{
x += y%10;
y/=10;
}
if (a[x] ==0)
a[x] = i;
}
printf("%d",a[n]);
return 0 ;
}
4、python实现一段文本中单词个数的统计
string = "hello world nihao world hey hello java world hi python yeoman word"
list1 = string.split()
set1 = set(list1)
list2 = list(set1)
dir2 = {}
for i in range(len(list2)):
dir2[list2[i]] = 0
for j in range(len(list1)):
if list1[j] == list2[i]:
dir2[list2[i]]+=1
print(dir2)
========
from collections import Counter
list1 = sentence.split()
dir1 = Counter(list1)
5、辗转相除法求最大公约数,求分数之和
def sum(a,b,c,d):
fenzi = b*c +a*d
fenmu = a*c
a =fenmu
b =fenzi
c = a% b
while(c):
a = b
b = c
c = a % b
fenzi /= b
fenmu /= b
return fenzi,fenmu
print(sum(2,1,3,1))
6、十进制转换
def divbase(num,base):
stack = []
digital = "0123456789ABCDEF"
while num >0:
temp = num %base
stack.append(temp)
num = num//base
#print(stack[::-1])
res=""
while stack:
#print(stack[-1])
if base>10:
res +=str(digital[stack[-1]])
stack.pop()
else:
res +=str(stack[-1])
stack.pop()
return res
print(divbase(43,16))
7、两根香,一根烧完1小时,如何测量15分钟
---->开始时一根香两头点着,一根香只点一头,两头点着的香烧完说明过去了半小时,这时将只点了一头的香另一头也点着,从这时开始到烧完就是15分钟。
8、写一个函数,求平方根,函数参数为目标数字和精度,测试案例 fn(4.1,0.001) fn(501.1,0.001) fn(0.045,0.001)
def mySqrt(target,g):
l = float(1)
r = float(target)
mid = float()
while r-l>g:
mid = (l+r)/2
if mid * mid > target:
r = mid
else:
l = mid
return (l+r)/2
print(mySqrt(4.1,0.001))
9、给定一个 0-4随机数生成器 如何生成0-6随机数
rand6() = {rand4() * 5 + rand4() }<=20?x/3:loop
我们可以想,第一个rand6()产生一个0~6范围的随机数a,第二个rand6()产生一个0~6范围的随机数b,如果(a, b)这一对数可以确定一个唯一的数,从而可以产生的49个数(a和b都有7种可能,则(a, b)确定的数就有7*7=49种可能)都是等概率的。这就相当于有一个二维坐标系,X轴和Y轴上都可以取0~6的数,对于取x=a,y=b可以确定一个唯一的点(a, b)。0-6生成0-9随机数
想到这个就是我们的关键,接下来就是想办法构造函数使得产生的每个数都是唯一的。我们不难想到把第一个rand6()产生的随机数a放在个位上,而把第二个rand6()产生的随机数b放在十位上,所有可能为:00~06,10~16,20~26,30~36,40~46,50~56,60~66。很容易看出这些就是7进制数,所以我们用rand6()*7+rand6()就能生成0~49范围的随机数,而且每个数产生的概率相等,都是1/49。产生40~49之间的随机数时不保留而重新产生一个新的随机数,这样产生0~39之间的数也是等概率的,仍然都是1/49,对于产生的数我们记为r(0<= r <=39),则 r/4 即可返回0~9范围的数。而且 r 取0~3这四个数时 r/4 返回0,r 取4~7这四个数时 r/4 返回1,……,r 取36~39这四个数时 r/4 返回9。因此返回0~9范围的每个数字的概率都是4/49,满足等概率。
10、海盗分金币
问题描述:
五个极其聪明的海盗抢到100颗宝石,每一颗宝石都一样大小和价值连城。他们决定以抽签投票的方式来分配这些宝石:有(1、2、3、4、5)五个号码,每人抽取一个。首先,由1号提出分配方案,然后大家举手表决,当且仅当超过半数的人同意时,按照他的分配方案 进行分配,否则将被扔进海里喂鲨鱼。如果1号死后,再由2号提出分配方案,规则如前所述,以此类推。
问:第一个海盗提出怎样的分配方案才能使自己的收益最大化?为什么?
解答:https://blog.csdn.net/guo_jia_liang/article/details/53957393
答案分析: 1号海盗分给3号1枚金币,4号或5号2枚金币,自己则独得97枚金币,即分配方案为(97,0,1,2,0)或(97,0,1,0,2)。
11、场景题:一个硬币,正面概率0.7,反面概率0.3,怎么掷能让两个人公平的喝到水
---->抛两次,先正后反A喝,先反后正B喝
某一次抛出硬币,正面向上的概率是p,反面向上的概率是1 - p,当p不等于0.5时,这两个事件的概率就不一样了。怎么能凑出等概率呢?还是要利用概率的加法和乘法法则。这里用乘法,也就是连续的独立事件。
连续抛两次硬币,正反面的出现有四种情况,概率依次为:
- 两次均为正面:p * p
- 第一次正面,第二次反面:p * (1 - p)
- 第一次反面,第二次正面:(1 - p) * p
- 两次均为反面:(1 - p) * (1 - p)
这不,中间两种情况的概率是完全一样的。于是问题的解法就是连续抛两次硬币,如果两次得到的相同则重新抛两次;否则根据第一次(或第二次)的正面反面情况,就可以得到两个概率相等的事件。
12、64匹马,8个跑道,选跑最快的4匹马需要比赛多少次
13、概率:两个人轮流抛硬币,先抛到正面的赢,问先抛的人赢的概率
设先抛先吃的概率为p1, 后抛先吃的概率为p2
那么有:
p1 = 1/2 + 1/2 * p2
p1 + p2 = 1
解方程可得,
p1 = 2/3
14、2个鸡蛋扔100层楼,求最坏情况下所需次数
- 1、只有一个小球怎么办,遍历,O(n)的时间复杂度
- 2、2个鸡蛋的时候具体来说,假设我们每10层楼扔第一个蛋,当在10x层扔坏,我们从10(x-1) 开始顺序扔到 10x。那么最坏情况下需要 10 + 9 = 19. 10 是第一个蛋的,9是第二个蛋的次数。按照这个方法,当第一个蛋达到最坏情况时,比第二个刚好多一。这样是平衡的。问题在于,如果第一个蛋在10就碎了,那么第二个蛋要扔9次,总共10次。两种最坏情况不平衡。也就是说,我们希望动态地调整扔蛋一的层数,使得(蛋一+蛋二)的总和平均。
那么考虑这个方法:假设蛋一第一次扔在X层,第二次扔在比原来高 X-1层,... 高2, 高1,使得X+(X-1)+...+2+1=100,那么最坏情况总的次数就是:
如果在第X层就碎,那么蛋二最坏情况要扔X-1,所以总数是 1+X-1=X
如果在X+(X-1)碎,那么蛋二最坏情况要扔 X-2,所以2+X-2=X
...1+2+3+4+......+14>100
解得 X = 14.
⼆分查找排除楼层的速度⽆疑是最快的, 那⼲脆先⽤⼆分查找, 等到只剩 1 个鸡蛋的时候再执⾏线性扫描, 这样得到的结
果是不是就是最少的扔鸡蛋次数呢?
很遗憾, 并不是, ⽐如说把楼层变⾼⼀些, 100 层, 给你 2 个鸡蛋, 你在 50层扔⼀下, 碎了, 那就只能线性扫描 1〜49 层了, 最坏情况下要扔 50 次。
如果不要「⼆分」 , 变成「五分」 「⼗分」 都会⼤幅减少最坏情况下的尝试次数。 ⽐⽅说第⼀个鸡蛋每隔⼗层楼扔, 在哪⾥碎了第⼆个鸡蛋⼀个个线性扫描, 总共不会超过 20 次。
最优解其实是 14 次。
15、两个很大的数据集存着URL,找到两个数据集共有的URL
参考链接:https://www.jianshu.com/p/f9f5b66f8a32
1)首先我们最常想到的方法是读取文件a,建立哈希表(为什么要建立hash表?因为方便后面的查找),然后再读取文件b,遍历文件b中每个url,对于每个遍历,我们都执行查找hash表的操作,若hash表中搜索到了,则说明两文件共有,存入一个集合。
(2)但上述方法有一个明显问题,加载一个文件的数据需要50亿*64bytes = 320G远远大于4G内存,何况我们还需要分配哈希表数据结构所使用的空间,所以不可能一次性把文件中所有数据构建一个整体的hash表。所以虽然可行,但是无法满足需求。
(3)针对上述问题,我们分治算法的思想。
step1:遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,...,a999,每个小文件约300M),为什么是1000?主要根据内存大小和要分治的文件大小来计算,我们就大致可以把320G大小分为1000份,每份大约300M(当然,到底能不能分布尽量均匀,得看hash函数的设计)
step2:遍历文件b,采取和a相同的方式将url分别存储到1000个小文件(记为b0,b1,...,b999)(为什么要这样做? 文件a的hash映射和文件b的hash映射函数要保持一致,这样的话相同的url就会保存在对应的小文件中,比如,如果a中有一个url记录data1被hash到了a99文件中,那么如果b中也有相同url,则一定被hash到了b99中)
所以现在问题转换成了:找出1000对小文件中每一对相同的url(不对应的小文件不可能有相同的url)
step3:因为每个hash大约300M,所以我们再可以采用(1)中的想法
16、多叉树的最长路径
二叉树的最长路径的进阶版:https://blog.csdn.net/wangbaochu/article/details/53870330
#include<iostream>
#include<vector>
#include<algorithm>
int maxDistance = 0;
std::vector<std::vector<int> > G(100001);//存储边信息
inline void AddEdge(int v, int s)//把结点v和s关联
{
G[v].push_back(s);
G[s].push_back(v);
}
int LastOrder(int pre, int cur)
{
int first = 0, second = 0;//最大值和次大值
for (size_t i = 0; i < G[cur].size(); ++i)
{
if (G[cur][i] == pre)//一直向下,不着重复的边
continue;
int temp = LastOrder(cur, G[cur][i]);//向下找
if (temp>first)
{
second = first;
first = temp;
}
else if (temp > second)
{
second = temp;
}
}
//注意:在以当前结点为根节点的子树中,最远两个结点的距离为first+second。
maxDistance = std::max(maxDistance, first + second);
return first + 1; //只返回当前子树的最大深度,父结点只需要知道子树的最大深度。
}
int main()
{
int N;
std::cin >> N;
int Ai, Bi;
for (int i = 1; i < N; ++i)
{
std::cin >> Ai >> Bi;
AddEdge(Ai, Bi);
}
LastOrder(0, 1);
std::cout << maxDistance;
return 0;
}
17、给定一个有向无环图,按拓扑排序的顺序输出节点
#使用循环进行拓扑排序
def topoSort(G):
#初始化计算被指向节点的字典
cnt=dict((u,0) for u in G.keys())
#若某节点被其他节点指向,该节点计算量+1
for u in G:
for v in G[u]:
cnt[v]+=1
#收集被指向数为0的节点,此时Q只有一个节点,即起始节点a
Q=[u for u in cnt.keys() if cnt[u]==0]
#记录结果
seq=[]
while Q:
s=Q.pop()
seq.append(s)
for u in G[s]:
cnt[u]-=1
if cnt[u]==0:
Q.append(u)
return seq
#有向无环图的邻接字典
G={
'a':{'b','f'},
'b':{'c','d','f'},
'c':{'d'},
'd':{'e','f'},
'e':{'f'},
'f':{}
}
20、抛硬币10000次,7000次朝上,求置信区间(中心极限定理)
这种属于二项分布的检验,会有2个数值方向。当概率p<0.626时,P(X<=70)>=0.9506(此时可以理解为概率过小,100次里不超过70次正面的可能性很大);当概率p>=0.775时,P(X>70)=1-0.0502=0.9498(此时可以理解为概率过大,100次里超过70次正面的可能性很大);所以当概率p在[0.626,0.775)区间内,在95%的置信区间(有95%把握)判断硬币出现正面的概率区间为[0.626,0.775)
可以得到对应的概率值,然后取计算Z值,可以得到95%置信区间对应的两边端点值
其中95置信区间下u一般为1.96