分治算法

面试经典的海量数据处理(TOPK)问题—转载+个人见解!

不想你离开。 提交于 2019-12-08 17:48:35
常见问题: ① Top K问题 : 分治+Trie树/Hash_map+小顶堆 。采用Hash(x)%M将原文件分割成小文件,如果小文件太大则继续Hash分割,直至可以放入内存。 ② 重复问题 : BitMap位图 或 Bloom Filter布隆过滤器 或 Hash_set集合 。每个元素对应一个bit处理。 ③ 排序问题 : 外排序 或 BitMap位图 。分割文件+文件内排序+文件之间归并。 Top K问题: 1. 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。 ①分治:顺序读文件,对每个词x取Hash(x)%2000,按照该值存到2000个小文件中。每个文件是500k左右。如果有文件超过了1M则继续分割。O(N) ②Trie树/Hash_map:字符串用Trie树最好。对每个小文件,统计其中出现的词频。 O(N)*(平均字符长度),长度一般是常数,也就是O(N). ③小顶堆:用容量为100的小顶堆,以频率为value值插入,取每个文件现频率最大的100个词,把这100个词及相应的频率存入文件。最差O(N)*lg(100),也就是O(N). 注:2,3步骤合起来需要一轮磁盘存取过程。存入文件的个数可以缩减一下,因为主要开销在磁盘读取上,减少文件读取次数,可以在每个文件存取最大容量的字符数量,比如这道题1*(

面试必须掌握的十个海量数据问题及解决方案

只谈情不闲聊 提交于 2019-12-08 17:44:44
原文链接: BAT直通车-海量数据专题 更多精彩内容(BAT招聘、笔试、面试、技术),请访问 BAT直通车 题目 问题一:现有海量日志数据,要提取出某日访问百度次数最多的那个IP(可以将题干简化,假设日志中仅包含IP数据,也就是说待处理的文件中包含且仅包含全部的访问IP,但内存空间有限,不能全部加载,假设只有512MB) 解决方案: 这是一道典型的分治思想的题目,这种问题处理起来套路比较固定,对于大部分的数据量比较大的前提的问题而言,分治都是一个可选的解决方案,但不一定是最优的,解决方法基本划分为三步走: 第一:如何有效的划分数据 第二:如何在子集上解决问题 第三:如何合并结果 那么对于本问题就显得比较明显了: 首先解决如何划分,由于IP地地址范围从000.000.000.000~255.255.255.255共有2^32个大约4GB,那么我们可以通过取模的方式进行划分,直接将四段数据相连就得到一个IP对应的数字A,再对A模1024(模数取多少可以自己指定,保证每个小文件能被放到内存就好),这样大文件被划分成小文件了,并且相同的IP一定被划分到相同的文件中。 其次解决每个小文件中TOP1的问题: 这里可以用很多方式进行处理,比如你可以构造自己的HashMap,key为IP,value为当前出现次数,最后找到value最大的Key即为当前文件中出现次数最多的IP。

浅谈点分治

随声附和 提交于 2019-12-05 22:57:48
学习点分治这个算法也有一个多星期了,期间也看了很多大佬的博客,其中@粉兔 大佬对我的帮助非常大,建议想要深度学习这个算法的同学去看看他的博客,他对点分治的理解比我透彻许多,我这篇博客里也只是简单分析一下点分治的实现原理以及这样做为什么是正确的。 【算法学习】点分治——粉兔 【点分治算法步骤】   1.找根   为什么要找根呢?因为在分治过程中,从根节点去遍历全图和从叶子节点去遍历全图显然是两个量级的复杂度,从根节点遍历的时间复杂度大概在logn,根节点遍历最优性的证明网上很容易找到,我这里就不在赘述。   那么怎么去找根节点呢?——树上DP inline void findrt(int u,int fa){ siz[u]=1;dp[u]=0; for(int i=0;i<G[u].size();i++){ node v=G[u][i]; if(v.to==fa||vis[v.to])continue; findrt(v.to,u); siz[u]+=siz[v.to]; dp[u]=max(dp[u],siz[v.to]); } dp[u]=max(dp[u],sum-siz[u]); if(dp[u]<dp[rt])rt=u; }   2.分治——对每个子树的“根”分治计算答案   其实这里就是一个树的遍历过程,只不过只是对每个子树的根进行遍历;    inline void

二分与分治

。_饼干妹妹 提交于 2019-12-05 07:33:03
二分与分治 主讲人:彭海洋  博客: https://www.cnblogs.com/smallocean/ 二分 二分法 常用来查找 单调序列 或 单调函数 上的答案. 当问题的答案具有 单调性 时,可以考虑通过二分求解. 先思考一个简单问题 A心里想一个1-1000之间的数,B来猜,B可以问问题,A只能回答是或者不是,怎么猜才能问的问题次数最小? 是1吗?是2吗?……平均要问500次 大于500吗?大于750吗?大于625吗?……每次缩小猜测范围到上次的一半,只需要10次( \(log_21000\) ) 这就是二分法的一个简单运用. 二分的实现方法有很多种,对于整数集合上的二分,需要注意终止边界,左右区间取舍时的开闭,避免漏掉答案或造成死循环;对于实数域上的二分,需要注意精度的取舍. 这里推荐大家采用<算法进阶指南>上的二分写法. 整数集合上的二分 下面代码摘自<算法进阶指南>P24  亲测好用,比赛时一直采用这种二分写法. 在单调递增序列 \(a\) 中查找$\geq x $的数中 最小 的一个 while(l<r){ int mid = (l+r)>>1;/*右移运算 相当于除2并且向下取整*/ if(a[mid]>=x) r=mid; else l=mid+1; } return a[l]; 在单调递增序列 \(a\) 中查找 \(\leq x\) 的数中 最大 的一个

分治的逻辑学描述

放肆的年华 提交于 2019-12-05 06:56:11
分治的逻辑学描述 https://bigdata.oden.utexas.edu/project/divide-conquer-methods-for-big-data-analytics/ 问题分解: P = P1 + P2 +..PN 问题求解: F(P) = F(P1 + P2 +..PN)= F1(P1) + F2(P2) +..Fn(PN); 设问题的解决方案为 Y = F ( X ),其中 X 为问题的输入集合, F 是问题的处理方法, Y 是问题的解。将 X 拆分成多个子集以后,设这些子集的集合为 X={ Xi ∣ i ≥1, i > n },则 X 满足以下条件: X 1 ++ X 2 ++...++ Xn = X 在拆分以后,分治法对每个子集独立处理,即 Yi = F ( Xi ),其中 Xi ∈X,则最终结果为: Y =Marge( Y 1 , Y 2 ,..., Yn ) 其中,Merge函数用于将所有的子结果 Yi 进行合并的函数。 https://blog.csdn.net/weixin_43145361/article/details/89510447 分治策略一般性描述 把上面的设计思想加以归纳,可以得到分治算法的一般描述.设P是待求解的问题,|P|代表问题的输入规模,一般的分治算法Divide-and-Conquer伪码描述如下: 算法 Divide

plan

依然范特西╮ 提交于 2019-12-04 13:11:52
算法: 贪心(带反悔) , 分治(归并) 图论: 欧拉路 ,A*搜索, 并查集按秩合并 , tarjan , 二分图 ,奇偶环 数论: 树上差分 dp: LIS , LCIS ,背包, 数据结构优化dp ,斜率优化dp, 单调队列优化dp ,概率期望dp, 坐标dp ,树上dp 数据结构:01trie,分块,分治? 至少要会一种平衡树吧?防止csps考 数学: 高斯消元 ,矩阵乘法, 欧拉函数 ,crt? 字符串:kmp,AC自动机,trie 来源: https://www.cnblogs.com/Juve/p/11742343.html

[HDU4867]Xor (线段树分治+类数位dp)

烂漫一生 提交于 2019-12-04 04:48:40
[HDU4867]Xor (线段树分治+类数位dp) 提供一种 \((m+n) log a log m\) 带有常数约 \(\frac{1}{log n}\) 的算法 处理询问,将后来加入的数算进序列中,则每个数 \(a_i\) 都有一段出现的区间 \([L,R]\) 离线询问后,我们考虑用线段树分治将这些数加入到询问区间上 由于最多只有5000个询问,事实上这些数在线段树上覆盖的区间最多只有 \(10000logm\) 个,并且有着极其不满的常数(因为每个位置上的数都由多段区间组合而来,总长为 \(m\) ,或者你可以觉得我在放屁) 如果直接处理每个数的贡献,那么这个 \(dp\) 是 \(a*a\) 转移的 然而事实上我们存在一种 \(a*loga\) 的转移方法 对于一个数 \(x\) ,如果我们取 \(y \leq x\) 时,最高位为 \(0\) ,则后面的位均可以随便取 换句话说,对于每一个前 \(k\) 位相同的集合,它们都能够转移到它们之间的任何一个,可以直接累和 同样的,考虑在第 \(k\) 位出现一个 \(x\) 在该位为 \(1\) , \(y\) 为 \(0\) ,都具有类似的转移性质 最后写出来跟数位 \(dp\) 一个样子。。。 (真不行你可以试试某变换,但是我不会!) 这样的情况个数即这个数 \(1\) 位的个数,这样的个数期望情况下可以看做常数。。。

分治算法(用C++、lua实现)

纵然是瞬间 提交于 2019-12-04 00:27:42
目录 1、算法 2、C++实现 3、lua实现 本文为 分治算法 的代码实现。 作者水平比较差,有错误的地方请见谅。 1、算法 分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。 可使用分治法求解的一些经典问题 (1)二分搜索 (2)大整数乘法 (3)Strassen矩阵乘法 (4)棋盘覆盖 (5)合并排序 (6)快速排序 (7)线性时间选择 (8)最接近点对问题 (9)循环赛日程表 (10)汉诺塔 此例子为使用分治法,来求一个数组中的最大连续子段。 连续子段为:-23,18,20,-7,12 最大值为:-23+18+20-7+12 = 43 2、C++实现 暴力求解 #include <iostream> using namespace std; int main() { //求最大的连续子段和 int sumArray[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7}; int length = sizeof(sumArray)/sizeof(sumArray[0]); int startIndex = 0; int

数据结构与算法思考

限于喜欢 提交于 2019-12-03 16:59:40
数据结构和算法的理论来源是优化和组合数学,是代数学。包括分治、动态规划的思想都是和第一、第二数学归纳法以及(高阶)马尔可夫模型相关的。所以计算问题深入地去理解的话还是免不了需要数学方面的角度,数学方面的背景。但也有一些是计算机方面的思想,比如双指针的解法。还有就是信息的思想,有些如回文串计算等,都是要怎么借助回文等带来的信息。大的方面来说,有那么几种思想(待完善): 分治、动规、贪心、近似、启发式搜索... 双指针、快慢指针、滑动窗口... 回文等自身结构带来信息... 来源: https://www.cnblogs.com/limancx/p/11803706.html

[点分治系列] 静态点分

这一生的挚爱 提交于 2019-12-03 15:16:00
没错...我就是要讲点分治。这个东西原本学过的,当时学得不好...今天模拟赛又考这个东西结果写不出来。 于是博主专门又去学了学这个东西,这次绝对要搞懂了... 【复赛倒计时:11天】 ——正片开始—— 点分是一个用来解决树上路径问题、距离问题的算法。说直接点其实就是分治思想在树上的体现和应用。 首先是分治的方法,既然是树上问题,自然就是对树进行分治。 我们对于一棵树,选定一个点作为它的根,将这个点与其子树拆开,然后对它的子树也进行相同的操作,然后利用子树统计答案。一般来说,点分更像一种思想,而不是一个算法,当然你也可以把它当算法来学。 关于怎么选根来分树,其他dalao的博客已经讲得非常清楚仔细了,每次选定一棵树的重心,然后分它,这样可以做到 O(nlogn)的优秀时间复杂度。 关于求重心,做法就是一个size统计。 这里还是介绍一下吧(很多博客都只贴一个代码...): 对于一个节点x,若我们把其删除,原来的树就会变成若干部分,我们设maxsubtree(x)表示删除x后剩下的最大子树的大小,若我们找到一个x,使得maxsubtree(x)最小,x就是这棵树的重心。 给出求重心的代码: void getroot(int x,int fa){ siz[x]=1;son[x]=0; for(int i=head[x];i;i=nxt(i)){ int y=to(i); if(y==fa