1.假如在抖音中发布视频时,可以选择带上位置信息,请设计一种数据结构或方案,用于存储检索位置信息(简化为平面坐标 x, y),以实现搜索附近视频的功能(如附近 3km)。
坐标范围检索,有四叉树、geohash 等几种标准解法。这道题本质并不是考察对高阶算法的掌握,而是想发掘在学习教材 btree 等基础二分思想后,能否进一步思考解出更复杂的问题;
另外考察思维灵活程度,看是否能变通的解决问题,如距离并没有限定必须是欧式距离;位置可以不精确,可以容忍有误差等。
- 方法 1,四叉树(QTree):在二叉树左、右节点的思想下,加入上、下、前、后等更多的方向,演进为四叉树和八叉树。高阶树比较超纲,相关实现省略
- 方法 2,geohash:把二维问题降为一维
如坐标(示例非标准 geohash,只是演示了思想):
(12, 345) -> (012, 345) -> "031425"
(13, 348) -> (013, 348) -> "031438"
(2, 789) -> (002, 789) -> "070829"
最终做字符串前缀匹配,可得 "031425" 和 "031438" 匹配到的位数最多,二者距离最近。求 3km 内的坐标,只需提前算出需匹配前几位即可,如匹配前 4 位,按 sql 表达是 LIKE '0314%' - 方法 3,变通距离为 方圆 3km(曼哈顿距离),即 deltaX = 1500, deltaY = 1500,通过数据库解决 Create table tb_name ( x int, y int ) 并添加索引。
假如原点是 (x0, y0),sql 如下:
WHERE (x > x0 - 1500) AND (x < x0 + 1500) AND (y > y0 - 1500) AND (y < y0 + 1500)
2.【多选】下列关于Join 运算不正确的是:
a. Nested Loop Join 不能使用索引做优化。
b. 如果左表太大,不能放入内存中,则不能使用 Hash Join。
c. 如果 Join 的一个输入表在 Join Key 上有序,则一定会使用 Sort Merge Join。
d. Broadcast Join 适用于一张表很小,另一张表很大的场景。
a、b、c;
Nested Loop Join 可以使用索引优化(Index Nested Loop Join);
如果左表太大,不能放入内存,可以先对量表做分区,再对每个分区做 Hash Join (Parititioned Hash Join);
如果Join的一个输入表在Join Key上有序,也可以 Hash Join,具体使用哪种 Join 方式取决于优化器的代价估计结果。
3.给定一个正整数数组 arrs 和整数 K ,请找出该数组内乘积小于等于 k 的连续的子数组的个数,算法时间复杂度o(n)。
双指针思想,对于每个right, 不断单调的移动left,直到满足需求。
4.【多选】绝大多数硬盘可以提供哪些写入保证?
a. 单个sector原子写入
b. 单个page原子写入
c. 硬盘顺序执行文件系统发送的操作
d. 以上都不可以
d;
本题主要考察对于一个数据密集型应用开发者(比如数据库开发者、文件系统开发者),在数据不能出错的前提下,可以依赖磁盘的哪些能力,事实证明几乎没有什么能力可以依赖。
在随时可能断电的情况下,大多数硬件能提供单个sector的原子性写入;但是也有少数硬件连单个sector的写入都无法保证;如果一个page对应多个sector,则单个page的完整写入总是无法得到保障。更加详细的情况可以查看这个讨论:crash - Are disk sector writes atomic?。
对于同时发给硬盘的多个操作(比如写多个不连续的sector),硬盘并不保证操作的顺序。结合其弱原子性,所以硬盘可能处在任何一个中间状态。这主要是由于机械硬盘的寻址优化策略和固态/机械硬盘的缓存策略导致的。
5.判断一棵二叉树是否是平衡二叉树。(平衡二叉树要求:树中节点左右子树树高差不超过1。)
解法一(常规解法):
分别求左右子树的深度,再进行判断、递归。(此方法会遍历一个节点多次,效率不高。)
bool IsBalanced_Solution1 (BinaryTreeNode * pRoot )
{
if ( pRoot == NULL ) return true ;
int left = TreeDepth ( pRoot->m_pLeft );
int right = TreeDepth ( pRoot->m_pRight );
int diff = left - right ;
if ( diff >1|| diff <-1 ) return false ;
return IsBalanced_Solution1 ( pRoot - >m_pLeft ) && IsBalanced_Solution1 ( pRoot - >m_pRight );
}
解法二(更高效的解法):
解决了遍历一个问题多次的问题。用后序遍历的方式遍历二叉树的每一个节点,在遍历到一个节点之前我们就已经遍历了它的左右子树。只要在遍历每个节点的时候记录深度,就可以一边遍历一边判断每个节点是不是平衡的。
bool IsBalanced_Solution2(BinaryTreeNode * pRoot)
{
int depth =0 ;
return IsBalanced(pRoot,& depth);
}
bool IsBalanced(BinaryTreeNode * pRoot, int* pDepth)
{
if (pRoot == NULL)
{
*pDepth=0 ;
return true ;
}
int left, right;
if (IsBalanced(pRoot-> m_pLeft,& left) && IsBalanced(pRoot-> m_pRight,& right))
{
int diff = left - right;
if (diff <=1&& diff>=-1 )
{
*pDepth = 1 + (left> right ? left : right);
return true ;
}
}
return false ;
}
5.【分布式文件处理,获取最多的 URL】如果有一个 20g 的日志文件,日志文件记录着用户访问过的 url,每一行为一个 url,给你一台 512M 的主机,找出出现次数最多的 10 个 url。
Top K算法:使用堆排序算法+大顶堆+10 个元素的数组。
- IP 地址最多有 2^32=4G 种取值情况,所以不能完全加载到内存中处理;
- 可以考虑采用“分而治之”的思想,按照 IP 地址的 Hash(IP)%1024 值,把海量 IP 日志分别存储到 1024 个小文件中。这样,每个小文件最多包含 4MB 个 IP 地址;
- 对于每一个小文件,可以构建一个 IP 为 key,出现次数为 value 的 Hash map,同时记录当前出现次数最多的那个 IP 地址;
- 可以得到 1024 个小文件中的出现次数最多的 IP,再依据常规的排序算法得到总体上出现次数最多的 IP;
6.Top K算法:使用堆排序算法+大顶堆+10 个元素的数组。
- IP 地址最多有 2^32=4G 种取值情况,所以不能完全加载到内存中处理;
- 可以考虑采用“分而治之”的思想,按照 IP 地址的 Hash(IP)%1024 值,把海量 IP 日志分别存储到 1024 个小文件中。这样,每个小文件最多包含 4MB 个 IP 地址;
- 对于每一个小文件,可以构建一个 IP 为 key,出现次数为 value 的 Hash map,同时记录当前出现次数最多的那个 IP 地址;
- 可以得到 1024 个小文件中的出现次数最多的 IP,再依据常规的排序算法得到总体上出现次数最多的 IP;
c;
Python 运行时并不会检查变量类型, 类型提示主要给第三方工具使用, 比如IDE可以据此给出方法/属性的自动补全建议。
7.给定包含 N 个任务 task 的数组 tasks 和整数 K,和一个可并发调用的执行函数 execute,要求实现以下逻辑:
- execute并发调用次数不超过10
- 以最快速度执行完所有task
- 使用golang
利用并发 + channel 缓冲区实现。
type Task int
func handle(tasks []Task, execute func(task Task)) {
wg := sync.WaitGroup{}
ch := make(chan struct{}, 10)
for i, _ := range tasks {
ch <- struct{}{}
wg.Add(1)
task := tasks[i]
go func() {
defer wg.Done()
// execute task
execute(task)
<-ch
}()
}
wg.Wait()
}
8.【多选】下面关于 HTTP1.x 的性能优化方式,正确的有:
a. 对域名进行分片,使得客户端可以创建更多的 TCP 连接提高请求并发度
b. 设置 Connection: Keep-Alive Header 保持长连接,减少 TCP 连接握手的开销
c. 利用 ServerPush 将页面上的关键静态资源直接推送到客户端,无需等待客户端请求
d. 将小的静态资源直接嵌入到页面中,减少 HTTP 请求次数
a,b,d;
ServerPush 为 HTTP2 协议才具备的能力,无法应用在 HTTP1.x 的优化中。
9.时间复杂度 O(nlogn) 空间复杂度 O(1) (非递归) 的限制下从单链表中找出第 K 大的节点 。
快排思路的逆向,快排递归思路是对序列持续拆成两个子序列处理,逆向过程就是每 2 个相邻的元素做合并排序,然后每相邻 4 个相邻的元素合并排序(因为之前一轮已经使这 4 个元素由两个长度为 2 的子序列构成),然后 8 个,16 个,直到覆盖整个原始序列。