This commit is contained in:
nonstriater
2014-05-08 15:01:56 +08:00
parent 3954690000
commit 04a7c20142
15 changed files with 847 additions and 57 deletions
+27
View File
@@ -0,0 +1,27 @@
void heap_build(int *a,int length){
}
// 插入
void heap_insert(int *a,int v){
}
//删除,删除的元素放在value指向的内存中
void heap_delete(int *a,int *value){
}
+67
View File
@@ -0,0 +1,67 @@
多线程有几种实现方法,都是什么?
多线程同步和互斥有几种实现方法,都是什么?
互斥量与临界区比较
生产者消费者问题
多线程同步问题
主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。子线程接收参数 -> sleep(50) -> 全局变量++ -> sleep(0) -> 输出参数和全局变量。
要求:
1.子线程输出的线程序号不能重复。
2.全局变量的输出必须递增。
读者写者问题
这也是一个非常经典的多线程题目,题目大意如下:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者读时写者也不能写。
编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。
有四个线程1、2、3、4。线程1的功能就是输出1,线程2的功能就是输出2,以此类推.........现在有四个文件ABCD。初始都为空。现要让四个文件呈如下格式:
A:1 2 3 4 1 2....
B:2 3 4 1 2 3....
C:3 4 1 2 3 4....
D:4 1 2 3 4 1....
请设计程序。
+78 -29
View File
@@ -13,42 +13,33 @@
在a【i,n】中最小的元素和 a[i]交换位置。空间复杂度O(1),时间复杂度 O(n^2) 在a【i,n】中最小的元素和 a[i]交换位置。空间复杂度O(1),时间复杂度 O(n^2)
### 堆排序
利用堆这种数据结构设计的一种排序算法
将待排序数列看做一颗完全二叉树的存储结构
堆总是一颗完全树,
堆也被称为`优先队列`,
堆分小根堆和大根堆
堆: 任一节点小于(或大于)其所有的孩子节点,如果是大于所有孩子节点,这就是一颗大根堆,也就是根节点是堆上的最大值;如果节点小于所有的子节点,这就是一颗小跟堆,也即是根节点是堆上所有节点的最小值。
按层遍历堆既可以得到一个排好顺序的序列。
堆排序主要时间花在建堆期间,找数列中最大树只需要O(1)时间复杂度
### 快速排序 ### 快速排序
1. 从数列中选择一个元素,作为基准 pivot
2. 重排数列,比 pivot 小得排左边,比pivot大的排右边,相等的随便 递归一次,pivot 左边都比它小,右边都比它大。这是递归,分治的思想。
对 A[p...r] :
1. 分解:A[p..q-1] A[q+1..r],使得 A[p...q-1]<A[q]<A[q+1..r]
2. 解决:递归调用 快排,,对子数组A[p..q-1],A[q+1..r]排序
3. 合并(子问题相互独立的,因此用分治算法就可以了)
具体步骤:
1. 从数列中选择一个元素,作为基准 pivot。通常去分区的第一个或最后一个
2. 重排数列,比 pivot 小得排左边,比pivot大的排右边,相等的随便。 一句话就是`挖坑填数`
3. 递归的,使用相同的方式,重排左右两边的子序列 3. 递归的,使用相同的方式,重排左右两边的子序列
递归一次,pivot 左边都比它小,右边都比它大 扫描过程分2种:
这是递归,分治的思想 1. 挖坑排序,2头向中间扫描,先从后向前找,再从前向后找。
2. 单向扫描
平均复杂度 O(n*logn) 平均复杂度 O(n*logn)
最坏O(n^2)
空间复杂度
快速排序是对冒泡排序的改进,划分交换排序。
@@ -78,12 +69,70 @@
### 归并排序merge
分治算法
2个有序数组的合并操作是O(n)的复杂度
因此我们可以将无序的数组,分成2个子数组分别排序,然后再merge,依次类推
归并排序的步骤
1. 分解 , 将一个数组分成2个子数组
2. 解决 , 将2个子数组都排好序,然后 merge 2个有序数组
3. 合并
### 堆排序
利用堆这种数据结构设计的一种排序算法
先来了解下 `堆` 结构
堆分小根堆和大根堆
堆: 任一节点小于(或大于)其所有的孩子节点,如果是大于所有孩子节点,这就是一颗大根堆,也就是根节点是堆上的最大值;如果节点小于所有的子节点,这就是一颗小跟堆,也即是根节点是堆上所有节点的最小值。
堆也被称为`优先队列`
堆总是一颗完全树
`堆用数组来存储`。 i节点的父节点就是(i-1)/2,左右子节点小标是 2i+1,2i+2
堆的操作有:
建堆
插入:都是插入到数组最后,然后再调整满足堆次序
删除:删除总是发生在 A[0]处,也就是只删除根节点
这样难怪`堆`被称为 `优先队列`。插入和删除分别在 数组尾部和 头部,只是需要再次调整以满足堆次序。
`堆`的应用场景有:
优先队列 如iOS中的NSOperationQueue 就是维护一个优先队列
堆排序
我们来看看如何使用`堆` 来做排序。
1.将待排序数列看做一颗完全二叉树的存储结构
2.`堆化数组`,结束后,根a[0]变成了最小的值(小根堆)
3.取a[0]值,然后对`堆`做删除操作,此时,`堆`会重新 `堆化数组`,a[0]又是下一个最小的值
4. 循环3,就可以按从小到大的顺序取出所有数组元素。
堆排序主要时间花在建堆期间和`堆化数组`,找数列中最大树只需要O(1)时间复杂度
### 希尔排序 ### 希尔排序
递减增量排序算法,对`插入排序`的改进
### 归并排序
Binary file not shown.
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>com.apple.xcode.dsym.a.out</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>dSYM</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
+76 -8
View File
@@ -1,6 +1,7 @@
#include "stdio.h" #include "stdio.h"
#include "stdlib.h"
#include "time.h"
// 插入排序 // 插入排序
void insert_sort(int *a,int length){ void insert_sort(int *a,int length){
@@ -78,39 +79,106 @@ void bubble_sort(int *a, int length){
// 快速排序 // 快速排序
void fast_sort(int *a, int length){ // 挖坑填数,2边向中间扫描
int partion(int *a, int start,int end){
int i=start,j=end;
int tmp = a[i]; // 这里要做越界检查
while(i<j){
// 从后向前扫描,找到第一个小于tmp的值,来填a[i]
while(i<j && a[j]>=tmp){
j--;
}
if (i<j)//找到了,这时候a[j]为坑
{
a[i++] = a[j];
}
// 从左向右扫描,找一个大于 tmp的 数, 去填坑a[j]
while(i<j && a[i]<tmp){
i++;
}
if (i<j)
{
a[j++]=a[i];
}
}
//扫描完成后,i==j
a[i]=tmp;
return i;
}
void quicksort(int *a, int left, int right){
if (left<right)//加上这个,不然有死循环,造成堆栈溢出
{
int i = partion(a,left,right);
quicksort(a,left,i-1);
quicksort(a,i+1,right);
}
} }
// 归并排序
void merge_sort(int *a, int length){
}
// 堆排序 // 堆排序
void heap_sort(int *a, int length){ void heap_sort(int *a, int length){
} }
#define Max_Number 50000
int main(){ int main(){
int a[] = {4,87,2,32,5,41,2,9}; //int a[] = {4,87,2,32,5,41,2,9};
//insert_sort(a,sizeof(a)/sizeof(int)); // 准备5000个数
//select_sort(a,sizeof(a)/sizeof(int)); int a[Max_Number];
bubble_sort(a,sizeof(a)/sizeof(int)); for (int i = 0; i < Max_Number; ++i)
{
a[i]=rand()%Max_Number;
}
clock_t start,finish;
start = clock();
//quicksort(a,0,sizeof(a)/sizeof(int)-1); // 0.01s
//insert_sort(a,sizeof(a)/sizeof(int)); // 3.85s
//select_sort(a,sizeof(a)/sizeof(int)); // 5.3s
//bubble_sort(a,sizeof(a)/sizeof(int)); // 12.5s
finish = clock();
printf("after sort:\n"); printf("after sort:\n");
for (int i = 0; i < sizeof(a)/sizeof(int); ++i) for (int i = 0; i < sizeof(a)/sizeof(int); ++i)
{ {
printf(" %d ",a[i]); printf(" %d ",a[i]);
} }
printf("time eclipse: %.4f sec\n", (double)(finish-start)/CLOCKS_PER_SEC); // CLOCKS_PER_SEC 1000 clock()是毫秒
return 0; return 0;
} }
+64
View File
@@ -0,0 +1,64 @@
>数学是科学的皇后
>数学是对自然界事实的总结和归纳,又是抽象思考的结果
>数学是上帝描写自然的语言 —— 伽利略
### 初等数学
也就是大学以前学习的数学内容。主要以研究常量为主
###微积分
微积分起源于资本主义工业革命,工业的发展需要精确计算各种运动如机械运动,天体运动,流体与气体运动的规律性。许多问题需要解决,归纳有下:
1. 求即时速度
2. 求曲线的切线问题
3. 求函数的最大值和最小值问题
4. 求曲线长,曲线围成的面积,曲面围成的体积,物体的重心
微积分研究的对象是变量(或函数)
微积分包括`微分` 和 `积分` ,微分和积分互为逆运算。
*微分*: `极限理论` 、 `导数` 、`微分` 是一套关于关于变化率的理论,将函数,速度,加速度,曲线,曲率用一套通用符号表示。
极限理论是微积分的基础
*积分*: 内容有`定积分 ` 和 `不定积分` , 是计算面积和体积的一套理论方法
###线性代数
`线性代数`是数学的另一个分支,研究对象是`向量` `矩阵` `线性变换` `线性方程组`
###概率论与数理统计
###离散数学
语言信息处理
互联网技术
数据挖掘和机器学习
推荐阅读
《数学之美》 吴军
+149 -4
View File
@@ -1,25 +1,170 @@
### 枚举迭代法 ### 迭代法
是一种不断用旧值递推新值的过程,分精确迭代和近视迭代。是用来求方程和方程组近似根的方法。
迭代变量
迭代关系, 迭代关系选择不合理,会导致迭代失败
迭代过程控制,也就是迭代什么时候结束,不能无休止进行下去
### 穷举搜索法(枚举)
对可能的解的众多候选按照某种顺序逐一枚举和检验
### 递推法
利用问题本身具有的一种递推关系求问题解的一种方法。
如
阶乘计算
### 递归 ### 递归
递归是一种设计和描述算法的有力工具。
递归算法执行过程分 `递推` 和 `回归` 两个阶段
在 `递推` 阶段,将大的问题分解成小的问题
在 `回归` 阶段,获得最简单问题的解后,逐级返回,依次得到稍微复杂情况的解,知道获得最终的结果
1) 确定递归公司
2) 确定边界条件
如
斐波那契数列 fib(n)=fib(n-1)+fib(n-2)
阶乘计算
梵塔问题 (三根针1,2,3表示,1号从小到大n个盘子,先要都移到3号上,不能出现大盘压小盘,找出移动次数最少的方案)
快速排序
递归运行效率较低,因为有函数调用的开销,递归多次也可能造成栈溢出。
### 回溯法
也叫 `试探法`。 是一种选优搜索法,按照选优条件搜索,当搜索到某一步,发现原先选择并不优或达不到目标,就退回重新选择。
一般步骤
1. 针对问题,定义解空间( 这时候解空间是一个集合,且包含我们要找的最优解)
2. 组织解空间,确定易于搜索的解空间结构,通常组织成`树结构` 或 `图结构`
3. 深度优先搜索解空间,搜索过程中用剪枝函数避免无效搜索
回溯法求解问题时,一般是一边建树,一边遍历该树;且采用非递归方法。
以八皇后问题为例
> 8x8的国际象棋棋盘上放置8个皇后,使得任何一个皇后都无法直接吃掉其他的皇后。任意2个皇后都不能处于同一个 横线,纵线,斜线上。
分析
任意2个皇后不能同一行,也就是每个皇后占据一行,通用的,每个皇后也要占据一列
一个斜线上也只有一个皇后
### 动态规划
案例:
迷宫问题
### 贪心算法
不追求最优解,只找到满意解
案例
找回零钱问题
装箱问题
赫夫曼编码
### 分治算法 ### 分治算法
将一个难以直接解决的大问题,分割成一些规模较小的相同问题,各个击破,分而治之。
分治算法常用递归实现
### 回溯法 1) 问题缩小的小规模可以很容易解决
2) 问题可以分解为规模较小相同问题
3) 子问题的解可以合并为该问题的解
4) 各个子问题相互独立,(如果这条不满足,转为`动态规划`求解)
分治法的步骤:
1. 分解
2. 解决
3. 合并
### 贪心算法 以 `大整数乘法` 为例
如 26542123532213598*345987342245553677884
案例:
快速排序
归并排序
最大子数组和
### 动态规划
复杂问题不能分解成几个子问题,而分解成一系列子问题;
动态规划算法的关键在于解决冗余,以空间换时间的技术,需要存储过程中的各种状态。可以看着是`分治算法`+`解决冗余`
使用动态规划算法的问题的特征是`子问题的重叠性`,否则动态规划算法不具备优势
####基本步骤
1. 划分问题
2. 选择状态
3. 确定决策并写出状态转移方程
4. 写出规划方程
案例
最短路径
### 总结
贪心法、分治法、动态规划都是将问题归纳为根小的、相似的子问题,通过求解子问题产生全局最优解。
`贪心法`
`分治法`
`动态规划`
+7 -3
View File
@@ -34,8 +34,11 @@ a = a - b;
不开辟用于交换数据的临时空间,如何完成字符串的逆序(在技术一轮面试中,有些面试官会这样问) 不开辟用于交换数据的临时空间,如何完成字符串的逆序(在技术一轮面试中,有些面试官会这样问)
用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。 用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。
按单词逆序 。如 “this is a sentence。” 逆序就是 “.sentence a is this”
按单词逆序 。如 “this is a sentence。” 逆序就是 “.sentence a is this”
思路:
1. 先按单词逆序,
2. 对1)的结果,对整个句子逆序
判断字符粗是否是回文 判断字符粗是否是回文
@@ -93,6 +96,9 @@ hash表长度 256,字符直接作为key值
BOOL isBrother(const char *first,const char *secd) BOOL isBrother(const char *first,const char *secd)
思路一: 循环匹配 指数级复杂度
思路二: 利用质数,平方和比较,但这样必须是2个串的长度要一样,需要的空间比较大,最多256个字节
@@ -116,8 +122,6 @@ void statistics(FILE *fd)
char *compress(const char *src,char *dest) char *compress(const char *src,char *dest)
给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。
Binary file not shown.
+189 -13
View File
@@ -30,6 +30,76 @@ void revert(const char *source,char *dest){
} }
// 按单词逆向 如 this is a sentence 输出 sentence a is this
// 先按单词逆序,再对整个句子逆序
void revertByWord(const char *src, char *dest){
// 先拷贝字符串到dest
char *tmp = dest;
char *dest_end;
while(*src){
*tmp++ = *src++;
}
*tmp='\0';
dest_end = tmp-1; //这里留作下面用;
tmp = dest;
// 扫描一遍 dest,按单词逆序
char *start=dest-1,*end=dest;
while(*tmp){
if (*tmp == ' ')//逆序
{
start++;
//对单词逆向
while(end>start){
*start = *start + *end;
*end = *start - *end;
*start = *start - *end;
start++;
end--;
}
start=tmp;// 如果句子中又多个连续空格,也会对空格做逆序,浪费cpu
}
tmp++;
end=tmp-1;
}
if (!*tmp)//最后一个字符,对最后一个单词逆序
{
start ++;
while(end>start){
*start = *start + *end;
*end = *start - *end;
*start = *start - *end;
start++;
end--;
}
}
tmp = dest;
// 逆向整个句子
while(dest_end>tmp){
*tmp = *tmp + *dest_end;
*dest_end = *tmp - *dest_end;
*tmp = *tmp - *dest_end;
tmp++;
dest_end--;
}
}
// 回文,或 对称 // 回文,或 对称
BOOL symmetrical(const char *scr,int length){ BOOL symmetrical(const char *scr,int length){
@@ -92,7 +162,7 @@ char first_appear_only_once(const char *source){
// 最大对称子串的长度 // 最大对称子串的长度
int max_symmetrical_char_length(const char *scr){ int max_symmetrical_char_length(const char *scr){
return 0; return 0;
@@ -107,7 +177,6 @@ int continumax(char *outputstr,char *intputstr){
} }
// 最大连续递增数字串 // 最大连续递增数字串
int continuousnum(const char *input, char *output){ int continuousnum(const char *input, char *output){
@@ -156,41 +225,126 @@ char *delete(char *source,const char *del,BOOL greedy){
// 字符串拷贝 // 字符串拷贝
char *strcpy(char *strDest, const char *strSrc){ char *strcpy(const char *src,char *dest){
char *tmp = dest;
while(*src){
return NULL; *dest++ = *src++;
}
*dest = '\0';
return tmp;
} }
// aabcd & bcad , abc&bca
BOOL isBrother(const char *first,const char *secd){ BOOL isBrother(const char *first,const char *secd){
return YES; return YES;
} }
// 字符串压缩
char *compress(const char *src,char *dest){
return NULL;
// 字符串就地压缩 如 “aaabcc” “a3bc2”
/*
注意连续字符大于9的情况
*/
void compress(char *src){
if (*src == NULL)
{
return;
}
int count = 1;
char *back=src,forward=src+1;
while(*forward){
count = 1;
if (*forward == *back)
{
count++;
forward++;
}else{ //
if (count!=1)
{
back++;
*back=count;
}
}
}
*++back = '\0';
} }
// 删除其中的数字并压缩
// 删除其中的数字并压缩,2个指针,一前一后
void filternum(char *src){
if (!*src)
{
return;
}
char *back=src ,*forward=src;
while(*forward){
if (*forward >= '0' && *forward <= '9')
{
forward++;
}else{
*back++ = *forward++;
}
}
*back='\0';
// 局部变量释放,不能作为返回值
}
// 删除字符串中的数字,不改变原串,同上
char *filternumber(const char*src,char *dest){ char *filternumber(const char*src,char *dest){
return NULL; return NULL;
} }
// 删除指定字符 // 删除指定字符,不开辟空间
char *delete_character(char *src , char target){ void delete_character(char *src , char target){
return NULL; char *back=src,*forward = src;
while(*forward){
if (*forward == target)
{
forward++;
}else{
*back++ = *forward++;
}
}
*back = '\0';
} }
@@ -223,6 +377,14 @@ int main(int argc, char const *argv[])
printf("%s after reverse : %s\n", reverse,reversed); printf("%s after reverse : %s\n", reverse,reversed);
// 按单词逆序
char *sentence = "this is a sentence!"; // "this is a sentence"
char dest[50];
revertByWord(sentence,dest);
printf("%s revert by word: %s\n",sentence,dest );
// 回文 // 回文
char symmetri[] = "dacbcacbcad"; char symmetri[] = "dacbcacbcad";
printf("%s : %d\n", symmetri, symmetrical(symmetri,sizeof(symmetri)-1) ); printf("%s : %d\n", symmetri, symmetrical(symmetri,sizeof(symmetri)-1) );
@@ -235,6 +397,20 @@ int main(int argc, char const *argv[])
printf("%s第一次出现一次的字符是:%c\n",symmetri,first_appear_only_once(symmetri) );// '0' printf("%s第一次出现一次的字符是:%c\n",symmetri,first_appear_only_once(symmetri) );// '0'
printf("%s第一次出现一次的字符是:%c\n",unsymmetrical,first_appear_only_once(unsymmetrical) ); // 'c' printf("%s第一次出现一次的字符是:%c\n",unsymmetrical,first_appear_only_once(unsymmetrical) ); // 'c'
// 过滤数字
char *charnumber = "this is 58tongcheng12haha!";
//filternum(charnumber);
printf(" after filter: %s\n", charnumber );
// 过滤指定字符
//delete_character(charnumber,'i'); // 居然会出 bus error!!!!!????
printf("after filter:%s\n", charnumber);
return 0; return 0;
} }
+165
View File
@@ -7,6 +7,17 @@
请用5分钟时间,找出重复出现最多的前10条。 请用5分钟时间,找出重复出现最多的前10条。
文件中的内容排序并去重复
一个文件中包含了1亿个随机整数,如何快速的找到最大(小)的100万个数字?(时间复杂度:O(n lg k))
分析: 堆排序
3.2 在O(n lg k)时间内,将k个排序表合并成一个排序表,n为所有有序表中元素个数。
【解析】取前100 万个整数,构造成了一棵数组方式存储的具有小顶堆,然后接着依次取下一个整数,如果它大于最小元素亦即堆顶元素,则将其赋予堆顶元素,然后用Heapify调整整个堆,如此下去,则最后留在堆中的100万个整数即为所求 100万个数字。该方法可大大节约内存。
3.大量的URL字符串,如何从中去除重复的,优化时间空间复杂度 3.大量的URL字符串,如何从中去除重复的,优化时间空间复杂度
@@ -95,3 +106,157 @@
那若面试官问有没有更高效率的解法之类的?这时,你可以优化一下,但是这个速度很快,hash函数,速度很快,他肯定会问,你如何设计,用bitmap也行。 那若面试官问有没有更高效率的解法之类的?这时,你可以优化一下,但是这个速度很快,hash函数,速度很快,他肯定会问,你如何设计,用bitmap也行。
1. 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
方案1:可以估计每个文件安的大小为50G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。
s 遍历文件a,对每个url求取clip_image002,然后根据所取得的值将url分别存储到1000个小文件(记为clip_image004)中。这样每个小文件的大约为300M。
s 遍历文件b,采取和a相同的方式将url分别存储到1000各小文件(记为clip_image006)。这样处理后,所有可能相同的url都在对应的小文件(clip_image008)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
s 求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
方 案2:如果允许有一定的错误率,可以使用Bloom filter,4G内存大概可以表示340亿bit。将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。
2. 有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。
方案1:
s 顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件(记为clip_image010)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
s 找一台内存在2G左右的机器,依次对clip_image010[1]用hash_map(query, query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(记为clip_image012)。
s 对clip_image012[1]这10个文件进行归并排序(内排序与外排序相结合)。
方案2:
一般query的总量是有限的,只是重复的次数比较多而已,可能对于所有的query,一次性就可以加入到内存了。这样,我们就可以采用trie树/hash_map等直接来统计每个query出现的次数,然后按出现次数做快速/堆/归并排序就可以了。
方案3:
与方案1类似,但在做完hash,分成多个文件后,可以交给多个文件来处理,采用分布式的架构来处理(比如MapReduce),最后再进行合并。
3. 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
方案1:顺序读文件中,对于每个词x,取clip_image014,然后按照该值存到5000个小文件(记为clip_image016) 中。这样每个文件大概是200k左右。如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,知道分解得到的小文件的大小都不超过1M。对 每个小文件,统计每个文件中出现的词以及相应的频率(可以采用trie树/hash_map等),并取出出现频率最大的100个词(可以用含100个结点 的最小堆),并把100词及相应的频率存入文件,这样又得到了5000个文件。下一步就是把这5000个文件进行归并(类似与归并排序)的过程了。
4. 海量日志数据,提取出某日访问百度次数最多的那个IP。
方案1:首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有clip_image018个 IP。同样可以采用映射的方法,比如模1000,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map进 行频率统计,然后再找出频率最大的几个)及相应的频率。然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。
5. 在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。
方案1:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存clip_image020内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。
方案2:也可采用上题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。
6. 海量数据分布在100台电脑中,想个办法高校统计出这批数据的TOP10。
方案1:
s 在每台电脑上求出TOP10,可以采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆)。比如求TOP10大,我们首先取前 10个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元 素就是TOP10大。
s 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,再利用上面类似的方法求出TOP10就可以了。
7. 怎么在海量数据中找出重复次数最多的一个?
方案1:先做hash,然后求模映射为小文件,求出每个小文件中重复次数最多的一个,并记录重复次数。然后找出上一步求出的数据中重复次数最多的一个就是所求(具体参考前面的题)。
8. 上千万或上亿数据(有重复),统计其中出现次数最多的钱N个数据。
方案1:上千万或上亿的数据,现在的机器的内存应该能存下。所以考虑采用hash_map/搜索二叉树/红黑树等来进行统计次数。然后就是取出前N个出现次数最多的数据了,可以用第6题提到的堆机制完成。
9. 1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
方案1:这题用trie树比较合适,hash_map也应该能行。
10. 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。
方 案1:这题是考虑时间效率。用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平准长度)。然后是找出出现最频繁的前10个 词,可以用堆来实现,前面的题中已经讲到了,时间复杂度是O(n*lg10)。所以总的时间复杂度,是O(n*le)与O(n*lg10)中较大的哪一 个。
11. 一个文本文件,找出前10个经常出现的词,但这次文件比较长,说是上亿行或十亿行,总之无法一次读入内存,问最优解。
方案1:首先根据用hash并求模,将文件分解为多个小文件,对于单个文件利用上题的方法求出每个文件件中10个最常出现的词。然后再进行归并处理,找出最终的10个最常出现的词。
12. 100w个数中找出最大的100个数。
方案1:在前面的题中,我们已经提到了,用一个含100个元素的最小堆完成。复杂度为O(100w*lg100)。
方案2:采用快速排序的思想,每次分割之后只考虑比轴大的一部分,知道比轴大的一部分在比100多的时候,采用传统排序算法排序,取前100个。复杂度为O(100w*100)。
方 案3:采用局部淘汰法。选取前100个元素,并排序,记为序列L。然后一次扫描剩余的元素x,与排好序的100个元素中最小的元素比,如果比这个最小的要 大,那么把这个最小的元素删除,并把x利用插入排序的思想,插入到序列L中。依次循环,知道扫描了所有的元素。复杂度为O(100w*100)。
13. 寻找热门查询:
搜 索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较 高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询 串,要求使用的内存不能超过1G。
(1) 请描述你解决这个问题的思路;
(2) 请给出主要的处理流程,算法,以及算法的复杂度。
方案1:采用trie树,关键字域存该查询串出现的次数,没有出现为0。最后用10个元素的最小推来对出现频率进行排序。
14. 一共有N个机器,每个机器上有N个数。每个机器最多存O(N)个数并对它们操作。如何找到clip_image022个数中的中数?
方案1:先大体估计一下这些数的范围,比如这里假设这些数都是32位无符号整数(共有clip_image018[1]个)。我们把0到clip_image024的整数划分为N个范围段,每个段包含clip_image026个整数。比如,第一个段位0到clip_image028,第二段为clip_image026[1]到clip_image030,…,第N个段为clip_image032到clip_image024[1]。 然后,扫描每个机器上的N个数,把属于第一个区段的数放到第一个机器上,属于第二个区段的数放到第二个机器上,…,属于第N个区段的数放到第N个机器上。 注意这个过程每个机器上存储的数应该是O(N)的。下面我们依次统计每个机器上数的个数,一次累加,直到找到第k个机器,在该机器上累加的数大于或等于clip_image034,而在第k-1个机器上的累加数小于clip_image034[1],并把这个数记为x。那么我们要找的中位数在第k个机器中,排在第clip_image036位。然后我们对第k个机器的数排序,并找出第clip_image036[1]个数,即为所求的中位数。复杂度是clip_image038的。
方案2:先对每台机器上的数进行排序。排好序后,我们采用归并排序的思想,将这N个机器上的数归并起来得到最终的排序。找到第clip_image034[2]个便是所求。复杂度是clip_image040的。
15. 最大间隙问题
给定n个实数clip_image042,求着n个实数在实轴上向量2个数之间的最大差值,要求线性的时间算法。
方案1:最先想到的方法就是先对这n个数据进行排序,然后一遍扫描即可确定相邻的最大间隙。但该方法不能满足线性时间的要求。故采取如下方法:
s 找到n个数据中最大和最小数据max和min。
s 用n-2个点等分区间[min, max],即将[min, max]等分为n-1个区间(前闭后开区间),将这些区间看作桶,编号为clip_image044,且桶clip_image046的上界和桶i+1的下届相同,即每个桶的大小相同。每个桶的大小为:clip_image048。实际上,这些桶的边界构成了一个等差数列(首项为min,公差为clip_image050),且认为将min放入第一个桶,将max放入第n-1个桶。
s 将n个数放入n-1个桶中:将每个元素clip_image052分配到某个桶(编号为index),其中clip_image054,并求出分到每个桶的最大最小数据。
s 最大间隙:除最大最小数据max和min以外的n-2个数据放入n-1个桶中,由抽屉原理可知至少有一个桶是空的,又因为每个桶的大小相同,所以最大间隙 不会在同一桶中出现,一定是某个桶的上界和气候某个桶的下界之间隙,且该量筒之间的桶(即便好在该连个便好之间的桶)一定是空桶。也就是说,最大间隙在桶 i的上界和桶j的下界之间产生clip_image056。一遍扫描即可完成。
16. 将多个集合合并成没有交集的集合:给定一个字符串的集合,格式如:clip_image058。要求将其中交集不为空的集合合并,要求合并完成的集合之间无交集,例如上例应输出clip_image060。
(1) 请描述你解决这个问题的思路;
(2) 给出主要的处理流程,算法,以及算法的复杂度;
(3) 请描述可能的改进。
方案1:采用并查集。首先所有的字符串都在单独的并查集中。然后依扫描每个集合,顺序合并将两个相邻元素合并。例如,对于clip_image062, 首先查看aaa和bbb是否在同一个并查集中,如果不在,那么把它们所在的并查集合并,然后再看bbb和ccc是否在同一个并查集中,如果不在,那么也把 它们所在的并查集合并。接下来再扫描其他的集合,当所有的集合都扫描完了,并查集代表的集合便是所求。复杂度应该是O(NlgN)的。改进的话,首先可以 记录每个节点的根结点,改进查询。合并的时候,可以把大的和小的进行合,这样也减少复杂度。
17. 最大子序列与最大子矩阵问题
数组的最大子序列问题:给定一个数组,其中元素有正,也有负,找出其中一个连续子序列,使和最大。
方案1:这个问题可以动态规划的思想解决。设clip_image064表示以第i个元素clip_image066结尾的最大子序列,那么显然clip_image068。基于这一点可以很快用代码实现。
最大子矩阵问题:给定一个矩阵(二维数组),其中数据有大有小,请找一个子矩阵,使得子矩阵的和最大,并输出这个和。
方案1:可以采用与最大子序列类似的思想来解决。如果我们确定了选择第i列和第j列之间的元素,那么在这个范围内,其实就是一个最大子序列问题。如何确定第i列和第j列可以词用暴搜的方法进行。代码详见我的博客。
+5
View File
@@ -47,6 +47,11 @@ int i=(j=4,k=8,l=16,m=32); printf(“%d”, i); 输出是多少?
要求设计一个DNS的Cache结构,要求能够满足每秒5000以上的查询,满足IP数据的快速插入,查询的速度要快。 要求设计一个DNS的Cache结构,要求能够满足每秒5000以上的查询,满足IP数据的快速插入,查询的速度要快。
概率分析,随机算法问题 (概率论,数理统计)
给你5个球,每个球被抽到的可能性为30、50、20、40、10,设计一个随机算法,该算法的输出结果为本次执行的结果。输出A,B,C,D,E即可。 给你5个球,每个球被抽到的可能性为30、50、20、40、10,设计一个随机算法,该算法的输出结果为本次执行的结果。输出A,B,C,D,E即可。