被面试官问了个对数组求交集,但是内存只能装载一个数组。然后想到数据结构的外部排序,但是当时没去看,现在补一下
对于这个面试问题的个人解法,可能不对,欢迎交流
- 先对两个数组用外部排序.使得有序
- 然后分别对两个有序数组掉一部分到内存,然后进行求交集,对两个有序数组求交集用双指针即可,具体实现自行了解
朋友的解法 感觉很妙!!!
- 将数组记录各自哈希到文件,用各自数组同哈希值的文件进行求交集,如果文件过大进行rehash
外部排序
基本方法
- 1.将外存n个记录分成若干长度为l的子文件或段,依次读入内存,利用有效的内部排序对其进行排序
- 2.将排序后得到的有序子文件重新写入外存 得到 m个归并段
- 3.对归并段进行多路归并
优化与分析
-
归并趟数: l o g k m {log_k{m}} logkm
-
普通K路归并段归并 每次取出一个最小元素要比较 k - 1 次,n个记录的内部归并段需要(n - 1) * (k - 1)
-
内部归并比较次数 ( k − 1 ) ( n − 1 ) l o g k m (k - 1)(n - 1){log_k{m}} (k−1)(n−1)logkm
-
多路平衡归并:将每次取出一个最小元素花的时间优化为 l o g 2 k {log_2{k}} log2k
-
使用败者树,叶子节点是归并段,值小的是胜利者,先调整,之后取出冠军,在从冠军对应的段中拿出新的数据(为了保证每个段都不空,给每个段尾加一个最大关键字,当冠军是最大关键字,说明已经归并完了)
-
于是K路归并比较次数 ( n − 1 ) l o g 2 m (n - 1){log_2{m}} (n−1)log2m
继续优化:减少m的大小
- 置换-选择排序
- 内排得到的归并段数目取决于工作区域大小 m = n/l,n为记录数,l为工作区可以装载的记录个数
- 为了减少归并段m,于是有了 置换-选择排序算法 来得到我们的归并段,原理是把做排序的时候把工作区拿来当类似缓冲的东西,通过败者树把原记录的文件有序的归并到多个其他文件。具体实现自行查阅.可以证明数据是随机数时,得到归并段平均长度是 内存工作区 w 的两倍,则归并段数目减半。通过扫雪机证明可以得到生成所有归并段时间复杂度是 O n l o g 2 w O{nlog_2{w}} Onlog2w
新的问题:置换-选择生成所得到的初始归并段,各个段长度不等对平衡归并的影响
- 解决:构造一棵哈夫曼树作为归并树(哈夫曼树:权值越大的结点离根结点越近,带权路径长度最短–>归并比较次数最小)