From 2957b950a45f28534262a24bb72a8fd28dc841e5 Mon Sep 17 00:00:00 2001 From: nonstriater <410495266@qq.com> Date: Sat, 28 Nov 2015 21:12:00 +0800 Subject: [PATCH] update format --- 数学/README.md | 17 +- 算法问题选编/README.md | 39 ++-- 算法问题选编/堆和栈.md | 19 +- 算法问题选编/字符串.md | 9 +- 算法问题选编/数值问题.md | 190 +++++++++----------- 算法问题选编/数组数列问题.md | 340 ++++++++++++++--------------------- 算法问题选编/智力思维训练.md | 8 +- 算法问题选编/海量数据处理.md | 246 +++++++++++++------------ 算法问题选编/矩阵.md | 65 +++++++ 算法问题选编/算法面试100题 | 124 ------------- 算法问题选编/系统设计.md | 45 +++-- 算法问题选编/链表.md | 135 ++++++++------ 12 files changed, 566 insertions(+), 671 deletions(-) create mode 100644 算法问题选编/矩阵.md diff --git a/数学/README.md b/数学/README.md index a20ef2e..b6ce8c9 100644 --- a/数学/README.md +++ b/数学/README.md @@ -1,8 +1,6 @@ >数学是科学的皇后 - >数学是对自然界事实的总结和归纳,又是抽象思考的结果 - >数学是上帝描写自然的语言 —— 伽利略 @@ -23,32 +21,27 @@ 微积分研究的对象是变量(或函数) - +极限理论是微积分的基础 微积分包括`微分` 和 `积分` ,微分和积分互为逆运算。 *微分*: `极限理论` 、 `导数` 、`微分` 是一套关于关于变化率的理论,将函数,速度,加速度,曲线,曲率用一套通用符号表示。 -极限理论是微积分的基础 - *积分*: 内容有`定积分 ` 和 `不定积分` , 是计算面积和体积的一套理论方法 - - -###线性代数 +### 线性代数 `线性代数`是数学的另一个分支,研究对象是`向量` `矩阵` `线性变换` `线性方程组` - -###概率论与数理统计 +### 概率论与数理统计 -###离散数学 +### 离散数学 @@ -58,7 +51,7 @@ -推荐阅读 +### 推荐阅读 《数学之美》 吴军 diff --git a/算法问题选编/README.md b/算法问题选编/README.md index d2e888a..7c3ce6d 100644 --- a/算法问题选编/README.md +++ b/算法问题选编/README.md @@ -18,6 +18,12 @@ ### 链表 +* 链表反转 +* 链表中是否有环 +* 删除链表中的p节点,在p节点前面插入节点q, 要求O(1)复杂度 +* 2个链表相交 +* 2个链表合并 + ### 堆和栈 @@ -26,22 +32,21 @@ -### 数组问题 - -数组 - -数列 - -矩阵 - - -###数值问题 +### 数值问题 这部分都是一些数学几何计算方面的问题。 -### 二叉树 +### 数组数列问题 + + + +### 矩阵 + + + +### 二叉树 @@ -54,6 +59,18 @@ ### 海量数据处理 +海量处理问题常用的分析解决问题的思路是: + +1. 分而治之/Hash映射 + hash统计/trie树/红黑树/二叉搜索树 + 堆排序/快速排序/归并排序 +2. 双层桶划分 +3. Bloom filter 、Bitmap +4. Trie树/数据库/倒排索引 +5. 外排序 +6. 分布处理之 Hadoop/Mapreduce + + +### 智力思维训练 + ###【剑指offer】 diff --git a/算法问题选编/堆和栈.md b/算法问题选编/堆和栈.md index bd421fc..1051b12 100644 --- a/算法问题选编/堆和栈.md +++ b/算法问题选编/堆和栈.md @@ -1,10 +1,13 @@ -如果用一个循环数组q[0..m-1]表示队列时,该队列只有一个队列头指针front,不设队列尾指针rear,求这个队列中从队列头到队列尾的元素个数(包含队列头、队列尾)(华赛面试题、腾讯笔试题)。 +#### 循环队列中元素个数 + +如果用一个循环数组q[0..m-1]表示队列时,该队列只有一个队列头指针front不设队列尾指针rear,求这个队列中从队列头到队列尾的元素个数(包含队列头、队列尾) 分析: 有rear指针时: + if rear>front count = rear-front+1 else count = rear-front+m+1 综合,count = (rear-front+m+1)%m @@ -15,8 +18,8 @@ else count = rear-front+m+1 +#### 设计包含min函数的栈 -设计包含min函数的栈。 定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。 要求函数min、push以及pop的时间复杂度都是O(1)。 @@ -36,7 +39,8 @@ a < f(top),把 a push 到 辅助栈中 -栈的push、pop序列 +#### 栈的push、pop序列 + 题目:输入两个整数序列。其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序。为了简单起见,我们假设push序列的任意两个整数都是不相等的。 比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。 @@ -62,21 +66,22 @@ push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop, -颠倒栈 -题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1在栈顶。 -颠倒之后的栈为{5, 4, 3, 2, 1},5处在栈顶。 +### 颠倒栈 + +题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1在栈顶。颠倒之后的栈为{5, 4, 3, 2, 1},5处在栈顶。 思路一:所有元素pop出来,放到一个数组里,然后在从第一个元素开始入栈,空间复杂度需要 O(N) 思路二: 递归方法。把栈看出2部分 :1 和 {2,3,4,5} , 把栈{2,3,4,5}颠倒过来,然后把1放到底部,那整个栈就颠倒过来了。 +``` void reverseStack(){ stack.pop(); reverseStack(); addTopToStackBottom(); - } +``` diff --git a/算法问题选编/字符串.md b/算法问题选编/字符串.md index 9e9ef0e..2f67307 100644 --- a/算法问题选编/字符串.md +++ b/算法问题选编/字符串.md @@ -6,7 +6,6 @@ * 回文判断 * 字符串的压缩 * 字符串的排列和组合 - * 字符串比较 * 子串 @@ -37,6 +36,7 @@ a = a - b; ``` 类似的题目还有: + 不开辟用于交换数据的临时空间,如何完成字符串的逆序(在技术一轮面试中,有些面试官会这样问) 用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。 @@ -58,7 +58,8 @@ a = a - b; #### 请编写能直接实现strstr()函数功能的代码。 ``` -string.h char *strstr(const char *haystack, const char * needle); //返回第一次出现子串的地址,否则返回0 +//返回第一次出现子串的地址,否则返回0 +string.h char *strstr(const char *haystack, const char * needle); ``` @@ -103,7 +104,9 @@ hash表长度 256,字符直接作为key值 #### 匹配兄弟字符串 -如果两个字符串的字符一样,但是顺序不一样,被认为是兄弟字符串,问如何在迅速匹配兄弟字符串(如,bad和adb就是兄弟字符串)。思路:判断各自素数乘积是否相等。更多方法请参见:http://blog.csdn.net/v_JULY_v/article/details/6347454。 +如果两个字符串的字符一样,但是顺序不一样,被认为是兄弟字符串,问如何在迅速匹配兄弟字符串(如,bad和adb就是兄弟字符串)。 + +思路:判断各自素数乘积是否相等。更多方法请参见:http://blog.csdn.net/v_JULY_v/article/details/6347454。 ``` BOOL isBrother(const char *first,const char *secd) diff --git a/算法问题选编/数值问题.md b/算法问题选编/数值问题.md index edbe1b5..0766b3f 100644 --- a/算法问题选编/数值问题.md +++ b/算法问题选编/数值问题.md @@ -1,84 +1,95 @@ +### 字符串转整数 + 写一个函数,检查字符是否是整数,如果是,返回其整数值 +使用递归实现 +``` +int string_to_integer(char *s,int length) +``` + +单要思考几个问题: + +1. s是空字符串 +2. s传参为非数字字符 +3. 超出整数所能表示范围 + 类似的题目还有: 怎样只用4行代码编写出一个从字符串到长整形的函数? 请编写能直接实现int atoi(const char * pstr)函数功能的代码。 将字符串转出整数 -分析: -使用递归实现 -int string_to_integer(char *s,int length) -单要思考几个问题: -1. s是空字符串 -2. s传参为非数字字符 -3. 超出整数所能表示范围 +#### 求1+2+…+n - -题目:求1+2+…+n, 要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。 分析: 1. 不能使用循环,那就用递归 2. 递归需要终止递归的条件判断语句,这里也不能用if,想其他办法,可以使用 &&逻辑与 运算符(在n>0条件满足是,才会指向后面的递归语句 - +``` int factorial(n){ int sum=0; (n>0) && sum=n+factorial(n-1) return sum; } +``` +#### 整数的二进制表示中1的个数 - -整数的二进制表示中1的个数 题目:输入一个整数,求该整数的二进制表达中有多少个1。 例如输入10,由于其二进制表示为1010,有两个1,因此输出2。 分析: 这是一道很基本的考查位运算的面试题。 -解法1:一轮循环移位计数 (移位运算比除法运算效率要高,注意要考虑是负数的情况) -解法2:位运算 +解法1:一轮循环移位计数 (移位运算比除法运算效率要高,注意要考虑是负数的情况) +解法2:位运算 解法3:num &= num-1 巧妙之处在于,对高位没有影响。不断做 `num &= num-1` 直到num=0 - -请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句 +#### 请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句 分析: +``` #define min(a,b) ((a)>(b)?(a):(b)) #define MIN(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; }) +``` 这里不能使用比较符号: +``` #define min(a,b) ((a)-(b) & (0x1<<31))?(a):(b) +``` - -编程实现:把十进制数(long型)分别以二进制和十六进制形式输出,不能使用printf系列 +#### 把十进制数(long型)分别以二进制和十六进制形式输出,不能使用printf系列 分析: +``` char *integer_to_hex(long i); char *integer_to_bin(long i); +``` 注意,转16进制中,要判断tmp[i]是否是有符号的数 +``` tmp[i]= tmp[i]>=0?tmp[i]:tmp[i]+16; +``` +#### 判断一个自然数是否是某个数的平方 - - -判断一个自然数是否是某个数的平方。说明:当然不能使用开方运算。 - +说明:当然不能使用开方运算。 +``` square(25) YES square(35) NO +``` 方法1: 从1开始遍历,显然这种方法很差 方法2: 除数跟余数比较,除数从2开始,每一轮都跟结果比较;相等就是存在这个数,不相等就把除数++;时间复杂度为(根号n)。在一个数比较大时,效率不够好。如1194877489=(34567)^2,需要从2开始,一直比较到34566,做3万多次除法和比较运算。跟方法1一样。。 @@ -88,7 +99,7 @@ square(35) NO -1024! 末尾有多少个0? +#### 1024! 末尾有多少个0? 分析: 末尾0的个数取决于2和5的个数 @@ -99,11 +110,13 @@ square(35) NO -编程实现两个正整数的除法(和取模) +#### 编程实现两个正整数的除法(和取模) + 编程实现两个正整数的除法,当然不能用除法操作符。 +``` // return x/y. int div(const int x, const int y) -{} +``` 1. 循环减被除数,减到不能再减,当除数很大,被除数小时,效率很低 2. 位运算 @@ -112,12 +125,13 @@ int div(const int x, const int y) -求两个或N个数的最大公约数和最小公倍数。 +#### 求两个或N个数的最大公约数和最小公倍数。 -大整数乘法(或 大整数阶乘) +#### 大整数乘法(或 大整数阶乘) + 请使用代码计算1234567891011121314151617181920*2019181716151413121110987654321 。 1. 注意结果可能超出长整形的最大范围 2^64-1 @@ -128,15 +142,14 @@ int div(const int x, const int y) - -两个数相乘,小数点后位数没有限制,请写一个高精度算法 +#### 两个数相乘,小数点后位数没有限制,请写一个高精度算法 +#### 求根号2的值 - -如何求根号2的值,并且按照我的需要列出指定小数位,比如根号2是1.141 我要列出1位小数就是1.1 2位就是1.14, 1000位就是1.141...... 等。。 +并且按照我的需要列出指定小数位,比如根号2是1.141 我要列出1位小数就是1.1 2位就是1.14, 1000位就是1.141...... 等。。 分析: 泰勒级数 @@ -144,14 +157,7 @@ int div(const int x, const int y) - - - - - - - -58、整数的素数和分解问题 +#### 整数的素数和分解问题 歌德巴赫猜想说任何一个不小于6的偶数都可以分解为两个奇素数之和。 对此问题扩展,如果一个整数能够表示成两个或多个素数之和,则得到一个素数和分解式。 @@ -159,37 +165,67 @@ int div(const int x, const int y) 对于一个给定的整数,输出所有这种素数和分解式。 注意,对于同构的分解只输出一次(比如5只有一个分解2 + 3,而3 + 2是2 + 3的同构分解式 -)。 例如,对于整数8,可以作为如下三种分解: + +``` (1) 8 = 2 + 2 + 2 + 2 (2) 8 = 2 + 3 + 3 (3) 8 = 3 + 5 +``` + + +#### 大于K的最小正整数 + +给定一个集合A=[0,1,3,8](该集合中的元素都是在0,9之间的数字,但未必全部包含),指定任意一个正整数K,请用A中的元素组成一个大于K的最小正整数。 + +比如,A=[1,0] K=21 那么输出结构应该为100。 + + + +#### 给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数。 + + + +#### 设计一个随机算法 + +给你5个球,每个球被抽到的可能性为30、50、20、40、10,设计一个随机算法,该算法的输出结果为本次执行的结果。输出A,B,C,D,E即可。 + + +#### 构造一个随机发生器 + +已知一随机发生器,产生0的概率是p,产生1的概率是1-p,现在要你构造一个发生器, +使得它构造0和1的概率均为1/2;构造一个发生器,使得它构造1、2、3的概率均为1/3;..., +构造一个发生器,使得它构造1、2、3、...n的概率均为1/n,要求复杂度最低。 +#### 两个圆相交 -在从1到n的正数中1出现的次数 -题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。 - -例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。 -分析:这是一道广为流传的google面试题。 - -int one_appear_count(int n); -思路1. 遍历1~n,统计出现1的个数;n足够大时,效率很低 -思路2. 分析规律 +两个圆相交,交点是A1,A2。现在过A1点做一直线与两个圆分别相交另外一点B1,B2。B1B2可以绕着A1点旋转。问在什么情况下,B1B2最长 +#### 输入四个点的坐标,求证四个点是不是一个矩形 -排列组合问题 -1,2,3,4,5 五个不同的数字,打印不同的排列 -这就是一个无向图的遍历,把每个数字看成一个节点。 +关键点: +1.相邻两边斜率之积等于-1, +2.矩形边与坐标系平行的情况下,斜率无穷大不能用积判断。 +3.输入四点可能不按顺序,需要对四点排序。 -用1、2、2、3、4、5这六个数字,写一个main函数,打印出所有不同的排列, + + +#### 排列组合问题 + + +##### 1,2,3,4,5 五个不同的数字,打印不同的排列。这就是一个无向图的遍历,把每个数字看成一个节点。 + + +##### 用1、2、2、3、4、5这六个数字,写一个main函数,打印出所有不同的排列, + 如:512234、412345等,要求:"4"不能在第三位,"3"与"5"不能相连. 这是对上一题增加难度。但是需要 @@ -202,54 +238,6 @@ int one_appear_count(int n); -给定一个集合A=[0,1,3,8](该集合中的元素都是在0,9之间的数字,但未必全部包含), -指定任意一个正整数K,请用A中的元素组成一个大于K的最小正整数。 - -比如,A=[1,0] K=21 那么输出结构应该为100。 - - - - -20、给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数。 - - - -给你5个球,每个球被抽到的可能性为30、50、20、40、10,设计一个随机算法,该算法的输出结果为本次执行的结果。输出A,B,C,D,E即可。 - - -2.已知一随机发生器,产生0的概率是p,产生1的概率是1-p,现在要你构造一个发生器, -使得它构造0和1的概率均为1/2;构造一个发生器,使得它构造1、2、3的概率均为1/3;..., -构造一个发生器,使得它构造1、2、3、...n的概率均为1/n,要求复杂度最低。 - - - - - - - - - -两个圆相交,交点是A1,A2。现在过A1点做一直线与两个圆分别相交另外一点B1,B2。B1B2可以绕着A1点旋转。问在什么情况下,B1B2最长 - - - - -题目:输入四个点的坐标,求证四个点是不是一个矩形 -关键点: -1.相邻两边斜率之积等于-1, -2.矩形边与坐标系平行的情况下,斜率无穷大不能用积判断。 -3.输入四点可能不按顺序,需要对四点排序。 - - -矩阵式螺旋输出 - - - - - - - - diff --git a/算法问题选编/数组数列问题.md b/算法问题选编/数组数列问题.md index e9e7154..b5fce1f 100644 --- a/算法问题选编/数组数列问题.md +++ b/算法问题选编/数组数列问题.md @@ -1,17 +1,17 @@ -///////// 数组 //////////// -题目:求子数组的最大和(最大字段和) +#### 求子数组的最大和(最大字段和) + 输入一个整形数组,数组里有正数也有负数。 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。 求所有子数组的和的最大值。要求时间复杂度为O(n)。 -例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2, +例如输入的数组为`1, -2, 3, 10, -4, 7, 2, -5` ,和最大的子数组为`3, 10, -4, 7, 2` , 因此输出为该子数组的和18。 1. 蛮力法 fmax(i,j) 找出最大的值,3重循环 ,复杂度 0(n^3) 2. maxendindhere保存当前累加的和,如果<0,就把maxendinghere清零 , max保存最终的最大和 - +``` int maxSumOfVector(int *data,int length){ int max=0; @@ -36,23 +36,24 @@ return max; } +``` +#### 查找最小的k个元素(top-k) - - -查找最小的k个元素(top-k) 题目:输入n个整数,输出其中最小的k个。 例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4。 +``` topMinK(int *a,int length,int k); topMaxK(int *a,int length, int k); +``` -1) 全部排序 复杂度 NlogN , 数据了较大时,内存可能承受不住 -2) 部分排序 维护一个大小为K的数组,由大到小排序,然后遍历所有数据,每个数据跟数组中最小元素比较,如果比最小元素大,就要插入数组了,这里还有寻找插入位置,移动数组元素的cpu消耗。复杂度是N*K -3) 堆排序 。在这的K较大时(比如这道题目:2亿个整数中求最大的100万之和),上面的算法还是有很多可以改进的地方,如采用二分查找定位插入位置,移动数组元素的计算是躲不过去了。那有没有什么数据结构即能`快速查找,还能快速移动元素`呢?最好是O(1)复杂度。 +1. 全部排序 复杂度 NlogN , 数据了较大时,内存可能承受不住 +2. 部分排序 维护一个大小为K的数组,由大到小排序,然后遍历所有数据,每个数据跟数组中最小元素比较,如果比最小元素大,就要插入数组了,这里还有寻找插入位置,移动数组元素的cpu消耗。复杂度是N*K +3. 堆排序 。在这的K较大时(比如这道题目:2亿个整数中求最大的100万之和),上面的算法还是有很多可以改进的地方,如采用二分查找定位插入位置,移动数组元素的计算是躲不过去了。那有没有什么数据结构即能`快速查找,还能快速移动元素`呢?最好是O(1)复杂度。 答案就是`二叉堆`。我们可以遍历总量中的每个元素来跟二叉堆中的堆顶元素比较(堆顶元素在`小根堆`中最小值,在`大根堆`中是最大值),这样在0(1)复杂度就可以完成查找操作,揭下来需要的操作就是重新调整推结构,复杂度是O(logk),因此整个操作复杂度是 O(n*logk) @@ -61,8 +62,7 @@ topMaxK(int *a,int length, int k); -最长公共子序列 (动态规划的经典题目) - +#### 最长公共子序列 (动态规划的经典题目) 【最大/小差问题】 @@ -77,21 +77,19 @@ topMaxK(int *a,int length, int k); `桶排序` 比快排还快,最耗空间 - - - +#### 和为m的组合 编程求解:输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来. 如: n=10,m=25;可能的组合有【】【】【】【】 - +``` print_sum_detials(int n , int sum) +``` `动态规划`(类似背包问题) 1. 划分问题 - 2. 选择状态 3. 状态转移方程 @@ -100,14 +98,15 @@ print_sum_detials(int n , int sum) +#### 找出和为m的2个数 +输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。 +例如输入数组【1、2、4、7、11、15=和数字15。由于4+11=15,因此输出4和11。 -输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。 -要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。 -例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。 - -int findaddends(int *data,int length,int sum,int *a,int *b); //返回0找到,返回-1没找到 - +``` +//返回0找到,返回-1没找到 +int findaddends(int *data,int length,int sum,int *a,int *b); +``` 分析: 数组升序排列,查找可用二分查找,时间复杂度O(logn),这样问题就变成了找其中一个加数的问题,复杂度为N*logN @@ -118,45 +117,24 @@ int findaddends(int *data,int length,int sum,int *a,int *b); //返回0找到, - -二维数组中的查找 -在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 - - - - -题目:定义Fibonacci数列如下: - / 0 n=0 -f(n)= 1 n=1 - / f(n-1)+f(n-2) n=2 - -输入n,用最快的方法求该数列的第n项。 - -虽然fibonacci数列是`递归`的经典应用,但递归效率很差,会有很多重复的计算,复杂度是成指数递增的,我测试了下计算50的时候已经要300s了。 - -思路二:从下往上计算,复杂度O(N),一个循环就搞定 - -思路三: logN - - - - +#### 若干个数的和与M最为接近 给定一个实数数组,按序排列(从小到大),从数组从找出若干个数,使得这若干个数的和与M最为接近,描述一个算法,并给出算法的复杂度。 - +#### 递减数列左移后的数组中找数 一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5} 是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。 -1) 右移,二分查找。 找到最小的数,右移到第一个位置的时候,右移完成 O(N) -2) 直接二分查找 +1. 右移,二分查找。 找到最小的数,右移到第一个位置的时候,右移完成 O(N) +2. 直接二分查找 -数组中超过出现次数超过一半的数字 +#### 数组中超过出现次数超过一半的数字 + 题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。 分析:这是一道广为流传的面试题,包括百度、微软和Google在内的多家公司 @@ -168,17 +146,11 @@ f(n)= 1 n=1 -一个int数组,里面数据无任何限制,要求求出所有这样的数a[i], -其左边的数都小于等于它,右边的数都大于等于它。 -能否只用一个额外数组和少量其它空间实现。 - - - 3. 有一个函数int getNum(),每运行一次可以从一个数组V[N]里面取出一个数,N未知,当数取完的时候,函数返回NULL。现在要求写一个函数int get(),这个函数运行一次可以从V[N]里随机取出一个数,而这个数必须是符合1/N平均分布的,也就是说V[N]里面任意一个数都有1/N的机会被取出,要求空间复杂度为O(1) - +#### 找数组里面重复的一个数 找数组里面重复的一个数,一个含n个元素的整数数组至少存在一个重复数, 请编程实现,在O(n)时间内找出其中任意一个重复数。 @@ -188,6 +160,7 @@ hash算法,空间要求多 (注意数组元素要位 int) 高级解法: 将问题转化为 `判断单链表中存在环` +#### 给出一个洗牌算法 给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里 @@ -198,6 +171,8 @@ hash算法,空间要求多 (注意数组元素要位 int) +#### 重合区间最长的两个区间段 + 在一维坐标轴上有n个区间段,求重合区间最长的两个区间段 如【-7,21】,【4,23】,【14,100】,【54,76】 @@ -206,7 +181,9 @@ hash算法,空间要求多 (注意数组元素要位 int) -奇偶分离。给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。 +#### 奇偶分离 + +给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。 要求:空间复杂度O(1),时间复杂度为O(n) 如 [4,5,2,7,5] => [5,7,5,4,2], 空间复杂度O(1),得使用 交换排序 @@ -230,7 +207,8 @@ hash算法,空间要求多 (注意数组元素要位 int) -找出数组中两个只出现一次的数字 +#### 找出数组中两个只出现一次的数字 + 题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。 请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。 @@ -242,7 +220,7 @@ hash算法,空间要求多 (注意数组元素要位 int) -用递归的方法判断整数组a[N]是不是升序排列 +#### 用递归的方法判断整数组a[N]是不是升序排列 递归 isAscend(n-1) && a[N-1]< a[N] @@ -277,19 +255,23 @@ hash算法,空间要求多 (注意数组元素要位 int) +#### 数组中找出某数字出现的次数 在排序数组中,找出给定数字的出现次数。比如 [1, 2, 2, 2, 3] 中2的出现次数是3次。 分析: 1. 因为是排序数组,可以使用二分查找 2. 将二分查找坚持到底,这样在最坏的情况下([2,2,2,2,2,2,2])都有0(lgn)复杂度 + +``` int binary_search_first(int *a,int length,int key); int binary_search_lash(int *a,int length,int key); +``` -俩个闲玩娱乐。 -1.扑克牌的顺子 +#### 扑克牌的顺子 + 从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。 2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。 @@ -298,12 +280,15 @@ int binary_search_lash(int *a,int length,int key); 2)统计0的个数 3)统计相邻元素空缺总数 -isShunZi(int *a,int length)//5个是不是顺子 +``` +//5个是不是顺子 +isShunZi(int *a,int length) +``` +#### n个骰子的点数 -2.n个骰子的点数。 把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。 输入n,打印出S的所有可能的值出现的概率。 @@ -313,21 +298,16 @@ isShunZi(int *a,int length)//5个是不是顺子 -把数组排成最小的数。 +#### 把数组排成最小的数 + 题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。 例如输入数组{32, 321},则输出这两个能排成的最小数字32132。 请给出解决问题的算法,并证明该算法。 -分析:这是09年6月份百度的一道面试题, -从这道题我们可以看出百度对应聘者在算法方面有很高的要求。 - - - - -旋转数组中的最小元素。 +#### 旋转数组中的最小元素。 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。 @@ -341,11 +321,14 @@ isShunZi(int *a,int length)//5个是不是顺子 -数值的整数次方。 +#### 数值的整数次方 + 题目:实现函数double Power(double base, int exponent),求base的exponent次方。 + 不需要考虑溢出。 分析:这是一道看起来很简单的问题。可能有不少的人在看到题目后30秒写出如下的代码: +``` double Power(double base, int exponent) { double result = 1.0; @@ -353,13 +336,19 @@ double Power(double base, int exponent) result *= base; return result; } +``` 上面的代码没有考虑 exponent<0的情况。 +#### +一个int数组,里面数据无任何限制,要求求出所有这样的数a[i],其左边的数都小于等于它,右边的数都大于等于它。 +能否只用一个额外数组和少量其它空间实现。 + +`快速排序` @@ -380,6 +369,7 @@ double Power(double base, int exponent) 给你10分钟时间,根据上排给出十个数,在其下排填出对应的十个数 要求下排每个数都是先前上排那十个数在下排出现的次数。 上排的十个数如下: + 【0,1,2,3,4,5,6,7,8,9】 初看此题,貌似很难,10分钟过去了,可能有的人,题目都还没看懂。 @@ -416,7 +406,9 @@ X0+X1+...+ = 10; -约瑟夫环问题。n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始, +#### 约瑟夫环问题 + +n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始, 每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。 当一个数字删除后,从被删除数字的下一个继续删除第m个数字。 求出在这个圆圈中剩下的最后一个数字。 @@ -433,9 +425,9 @@ X0+X1+...+ = 10; +#### 圆形和正方形是否相交 - -用最简单, 最快速的方法计算出下面这个圆形是否和正方形相交。" +用最简单, 最快速的方法计算出下面这个圆形是否和正方形相交 3D坐标系 原点(0.0,0.0,0.0) 圆形: 半径r = 3.0 @@ -454,34 +446,24 @@ X0+X1+...+ = 10; - +#### 两个序列和只差最小 有两个序列a,b,大小都为n,序列元素的值任意整数,无序; 要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。 -例如: +例如: +``` var a=[100,99,98,1,2, 3]; var b=[1, 2, 3, 4,5,40]; +``` +#### 重新排列使负数排在正数前面 +一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序 -n支队伍比赛,分别编号为0,1,2。。。。n-1,已知它们之间的实力对比关系, -存储在一个二维数组w[n][n]中,w[i][j] 的值代表编号为i,j的队伍中更强的一支。 +比如: input: 1,7,-5,9,-12,15 ans: -5,-12,1,7,9,15 要求时间复杂度O(N),空间O(1)。(此题一直没看到令我满意的答案,一般达不到题目所要求的:时间复杂度O(N),空间O(1),且保证原来正负数之间的相对位置不变)。 -所以w[i][j]=i 或者j,现在给出它们的出场顺序,并存储在数组order[n]中, -比如order[n] = {4,3,5,8,1......},那么第一轮比赛就是 4对3, 5对8。....... - -胜者晋级,败者淘汰,同一轮淘汰的所有队伍排名不再细分,即可以随便排, -下一轮由上一轮的胜者按照顺序,再依次两两比,比如可能是4对5,直至出现第一名 - -编程实现,给出二维数组w,一维数组order 和 用于输出比赛名次的数组result[n],求出result。 - - - - - -一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序 比如: input: 1,7,-5,9,-12,15 ans: -5,-12,1,7,9,15 要求时间复杂度O(N),空间O(1) 。(此题一直没看到令我满意的答案,一般达不到题目所要求的:时间复杂度O(N),空间O(1),且保证原来正负数之间的相对位置不变)。 updated:设置一个起始点j, 一个翻转点k,一个终止点L 从右侧起 起始点在第一个出现的负数, 翻转点在起始点后第一个出现的正数,终止点在翻转点后出现的第一个负数(或结束) @@ -508,60 +490,7 @@ updated:设置一个起始点j, 一个翻转点k,一个终止点L -搜狗笔试题:一个长度为n的数组a[0],a[1],...,a[n-1]。现在更新数组的名个元素,即a[0]变为a[1]到a[n-1]的积,a[1]变为a[0]和a[2]到a[n-1]的积,...,a[n-1]为a[0]到a[n-2]的积(就是除掉当前元素,其他所有元素的积)。程序要求:具有线性复杂度,且不能使用除法运算符。 - - 思路:left[i]标示着a[i]之前的乘积,right[i]标示着a[i]之后的乘积,但不申请空间,那么a[i]=left[i]*right[i] 。不过,left的计算从左往右扫的时候得出,right是从右往左扫得出。因为就是当中某个元素a[i]的左边所有元素与右边所有元素的乘积,就这么简单。所以计算a[i]=left[i]*right[i]时,直接出结果。 - - - -后2012年4月6日的腾讯暑期实习生招聘笔试中,出了一道与上述21题类似的题,原题大致如下: - - 两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i]赋值,b[i] = a[0]*a[1]*a[2]...*a[N-1]/a[i]; -要求: - 1.不准用除法运算 - 2.除了循环计数值,a[N],b[N]外,不准再用其他任何变量(包括局部变量,全局变量等) - 3.满足时间复杂度O(n),空间复杂度O(1)。 - - 说白了,你要我求b=a[0]*a*...a[i-1]*a*a[i+1]..*a[N-1]/a ,就是求:a[0]*a[1]*...a[i-1]*a[i+1]..*a[N-1]。只是我把a[i]左边部分标示为left[i],b[i]右边部分标示为right[i],而实际上完全不申请left[i],与right[i]变量,之所以那样标示,无非就是为了说明:除掉当前元素a[i],其他所有元素(a[i]左边部分,和a[i]右边部分)的积。读者你明白了么? - - 下面是此TX笔试题的两段参考代码,如下: - - - -//ncicc -b[0] = 1; -for (int i = 1; i < N; i++) -{ - b[0] *= a[i-1]; - b[i] = b[0]; -} -b[0] = 1; -for (i = N-2; i > 0; i--) -{ - b[0] *= a[i+1]; - b[i] *= b[0]; -} -b[0] *= a[1]; - from wasd6081058上面第二段代码最后一行的意义是:我们看第二个循环,从N-2到 1;再看for循环中b[0]的赋值,从N-1到2,而根据题目要求b[i] = a[0]*a[1]*a[2]...*a[N-1]/a[i],b[0]应等于a[1]*a[2]* ....a[N-1],所以这里手动添加a[1]。 - 此外,也有朋友在微博上:http://weibo.com/1761944724/ydwuMt9bH 给出了另外一种解法,如下图所示: - - - - -求随机数构成的数组中找到长度大于=3的最长的等差数列9 d- x' W) w9 ?" o3 b0 R -输出等差数列由小到大: -如果没有符合条件的就输出 -格式: -输入[1,3,0,5,-1,6] -输出[-1,1,3,5] -要求时间复杂度,空间复杂度尽量小 - - - - - - -57、求最大重叠区间大小 +#### 求最大重叠区间大小 题目描述:请编写程序,找出下面“输入数据及格式”中所描述的输入数据文件中最大重叠区间的大小。 对一个正整数 n ,如果n在数据文件中某行的两个正整数(假设为A和B)之间,即A<=n<=B或A>=n>=B ,则 n 属于该行; @@ -572,14 +501,15 @@ b[0] *= a[1]; -四对括号可以有多少种匹配排列方式?比如两对括号可以有两种:()()和(()) +#### 四对括号可以有多少种匹配排列方式 + +比如两对括号可以有两种:()()和(()) +#### 输出1到最大的N位数 - -输出1到最大的N位数 题目:输入数字n,按顺序输出从1最大的n位10进制数。比如输入3,则输出1、2、3一直到最大的3位数即999。 分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。 @@ -591,7 +521,8 @@ b[0] *= a[1]; -和为n连续正数序列。 +#### 和为n连续正数序列 + 题目:输入一个正数n,输出所有和为n连续正数序列。 例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。 @@ -604,7 +535,7 @@ print_continuous_sequence_sum(int n) 思路二: a[small,big] sum[small,big]>N small往前移动,否则,big往前移动。 O(N)复杂度搞定 - +### 找出和为N+1的2个数 一个整数数列,元素取值可能是1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。 设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于N+1。 @@ -612,30 +543,13 @@ print_continuous_sequence_sum(int n) 分析:列出所有的数对,如输入15,输出【1,14】【2,13】【3,12】。。。 +``` int print_sequence_sum(int n) - - - - -一个有序数列,序列中的每一个值都能够被2或者3或者5所整除,1是这个序列的第一个元素。求第1500个值是多少?(与下面的寻找丑数是一个意思) - -1,2,3,4,5,6,8,9,10,12,14,15,16,... - -1)所有的偶数都在序列中 -2)3的倍数也在序列中 -3)5的倍数也在系列中 -unsigned long value_in_sequence(int index) - -0. 2,3,5最小公倍数是30 -1. [1,30]符合条件有22个 -2. [30,60]符合条件也22个 - -第1500个: 1500/22=68余4,一个周期内的前4个数是2,3,4,5; 最终答案是`68*30+5` - - +``` -寻找丑数 +#### 寻找丑数 + 题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。 例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。 求按从小到大的顺序的第1500个丑数。 @@ -646,35 +560,63 @@ unsigned long value_in_sequence(int index) 1,2,3,4,5,6,8,9,10,12,15,16,18,20,28.... +1)所有的偶数都在序列中 +2)3的倍数也在序列中 +3)5的倍数也在系列中 + + +0. 2,3,5最小公倍数是30 +1. [1,30]符合条件有22个 +2. [30,60]符合条件也22个 + +第1500个: 1500/22=68余4,一个周期内的前4个数是2,3,4,5; 最终答案是`68*30+5` -调整数组顺序使奇数位于偶数前面。 +#### 调整数组顺序使奇数位于偶数前面 + 题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分, 所有偶数位于数组的后半部分。要求时间复杂度为O(n)。 思路:两边向中间扫描,如果第一个指针是偶数,第二个指针是奇数,就交换;如果第一个是偶数,第二个也偶数,第二个指针向前移;反之,第一个指针向后移 +``` void reorder(int *data, int length); - +``` +#### 在从1到n的正数中1出现的次数 + +题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。 + +例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。 +分析:这是一道广为流传的google面试题。 +``` +int one_appear_count(int n); +``` + +思路1. 遍历1~n,统计出现1的个数;n足够大时,效率很低 +思路2. 分析规律 -求一个数组的最长递减子序列。 比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2} + + +#### 求一个数组的最长递减子序列 + + 比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2} `动态规划` -如何求2个集合的交集 +#### 如何求2个集合的交集 1.每个集合里面是否有重复元素? 思路一:hash,复杂度O(M+N) - +#### 两两之差绝对值最小的值 有一个整数数组,请求出两两之差绝对值最小的值,记住,只要得出最小值即可,不需要求出是哪两个数。 如 [-1, 3, 5, 9] 绝对值最小的是2(5-3) @@ -686,6 +628,7 @@ void reorder(int *data, int length); +#### 数值是否连续相邻 一个整数数列,元素取值可能是0~65535中的任意一个数,相同数值不会重复出现。0是例外,可以反复出现。请设计一个算法,当你从该数列中随意选取5个数值,判断这5个数值是否连续相邻。 注意: @@ -699,7 +642,8 @@ void reorder(int *data, int length); -最长递增子序列 +#### 最长递增子序列 + 题目描述:设L=是n个不同的实数的序列,L的递增子序列是这样一个子序列 Lin=< aK1,ak2,…,akm >,其中k1< k2<…< km且aK1< ak2<…< akm。 求最大的m值。 @@ -710,41 +654,21 @@ Lin=< aK1,ak2,…,akm >,其中k1< k2<…< km且aK1< ak2<…< akm。 -/////////////////////// 矩阵 ///////////////////////////////// +#### Fibonacci数列 -求一个矩阵中最大的二维矩阵(元素和最大).如: -1 2 0 3 4 -2 3 4 5 1 -1 1 5 3 0 -中最大的是: -4 5 -5 3 -要求:(1)写出算法;(2)分析时间复杂度;(3)用C写出关键代码 +题目:定义Fibonacci数列如下: + / 0 n=0 +f(n)= 1 n=1 + / f(n-1)+f(n-2) n=2 +输入n,用最快的方法求该数列的第n项。 +虽然fibonacci数列是`递归`的经典应用,但递归效率很差,会有很多重复的计算,复杂度是成指数递增的,我测试了下计算50的时候已经要300s了。 +思路二:从下往上计算,复杂度O(N),一个循环就搞定 - - -对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)某一个元素也加一, -现给出一正数矩阵,判断其是否能够由一个全零矩阵经过上述运算得到。 - - - -顺时针打印矩阵 -题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。 -例如:如果输入如下矩阵: -1 2 3 4 -5 6 7 8 -9 10 11 12 -13 14 15 16 -则依次打印出数字1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10。 - -分析:包括Autodesk、EMC在内的多家公司在面试或者笔试里采用过这道题。 - - - +思路三: logN diff --git a/算法问题选编/智力思维训练.md b/算法问题选编/智力思维训练.md index 847f657..24dabd9 100644 --- a/算法问题选编/智力思维训练.md +++ b/算法问题选编/智力思维训练.md @@ -1,17 +1,17 @@ -此贴选一些 比较怪的题,,由于其中题目本身与算法关系不大,仅考考思维。特此并作一题。 +这部分内容的题目侧重思维发散。 + + 1.有两个房间,一间房里有三盏灯,另一间房有控制着三盏灯的三个开关,这两个房间是 分割开的,从一间里不能看到另一间的情况。 现在要求受训者分别进这两房间一次,然后判断出这三盏灯分别是由哪个开关控制的。 有什么办法呢? + 2.你让一些人为你工作了七天,你要用一根金条作为报酬。金条被分成七小块,每天给出一块。如果你只能将金条切割两次,你怎样分给这些工人? - - -第22题: 有4张红色的牌和4张蓝色的牌,主持人先拿任意两张,再分别在A、B、C三人额头上贴任意两张牌, A、B、C三人都可以看见其余两人额头上的牌,看完后让他们猜自己额头上是什么颜色的牌, A说不知道,B说不知道,C说不知道,然后A说知道了。 diff --git a/算法问题选编/海量数据处理.md b/算法问题选编/海量数据处理.md index e264f09..f2ca5d1 100644 --- a/算法问题选编/海量数据处理.md +++ b/算法问题选编/海量数据处理.md @@ -1,10 +1,11 @@ -/////////////////// 海量数据处理(千万以上量级) /////////// +这部分内容是海量数据处理的问题。所谓海量: 时间上:无法在较短时间内迅速解决。可以设计巧妙的算法搭配合适的数据结构解决 空间上:数据量太大,无法一次全部装入内存;针对方法是大而化小,分而治之。将规模大划分为规模小的问题,各个击破 +海量处理问题常用的分析解决问题的思路是: 1. 分而治之/Hash映射 + hash统计/trie树/红黑树/二叉搜索树 + 堆排序/快速排序/归并排序 2. 双层桶划分 @@ -14,11 +15,9 @@ 6. 分布处理之 Hadoop/Mapreduce -### 海量日志处理,提取某日访问百度次数最多的那个IP +#### top-1问题 -- 海量日志提取访问最多的IP -类似问题: -a.怎么在海量数据中找出重复次数最多的一个? -b.服务器内存1G,有一个2G的文件,里面每行存着一个QQ号(5-10位数),怎么最快找出出现过最多次的QQ号。(10位数可以表示(不到)10亿个QQ号) +海量日志处理,提取某日访问百度次数最多的那个IP 分析 @@ -54,20 +53,35 @@ unsigned count[N],这个N=2^32=4G个IP地址 sizeof(count) = 4*N = 16G内存, 第三步 排序 堆排序/快速排序/归并排序 +类似问题: +a.怎么在海量数据中找出重复次数最多的一个? +b.服务器内存1G,有一个2G的文件,里面每行存着一个QQ号(5-10位数),怎么最快找出出现过最多次的QQ号。(10位数可以表示(不到)10亿个QQ号) +#### 统计指定时间段内ip访问量 -###top-K 找出最大的k个数 -分析: hashmap统计+二叉堆 +假设某个网站每天有超过10亿次的页面访问量,出于安全考虑,网站会记录访问客户端访问的ip地址和对应的时间,如果现在已经记录了1000亿条数据,想统计一个指定时间段内的区域ip地址访问量,那么这些数据应该按照何种方式来组织,才能尽快满足上面的统计需求呢, +设计完方案后,并指出该方案的优缺点,比如在什么情况下,可能会非常慢? + +参考答案:用B+树来组织,非叶子节点存储(某个时间点,页面访问量),叶子节点是访问的IP地址。这个方案的优点是查询某个时间段内的IP访问量很快,但是要统计某个IP的访问次数或是上次访问时间就不得不遍历整个树的叶子节点。或者可以建立二级索引,分别是时间和地点来建立索引。 + + + + +#### top-K 找出最大的k个数 + +类似的题目类型有: 100w个数中找出最大的100个数。 寻找热门查询,300万个查询字符串中统计最热门的10个查询。 有一个文件中保存了2亿个整数,每个整数都以' '分隔。求最大的100万个整数之和。 有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。请用5分钟时间,找出重复出现最多的前10条。 一个文件中包含了1亿个随机整数,如何快速的找到最大(小)的100万个数字?(时间复杂度:O(n lg k)) +有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。 -分析:100W个数里面是否有重复 + +分析: hashmap统计+二叉堆 方案1:在前面的题中,我们已经提到了,用一个含100个元素的最小堆完成。复杂度为O(100w*lg100)。 @@ -78,7 +92,8 @@ unsigned count[N],这个N=2^32=4G个IP地址 sizeof(count) = 4*N = 16G内存, -这里给个最简单hash函数,`字符串到int类型的哈希` +这里给个最简单hash函数,`字符串到int类型的哈希` +``` int hash_function(const char *p) { int value = 0; @@ -90,16 +105,61 @@ int hash_function(const char *p) } return value; } +``` 这个hash函数要确保 不同的字符串 hash出不同的一个 整数。 +类似的题目还有: + +##### 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。 + +方案1:顺序读文件中,对于每个词x,取clip_image014,然后按照该值存到5000个小文件(记为clip_image016) 中。这样每个文件大概是200k左右。如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,知道分解得到的小文件的大小都不超过1M。对 每个小文件,统计每个文件中出现的词以及相应的频率(可以采用trie树/hash_map等),并取出出现频率最大的100个词(可以用含100个结点 的最小堆),并把100词及相应的频率存入文件,这样又得到了5000个文件。下一步就是把这5000个文件进行归并(类似与归并排序)的过程了。 -### 文件中的内容排序并去重复 -类似的题目有: -大量的URL字符串,如何从中去除重复的,优化时间空间复杂度 -对2亿条手机号码删除重复记录 + +##### 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。 + +方 案1:这题是考虑时间效率。用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平准长度)。然后是找出出现最频繁的前10个 词,可以用堆来实现,前面的题中已经讲到了,时间复杂度是O(n*lg10)。所以总的时间复杂度,是O(n*le)与O(n*lg10)中较大的哪一 个。 + + +##### 一个文本文件,找出前10个经常出现的词,但这次文件比较长,说是上亿行或十亿行,总之无法一次读入内存,问最优解。 + +方案1:首先根据用hash并求模,将文件分解为多个小文件,对于单个文件利用上题的方法求出每个文件件中10个最常出现的词。然后再进行归并处理,找出最终的10个最常出现的词。 + + + + +##### 寻找热门查询: + +搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较 高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询 串,要求使用的内存不能超过1G。 + +(1) 请描述你解决这个问题的思路; + +(2) 请给出主要的处理流程,算法,以及算法的复杂度。 + +方案1:采用trie树,关键字域存该查询串出现的次数,没有出现为0。最后用10个元素的最小推来对出现频率进行排序。 + + + + +#### 分布式中的top-K + +海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。 + + +方案1: + +s 在每台电脑上求出TOP10,可以采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆)。比如求TOP10大,我们首先取前 10个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元 素就是TOP10大。 + +s 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,再利用上面类似的方法求出TOP10就可以了。 + + + + + + +#### 文件中的内容排序并去重复 1) 如果文件太大,就分片 @@ -108,9 +168,11 @@ int hash_function(const char *p) linux中的几个命令: +``` sort 将文件内容排序 uniq 检查和删除文件内容中重复出现的行列 comm 比较2个已经排序的文件 +``` 对于较大文件 G级别的文件 split -b 把大文件分割成小文件 @@ -119,26 +181,35 @@ sort -m 合并结果 然后在 uniq 命令去重 +类似的题目有: +大量的URL字符串,如何从中去除重复的,优化时间空间复杂度 +对2亿条手机号码删除重复记录 -### 在O(n lg k)时间内,将k个排序表合并成一个排序表,n为所有有序表中元素个数。 + +#### 在O(n lg k)时间内,将k个排序表合并成一个排序表,n为所有有序表中元素个数 + 【解析】取前100 万个整数,构造成了一棵数组方式存储的具有小顶堆,然后接着依次取下一个整数,如果它大于最小元素亦即堆顶元素,则将其赋予堆顶元素,然后用Heapify调整整个堆,如此下去,则最后留在堆中的100万个整数即为所求 100万个数字。该方法可大大节约内存。 - - -### 搜索中的智能提示(如 "中国" ,"zhongguo",考虑拼音) +#### 搜索中的智能提示(如 "中国" ,"zhongguo",考虑拼音) 把2万个汉字排序,弄成一个超长的字符串 + 用Int16索引汉字的所有拼音 + Int64 建立汉字和拼音的关联--汉字有多音字,需要把多个拼音pack到一个Int64,位操作搞定 + 二分+位移unpack,直接做到汉字到拼音的检索 +#### 找出文件中相反的串对 + 一个文件,内含一千万行字符串,每个字符串在1K以内,要求找出所有相反的串对,如abc和cba。 + 分析: 文件大小上限是:1000万*1K ~ 10GB。不可能内存操作 @@ -149,6 +220,8 @@ Int64 建立汉字和拼音的关联--汉字有多音字,需要把多个拼音 +#### 求出这个文件里的整数里不包含的一个整数 + 一个文件中有40亿个整数,每个整数为四个字节,内存为1GB,写出一个算法:求出这个文件里的整数里不包含的一个整数 40亿*4 = 15GB, @@ -190,6 +263,8 @@ getbit(int *bitmap,int i){ +#### 对文件里的整数排序 + 假设一个文件中有9亿条不重复的9位整数,现在要求对这个文件进行排序。 9位整数做多可表示10亿条不重复整数(无符号数) @@ -206,85 +281,50 @@ getbit(int *bitmap,int i){ +#### {url,size} 查询子串并按size排序 +给出一个文件,里面包含两个字段{url、size},即url为网址,size为对应网址访问的次数; -给出一个文件,里面包含两个字段{url、size}, -即url为网址,size为对应网址访问的次数, 要求: + 问题1、利用Linux Shell命令或自己设计算法, 查询出url字符串中包含“baidu”子字符串对应的size字段值; + 问题2、根据问题1的查询结果,对其按照size由大到小的排列。 (说明:url数据量很大,100亿级以上) - - -有一个很大很大的输入流,大到没有存储器可以将其存储下来,而且只输入一次,如何从这个输入流中随机取得m个记录 - +#### 找出文件中的中位数 在一个文件中有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。只写出思路即可。 -一个文件中有40亿个整数,每个整数为四个字节,内存为1GB,写出一个算法:求出这个文件里的整数里不包含的一个整数 - - +#### 找出重复登陆的QQ号 腾讯服务器每秒有2w个QQ号同时上线,找出5min内重新登入的qq号并打印出来。 +#### 海量节点树中寻找共同祖先 + +有一个一亿节点的树,现在已知两个点,找这两个点的共同的祖先。 -淘宝面试题:有一个一亿节点的树,现在已知两个点,找这两个点的共同的祖先。 -海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。(此题请参考本博客内其它文章)。 -某服务器流量统计器,每天有1000亿的访问记录数据,包括时间、url、ip。设计系统实现记录数据的 +#### 设计流量统计系统 + +某服务器流量统计器,每天有1000亿的访问记录数据,包括时间、url、ip。设计系统实现记录数据的保存、管理、查询。要求能实现一下功能: -保存、管理、查询。要求能实现一下功能: (1)计算在某一时间段(精确到分)时间内的,某url的所有访问量。 (2)计算在某一时间段(精确到分)时间内的,某ip的所有访问量。 -假设某个网站每天有超过10亿次的页面访问量,出于安全考虑,网站会记录访问客户端访问的ip地址和对应的时间,如果现在已经记录了1000亿条数据,想统计一个指定时间段内的区域ip地址访问量,那么这些数据应该按照何种方式来组织,才能尽快满足上面的统计需求呢, -设计完方案后,并指出该方案的优缺点,比如在什么情况下,可能会非常慢?(参考答案:用B+树来组织,非叶子节点存储(某个时间点,页面访问量),叶子节点是访问的IP地址。这个方案的优点是查询某个时间段内的IP访问量很快,但是要统计某个IP的访问次数或是上次访问时间就不得不遍历整个树的叶子节点。或者可以建立二级索引,分别是时间和地点来建立索引。) - - - - - - - - - - -18、如何随机选取1000个关键字 -给定一个数据流,其中包含无穷尽的搜索关键字(比如,人们在谷歌搜索时不断输入的关键字)。如何才能从这个无穷尽的流中随机的选取1000个关键字? - - - - - -14、腾讯最新面试题:服务器内存1G,有一个2G的文件,里面每行存着一个QQ号(5-10位数),怎么最快找出出现过最多次的QQ号。 - -以下是个人所建第Algorithms_12群内朋友的聊天记录: - - 首先你要注意到,数据存在服务器,存储不了(内存存不了),要想办法统计每一个qq出现的次数。 -比如,因为内存是1g,首先 你用hash 的方法,把qq分配到10个(这个数字可以变动,比较)文件(在硬盘中)。 - 相同的qq肯定在同一个文件中,然后对每一个文件,只要保证每一个文件少于1g的内存,统计每个qq的次数,可以使用hash_map(qq, qq_count)实现。然后,记录每个文件的最大访问次数的qq,最后,从10个文件中找出一个最大,即为所有的最大。更多读者可以参见此文:海量数据处理面试题集锦与Bit-map详解 。 - - 那若面试官问有没有更高效率的解法之类的?这时,你可以优化一下,但是这个速度很快,hash函数,速度很快,他肯定会问,你如何设计,用bitmap也行。 - - - - - - - +#### a,b文件中共同的记录 1. 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url? @@ -321,15 +361,11 @@ s 对clip_image012[1]这10个文件进行归并排序(内排序与外排序相 -3. 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。 - -方案1:顺序读文件中,对于每个词x,取clip_image014,然后按照该值存到5000个小文件(记为clip_image016) 中。这样每个文件大概是200k左右。如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,知道分解得到的小文件的大小都不超过1M。对 每个小文件,统计每个文件中出现的词以及相应的频率(可以采用trie树/hash_map等),并取出出现频率最大的100个词(可以用含100个结点 的最小堆),并把100词及相应的频率存入文件,这样又得到了5000个文件。下一步就是把这5000个文件进行归并(类似与归并排序)的过程了。 +#### 找出不重复的整数(QQ号) - - -5. 在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.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的整数输出即可。 @@ -338,24 +374,7 @@ s 对clip_image012[1]这10个文件进行归并排序(内排序与外排序相 -6. 海量数据分布在100台电脑中,想个办法高校统计出这批数据的TOP10。 - -方案1: - -s 在每台电脑上求出TOP10,可以采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆)。比如求TOP10大,我们首先取前 10个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元 素就是TOP10大。 - -s 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,再利用上面类似的方法求出TOP10就可以了。 - - - - - - -8. 上千万或上亿数据(有重复),统计其中出现次数最多的钱N个数据。 - -方案1:上千万或上亿的数据,现在的机器的内存应该能存下。所以考虑采用hash_map/搜索二叉树/红黑树等来进行统计次数。然后就是取出前N个出现次数最多的数据了,可以用第6题提到的堆机制完成。 - - +#### 字符串去重 1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现? @@ -363,42 +382,20 @@ s 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合 -10. 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。 -方 案1:这题是考虑时间效率。用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平准长度)。然后是找出出现最频繁的前10个 词,可以用堆来实现,前面的题中已经讲到了,时间复杂度是O(n*lg10)。所以总的时间复杂度,是O(n*le)与O(n*lg10)中较大的哪一 个。 +#### 如何随机选取1000个关键字 + +给定一个数据流,其中包含无穷尽的搜索关键字(比如,人们在谷歌搜索时不断输入的关键字)。如何才能从这个无穷尽的流中随机的选取1000个关键字? +类似的题目还有: -11. 一个文本文件,找出前10个经常出现的词,但这次文件比较长,说是上亿行或十亿行,总之无法一次读入内存,问最优解。 - -方案1:首先根据用hash并求模,将文件分解为多个小文件,对于单个文件利用上题的方法求出每个文件件中10个最常出现的词。然后再进行归并处理,找出最终的10个最常出现的词。 +有一个很大很大的输入流,大到没有存储器可以将其存储下来,而且只输入一次,如何从这个输入流中随机取得m个记录 - -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个数之间的最大差值,要求线性的时间算法。 @@ -416,7 +413,7 @@ s 最大间隙:除最大最小数据max和min以外的n-2个数据放入n-1个 -16. 将多个集合合并成没有交集的集合:给定一个字符串的集合,格式如:clip_image058。要求将其中交集不为空的集合合并,要求合并完成的集合之间无交集,例如上例应输出clip_image060。 +#### 将多个集合合并成没有交集的集合:给定一个字符串的集合,格式如:clip_image058。要求将其中交集不为空的集合合并,要求合并完成的集合之间无交集,例如上例应输出clip_image060。 (1) 请描述你解决这个问题的思路; @@ -429,7 +426,7 @@ s 最大间隙:除最大最小数据max和min以外的n-2个数据放入n-1个 -17. 最大子序列与最大子矩阵问题 +#### 最大子序列与最大子矩阵问题 数组的最大子序列问题:给定一个数组,其中元素有正,也有负,找出其中一个连续子序列,使和最大。 @@ -442,6 +439,17 @@ s 最大间隙:除最大最小数据max和min以外的n-2个数据放入n-1个 +#### 一共有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的。 + + + + + + diff --git a/算法问题选编/矩阵.md b/算法问题选编/矩阵.md new file mode 100644 index 0000000..0c446c0 --- /dev/null +++ b/算法问题选编/矩阵.md @@ -0,0 +1,65 @@ + + +矩阵在计算机中表示就是二维数组。这部分内容都是有关二维数组和矩阵相关的题目。 + + +#### 二维数组中的查找 + +在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 + + +#### 矩阵中最大的二维矩阵 + +求一个矩阵中最大的二维矩阵(元素和最大).如: +``` +1 2 0 3 4 +2 3 4 5 1 +1 1 5 3 0 +``` +中最大的是: +``` +4 5 +5 3 +``` + +要求:(1)写出算法;(2)分析时间复杂度;(3)用C写出关键代码 + + + + +#### 顺时针打印矩阵 + +题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。 +例如:如果输入如下矩阵: + +``` +1 2 3 4 +5 6 7 8 +9 10 11 12 +13 14 15 16 +``` +则依次打印出数字`1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10` 。 + +分析:包括Autodesk、EMC在内的多家公司在面试或者笔试里采用过这道题。 + + + +#### 矩阵式螺旋输出 + + + + +n支队伍比赛,分别编号为0,1,2。。。。n-1,已知它们之间的实力对比关系, +存储在一个二维数组w[n][n]中,w[i][j] 的值代表编号为i,j的队伍中更强的一支。 + +所以w[i][j]=i 或者j,现在给出它们的出场顺序,并存储在数组order[n]中, +比如order[n] = {4,3,5,8,1......},那么第一轮比赛就是 4对3, 5对8。....... + +胜者晋级,败者淘汰,同一轮淘汰的所有队伍排名不再细分,即可以随便排, +下一轮由上一轮的胜者按照顺序,再依次两两比,比如可能是4对5,直至出现第一名 + +编程实现,给出二维数组w,一维数组order 和 用于输出比赛名次的数组result[n],求出result。 + + + + diff --git a/算法问题选编/算法面试100题 b/算法问题选编/算法面试100题 index 9d7afc9..3678df8 100644 --- a/算法问题选编/算法面试100题 +++ b/算法问题选编/算法面试100题 @@ -3,100 +3,6 @@ -int i=(j=4,k=8,l=16,m=32); printf(“%d”, i); 输出是多少? - -逗号运算符 -(表达式1,表达式2,...,表达式n) 分别计算每个表达式,,最后表达式的值做为整个表达式的值 -所以输出是32 - - - - - - - - - -49.一道看上去很吓人的算法面试题: - -如何对n个数进行排序,要求时间复杂度O(n),空间复杂度O(1) - - -3.快速排序(东软喜欢考类似的算法填空题,又如堆排序的算法等) - - - - -1.编写实现链表排序的一种算法。说明为什么你会选择用这样的方法? -2.编写实现数组排序的一种算法。说明为什么你会选择用这样的方法? - - - - - - - - - - -3.收藏了1万条url,现在给你一条url,如何找出相似的url。(面试官不解释何为相似) - -四对括号可以有多少种匹配排列方式?比如两对括号可以有两种:()()和(()) - - -要求设计一个DNS的Cache结构,要求能够满足每秒5000以上的查询,满足IP数据的快速插入,查询的速度要快。 - - - - - -概率分析,随机算法问题 (概率论,数理统计) - -给你5个球,每个球被抽到的可能性为30、50、20、40、10,设计一个随机算法,该算法的输出结果为本次执行的结果。输出A,B,C,D,E即可。 - - -2.已知一随机发生器,产生0的概率是p,产生1的概率是1-p,现在要你构造一个发生器, -使得它构造0和1的概率均为1/2;构造一个发生器,使得它构造1、2、3的概率均为1/3;..., -构造一个发生器,使得它构造1、2、3、...n的概率均为1/n,要求复杂度最低。 - - - - - - -请编写实现malloc()内存分配函数功能一样的代码。 - - -25、用C语言实现函数void * memmove(void *dest, const void *src, size_t n)。memmove -函数的功能是拷贝src所指的内存内容前n个字节到dest所指的地址上。 - -分析:由于可以把任何类型的指针赋给void类型的指针,这个函数主要是实现各种数据类型的拷贝。 - - - - - - - - -42、两个圆相交,交点是A1,A2。现在过A1点做一直线与两个圆分别相交另外一点B1,B2。 - -B1B2可以绕着A1点旋转。问在什么情况下,B1B2最长 - - - - -题目:输入四个点的坐标,求证四个点是不是一个矩形 -关键点: -1.相邻两边斜率之积等于-1, -2.矩形边与坐标系平行的情况下,斜率无穷大不能用积判断。 -3.输入四点可能不按顺序,需要对四点排序。 - - -51、矩阵式螺旋输出 - - - @@ -104,36 +10,6 @@ B1B2可以绕着A1点旋转。问在什么情况下,B1B2最长 /////////////// 系统设计 /////////////////////////////// -一、系统有很多任务,任务之间有依赖,比如B依赖于A,则A执行完后B才能执行 - (1)不考虑系统并行性,设计一个函数(Task *Ptask,int Task_num)不考虑并行度,最快的方法完成所有任务。 - (2)考虑并行度,怎么设计 - typedef struct{ - int ID; - int * child; - int child_num; - }Task; - 提供的函数: - bool doTask(int taskID);无阻塞的运行一个任务; - int waitTask(int timeout);返回运行完成的任务id,如果没有则返回-1; - bool killTask(int taskID);杀死进程 - - - -2.设计一种内存管理算法。 - - - - -1.有不同的手机终端,如iphone,安卓,Symbian,不同的终端处理不一样,设计一种服务器和算法实现对不同的终端的处理。 - - - -3.A向B发邮件,B收到后读取并发送收到,但是中间可能丢失了该邮件,怎么设计一种最节省的方法,来处理丢失问题。 - -4.设计一种算法求出算法复杂度 。 - - - diff --git a/算法问题选编/系统设计.md b/算法问题选编/系统设计.md index cd2dff9..c97e970 100644 --- a/算法问题选编/系统设计.md +++ b/算法问题选编/系统设计.md @@ -1,58 +1,55 @@ +#### 要求设计一个DNS的Cache结构,要求能够满足每秒5000以上的查询,满足IP数据的快速插入,查询的速度要快。 + + +#### 任务调度 系统有很多任务,任务之间有依赖,比如B依赖于A,则A执行完后B才能执行 (1)不考虑系统并行性,设计一个函数(Task *Ptask,int Task_num)不考虑并行度,最快的方法完成所有任务。 (2)考虑并行度,怎么设计 + +``` typedef struct{ int ID; int * child; int child_num; }Task; - 提供的函数: +``` + +提供的函数: +``` bool doTask(int taskID);无阻塞的运行一个任务; int waitTask(int timeout);返回运行完成的任务id,如果没有则返回-1; bool killTask(int taskID);杀死进程 +``` - -设计一种内存管理算法。 +#### 设计一种内存管理算法 +相关的问题: +##### 请编写实现malloc()内存分配函数功能一样的代码 -有不同的手机终端,如iphone,安卓,Symbian,不同的终端处理不一样,设计一种服务器和算法实现对不同的终端的处理。 +##### 用C语言实现函数memmove()函数 +``` +void * memmove(void *dest, const void *src, size_t n) +``` - -A向B发邮件,B收到后读取并发送收到,但是中间可能丢失了该邮件,怎么设计一种最节省的方法,来处理丢失问题。 - - - -设计一种算法求出算法复杂度 。 - - - - -要求设计一个DNS的Cache结构,要求能够满足每秒5000以上的查询,满足IP数据的快速插入,查询的速度要快。 - - - - - -请编写实现malloc()内存分配函数功能一样的代码。 - -用C语言实现函数void * memmove(void *dest, const void *src, size_t n)。memmove -函数的功能是拷贝src所指的内存内容前n个字节到dest所指的地址上。 +memmove()函数的功能是拷贝src所指的内存内容前n个字节到dest所指的地址上。 分析:由于可以把任何类型的指针赋给void类型的指针,这个函数主要是实现各种数据类型的拷贝。 +#### A向B发邮件,B收到后读取并发送收到,但是中间可能丢失了该邮件,怎么设计一种最节省的方法,来处理丢失问题。 +#### 设计一种算法求出算法复杂度 diff --git a/算法问题选编/链表.md b/算法问题选编/链表.md index 2fcb589..9656279 100644 --- a/算法问题选编/链表.md +++ b/算法问题选编/链表.md @@ -1,31 +1,31 @@ 链表常常碰到的问题有: -链表反转 -链表中是否有环 -删除链表中的p节点,在p节点前面插入节点q, 要求O(1)复杂度 - -2个链表相交 -2个链表合并 +* 链表反转 +* 链表中是否有环 +* 删除链表中的p节点,在p节点前面插入节点q, 要求O(1)复杂度 +* 2个链表相交 +* 2个链表合并 -判断俩个链表是否相交 -给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。 -为了简化问题,我们假设俩个链表均不带环。 +#### 判断俩个链表是否相交 + +给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。为了简化问题,我们假设俩个链表均不带环。 问题扩展: + 1.如果链表可能有环列? 2.如果需要求出俩个链表相交的第一个节点列? - +#### 输出两个非降序链表的并集 请修改append函数,利用这个函数实现: -两个非降序链表的并集,1->2->3 和 2->3->5 并为 1->2->3->5 -另外只能输出结果,不能修改两个链表的数据。 +两个非降序链表的并集,1->2->3 和 2->3->5 并为 1->2->3->5 。另外只能输出结果,不能修改两个链表的数据。 +#### 输出链表中倒数第k个结点 题目:输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针。 链表结点定义如下: @@ -36,109 +36,122 @@ struct ListNode }; +#### 链表排序 +Given a head pointer pointing to a linked list ,please write a function that sort the list +in increasing order. You are not allowed to use temporary array or memory copy (微软面试题) -30、微软面试题:Given a head pointer pointing to a linked list ,please write a function that sort the list -in increasing order. You are not allowed to use temporary array or memory copy +``` struct { int data; struct S_Node *next; }Node; + Node * sort_link_list_increasing_order (Node *pheader): +``` +#### 给定单链表,检测是否有环。 + +使用两个指针p1,p2从链表头开始遍历,p1每次前进一步,p2每次前进两步。如果p2到达链表尾部,说明无环,否则p1、p2必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。 -题一、 给定单链表,检测是否有环。 - 使用两个指针p1,p2从链表头开始遍历,p1每次前进一步,p2每次前进两步。 -如果p2到达链表尾部,说明无环,否则p1、p2必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。 - -题二、 给定两个单链表(head1, head2),检测两个链表是否有交点,如果有返回第一个交点。 +类似的题有: + +1 给定两个单链表(head1, head2),检测两个链表是否有交点,如果有返回第一个交点。 + 如果head1==head2,那么显然相交,直接返回head1。 否则,分别从head1,head2开始遍历两个链表获得其长度len1与len2,假设len1>=len2, 那么指针p1由head1开始向后移动len1-len2步,指针p2=head2, 下面p1、p2每次向后前进一步并比较p1p2是否相等,如果相等即返回该结点, 否则说明两个链表没有交点。 -题三、 给定单链表(head),如果有环的话请返回从头结点进入环的第一个节点。 - 运用题一,我们可以检查链表中是否有环。 - - 如果有环,那么p1p2重合点p必然在环中。从p点断开环,方法为:p1=p, p2=p->next, + +2 给定单链表(head),如果有环的话请返回从头结点进入环的第一个节点。 + + 运用题一,我们可以检查链表中是否有环。如果有环,那么p1p2重合点p必然在环中。从p点断开环,方法为:p1=p, p2=p->next, p->next=NULL。此时,原单链表可以看作两条单链表,一条从head开始,另一条从p2开始, 于是运用题二的方法,我们找到它们的第一个交点即为所求。 -题四、只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。 - 办法很简单,首先是放p中数据,然后将p->next的数据copy入p中,接下来删除p->next即可。 -题五、只给定单链表中某个结点p(非空结点),在p前面插入一个结点。 - 办法与前者类似,首先分配一个结点q,将q插入在p后, +#### 删除链表中的p节点 + +只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。 + +办法很简单,首先是放p中数据,然后将p->next的数据copy入p中,接下来删除p->next即可。 + + +类似的还有问题:只给定单链表中某个结点p(非空结点),在p前面插入一个结点。办法类似,首先分配一个结点q,将q插入在p后, 接下来将p中的数据copy入q中,然后再将要插入的数据记录在p中。都可以做到0(1)复杂度 -题六、 链表反转 - 三个指针,遍历一遍(0(n)复杂度) +#### 链表反转 + +三个指针,遍历一遍(0(n)复杂度 -复杂链表的复制 +#### 复杂链表的复制 题目:有一个复杂链表,其结点除了有一个m_pNext指针指向下一个结点外, 还有一个m_pSibling指向链表中的任一结点或者NULL。其结点的C++定义如下: +``` struct ComplexNode { int m_nValue; ComplexNode* m_pNext; ComplexNode* m_pSibling; }; +``` -下图是一个含有5个结点的该类型复杂链表。 -图中实线箭头表示m_pNext指针,虚线箭头表示m_pSibling指针。 -为简单起见,指向NULL的指针没有画出。 -请完成函数ComplexNode* Clone(ComplexNode* pHead),以复制一个复杂链表。 +下图是一个含有5个结点的该类型复杂链表。图中实线箭头表示m_pNext指针,虚线箭头表示m_pSibling指针。为简单起见,指向NULL的指针没有画出。 +请完成函数`ComplexNode* Clone(ComplexNode* pHead)`,以复制一个复杂链表。 -//图,接下来,自会补上。July、11.19. -分析:在常见的数据结构上稍加变化,这是一种很新颖的面试题。 -要在不到一个小时的时间里解决这种类型的题目, -我们需要较快的反应能力,对数据结构透彻的理解以及扎实的编程功底。 - -找出链表的第一个公共结点。 -题目:两个单向链表,找出它们的第一个公共结点。 +#### 找出两个链表的第一个公共结点 -链表的结点定义为: +题目:两个单向链表,找出它们的第一个公共结点。链表的结点定义为: +``` struct ListNode { int m_nKey; ListNode* m_pNext; }; +``` 分析: 第一个公共节点,也就是2个链表中的节点的m_pNext 指向的同一个节点。 -1) 先遍历2个链表,得到各自的长度,和差sub -2) 长链表先遍历sub个节点,然后2个节点一起遍历 -3) 第一次同时指向的同一个节点就是这个commonNode + +1. 先遍历2个链表,得到各自的长度,和差sub +2. 长链表先遍历sub个节点,然后2个节点一起遍历 +3. 第一次同时指向的同一个节点就是这个commonNode -题目:从尾到头输出链表。输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下: + +#### 从尾到头输出链表 + +输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下: + +``` struct ListNode { int m_nKey; ListNode* m_pNext; }; +``` + +思路一: 辅助栈,需要一个栈空间 +思路二: 反转链表,然后遍历 +思路三: 递归实现,将printf语句放在递归调用后面。果然妙极。。 -思路一: 辅助栈,需要一个栈空间 -思路二: 反转链表,然后遍历 -思路三: 递归实现,将printf语句放在递归调用后面。果然妙极。。 - - - - -在O(1)时间内删除链表结点。 +#### 在O(1)时间内删除链表结点 题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下: + +``` struct ListNode { int m_nKey; @@ -146,29 +159,35 @@ struct ListNode }; -函数的声明如下: +//函数的声明如下: void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted); +``` + 思路: 保存下一个节点的值tmp,删除下一个节点,当前节点=tmp +#### 将两链表中data值相同的结点删除 + 有双向循环链表结点定义为: + +``` struct node { int data; struct node *front,*next; }; +```` + 有两个双向循环链表A,B,知道其头指针为:pHeadA,pHeadB,请写一函数将两链表中data值相同的结点删除。 - - -链表和数组的区别在哪里? +#### 链表和数组的区别在哪里? 分析:主要在基本概念上的理解。 但是最好能考虑的全面一点,现在公司招人的竞争可能就在细节上产生,