From 16cf6be47eaadaae9fb63621c6ad60d95e19948b Mon Sep 17 00:00:00 2001 From: nonstriater <510495266@qq.com> Date: Tue, 13 May 2014 09:19:55 +0800 Subject: [PATCH] add --- README.md | 6 +- 排序算法/README.md | 27 +- 算法问题选编/二叉树.md | 4 + 算法问题选编/字符串.md | 27 +- 算法问题选编/数值问题.md | 51 +-- 算法问题选编/数组数列问题.md | 719 ++++++++++++++++++------------ 算法问题选编/测试代码/10.c | 47 ++ 算法问题选编/测试代码/11.c | 47 ++ 算法问题选编/测试代码/12.c | 55 +++ 算法问题选编/测试代码/fibonacci.c | 57 +++ 算法问题选编/测试代码/string.c | 5 +- 算法问题选编/海量数据处理.md | 70 +-- 算法问题选编/链表.md | 2 + 13 files changed, 764 insertions(+), 353 deletions(-) create mode 100644 算法问题选编/测试代码/10.c create mode 100644 算法问题选编/测试代码/11.c create mode 100644 算法问题选编/测试代码/12.c create mode 100644 算法问题选编/测试代码/fibonacci.c diff --git a/README.md b/README.md index cda8c2b..128b3ec 100644 --- a/README.md +++ b/README.md @@ -117,9 +117,10 @@ http://www.chinaunix.net/old_jh/23/437639.html *侧重经典算法的实现* -《Elements of Programming》 STL代码 快 准 狠 +《Elements of Programming》 STL代码 快 准 狠 ,写出的代码可以上层次 《C interfaces and Implementation》 +《The practice of programming》 Brian Kernighan和Rob Pike 《微软的梦工厂》 《Language Implementatin patterns》 @@ -199,5 +200,8 @@ http://www.cs.usfca.edu/~galles/visualization/Algorithms.html http://leetcode.com/ http://oj.leetcode.com/ http://openjudge.cn/ POJ。这有个POJ上的ACM训练方案 http://www.java3z.com/cwbwebhome/article/article19/res041.html +http://ac.jobdu.com/index.php 九度OJ + + diff --git a/排序算法/README.md b/排序算法/README.md index 9ae5ffb..5ee4ab7 100644 --- a/排序算法/README.md +++ b/排序算法/README.md @@ -136,15 +136,7 @@ 递减增量排序算法,对`插入排序`的改进 - - - - -### 基数排序 - - - -### 总结 +### 小结 常见的排序算法都是比较排序,比较排序的时间复杂度通常为 O(n^2) 或 O(nlogn) @@ -152,6 +144,23 @@ +## 3种线性排序算法 + +上面的算法都是基于比较的排序,时间复杂度最好也是 NlogN. + + +### 桶排序 bucket sort + +### 计数排序 counting sort + +### 基数排序 radix sort + + + +###小结 + + + diff --git a/算法问题选编/二叉树.md b/算法问题选编/二叉树.md index fe8e1d4..52b412d 100644 --- a/算法问题选编/二叉树.md +++ b/算法问题选编/二叉树.md @@ -66,6 +66,10 @@ BinaryTreeNode *m_pRight; // right child of node + + + + 13、设计一个算法,找出二叉树上任意两个结点的最近共同父结点。 复杂度如果是O(n2)则不得分。 diff --git a/算法问题选编/字符串.md b/算法问题选编/字符串.md index 631195e..43a652a 100644 --- a/算法问题选编/字符串.md +++ b/算法问题选编/字符串.md @@ -70,8 +70,6 @@ char *delete(char *source,const char *del); - - 题目:在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b。 分析:这道题是2006年google的一道笔试题。 @@ -87,10 +85,6 @@ hash表长度 256,字符直接作为key值 - - - - 题目:匹配兄弟字符串 如果两个字符串的字符一样,但是顺序不一样,被认为是兄弟字符串,问如何在迅速匹配兄弟字符串(如,bad和adb就是兄弟字符串)。思路:判断各自素数乘积是否相等。更多方法请参见:http://blog.csdn.net/v_JULY_v/article/details/6347454。 @@ -112,20 +106,18 @@ BOOL isBrother(const char *first,const char *secd) 设计相应的数据结构和算法,尽量高效的统计一片英文文章(总单词数目)里出现的所有英文单词,按照在文章中首次出现的顺序打印输出该单词和它的出现次数。 -void statistics(FILE *fd) +void statistics(FILE *fd) 字符串原地压缩 题目描述:“eeeeeaaaff" 压缩为 "e5a3f2",请编程实现。 -分析 +分析,主要问题在于字符串的移动 char *compress(const char *src,char *dest) - - -55、字符串匹配实现 +字符串匹配实现 请以俩种方法,回溯与不回溯算法实现。 @@ -218,10 +210,23 @@ outputstr所指的值为123456789 char *delete_character(char *src , char target) +替换空格 +实现一个函数,把每个空格替换成 "%20",如输入“we are happy”,则输出“we%20are%20happy” + + + +寻址重复的元素 +a~z包括大小写与0~9组成的N个数,用最快的方式把其中重复的元素挑出来。 + +hash方法。hash[128], O(N)复杂度 + + + 2.编码实现字符串转整型的函数(实现函数atoi的功能),据说是神州数码笔试题。如将字符 串 ”+123”123, ”-0123”-123, “123CS45”123, “123.45CS”123, “CS123.45”0 + 85.又见字符串的问题 1.给出一个函数来复制两个字符串A和B。 diff --git a/算法问题选编/数值问题.md b/算法问题选编/数值问题.md index e398ea2..9c53373 100644 --- a/算法问题选编/数值问题.md +++ b/算法问题选编/数值问题.md @@ -14,6 +14,7 @@ int string_to_integer(char *s,int length) 请编写能直接实现int atoi(const char * pstr)函数功能的代码。 将字符串转出整数 + 题目:求1+2+…+n, 要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。 @@ -39,10 +40,7 @@ int factorial(n){ 这是一道很基本的考查位运算的面试题。 解法1:一轮循环移位计数 (移位运算比除法运算效率要高,注意要考虑是负数的情况) 解法2:位运算 -解法3:num &= num-1 巧妙之处在于,对高位没有影响 - - - +解法3:num &= num-1 巧妙之处在于,对高位没有影响。不断做 `num &= num-1` 直到num=0 @@ -56,14 +54,6 @@ int factorial(n){ -1024! 末尾有多少个0? - -分析: -末尾0的个数取决于2和5的个数 -能被2整除的数比能被5整除的数要多得多,因此只统计被5整除的数的个数 -?? - - 编程实现:把十进制数(long型)分别以二进制和十六进制形式输出,不能使用printf系列 @@ -79,6 +69,28 @@ 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一样。。 + +方法3:二分查找 O(logn) + + + + +1024! 末尾有多少个0? + +分析: +末尾0的个数取决于2和5的个数 +能被2整除的数比能被5整除的数要多得多,因此只统计被5整除的数的个数 +?? + + + 编程实现两个正整数的除法(和取模) @@ -155,8 +167,8 @@ int div(const int x, const int y) 分析:这是一道广为流传的google面试题。 int one_appear_count(int n); -1. 遍历1~n,统计出现1的个数;n足够大时,效率很低 -2. 分析规律 +思路1. 遍历1~n,统计出现1的个数;n足够大时,效率很低 +思路2. 分析规律 @@ -164,17 +176,6 @@ int one_appear_count(int n); -判断一个自然数是否是某个数的平方。说明:当然不能使用开方运算。 - -square(25) YES -square(35) NO - -方法1: 从1开始遍历,显然这种方法很差 -方法2: 除数跟余数比较,除数从2开始,每一轮都跟结果比较;相等就是存在这个数,不相等就把除数++;时间复杂度为(根号n)。在一个数比较大时,效率不够好。如1194877489=(34567)^2,需要从2开始,一直比较到34566,做3万多次除法和比较运算。跟方法1一样。。 - -方法3:二分查找 O(logn) - - diff --git a/算法问题选编/数组数列问题.md b/算法问题选编/数组数列问题.md index 83bfaf4..11e9448 100644 --- a/算法问题选编/数组数列问题.md +++ b/算法问题选编/数组数列问题.md @@ -1,6 +1,7 @@ +///////// 数组 //////////// -题目:求子数组的最大和 +题目:求子数组的最大和(最大字段和) 输入一个整形数组,数组里有正数也有负数。 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。 求所有子数组的和的最大值。要求时间复杂度为O(n)。 @@ -9,7 +10,53 @@ 因此输出为该子数组的和18。 1. 蛮力法 fmax(i,j) 找出最大的值,3重循环 ,复杂度 0(n^3) -2. +2. maxendindhere保存当前累加的和,如果<0,就把maxendinghere清零 , max保存最终的最大和 + + int maxSumOfVector(int *data,int length){ + + int max=0; + int maxendinghere = 0; + + if(data==NULL || length<=0){ + return 0; + } + for(int i=0;i < length, i++){ + + maxendinghere+=data[i]; + if(maxendinghere<0){ + maxendinghere=0; + continue; + } + + if(maxendinghere>max){ + max=maxendinghere; + } + + } + + return max; + } + + + + + + +查找最小的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)复杂度。 + +答案就是`二叉堆`。我们可以遍历总量中的每个元素来跟二叉堆中的堆顶元素比较(堆顶元素在`小根堆`中最小值,在`大根堆`中是最大值),这样在0(1)复杂度就可以完成查找操作,揭下来需要的操作就是重新调整推结构,复杂度是O(logk),因此整个操作复杂度是 O(n*logk) + +`top-k 小的时候用 *大根堆* ,top-k 大得时候用 *小根堆*` @@ -18,49 +65,306 @@ +【最大/小差问题】 +求相邻元素的最大差值,有无序的实数列V[N],要求求里面大小相邻的实数的差的最大值,关键是要求线性空间和线性时间 -编程求解: -输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来. +如 【9,-1,-11,2】 最大差值 = 2-(-11) = 13 + +最小差 hash合并 +最大差 hash分解 +桶排序 + +`桶排序` 比快排还快,最耗空间 + + + + + + +编程求解:输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来. + +如: n=10,m=25;可能的组合有【】【】【】【】 print_sum_detials(int n , int sum) -动态规划(类似背包问题) +`动态规划`(类似背包问题) + +1. 划分问题 + + +2. 选择状态 + +3. 状态转移方程 + +4. 规划方程 + + + + + +输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。 +要求时间复杂度是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没找到 + + +分析: +数组升序排列,查找可用二分查找,时间复杂度O(logn),这样问题就变成了找其中一个加数的问题,复杂度为N*logN + +巧妙解法:数组2端向中间扫描,复杂度O(N) + + + 二维数组中的查找 +在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 + + + + +题目:定义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 -查找最小的k个元素 -题目:输入n个整数,输出其中最小的k个。 -例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4。 - -print_minimums(int *a,int length ,int k); +给定一个实数数组,按序排列(从小到大),从数组从找出若干个数,使得这若干个数的和与M最为接近,描述一个算法,并给出算法的复杂度。 -题目:输入一个已经按升序排序过的数组和一个数字, -在数组中查找两个数,使得它们的和正好是输入的那个数字。 -要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。 -例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。 +一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5} +是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。 + + +1) 右移,二分查找。 找到最小的数,右移到第一个位置的时候,右移完成 O(N) +2) 直接二分查找 -给出两个集合A和B,其中集合A={name}, -集合B={age、sex、scholarship、address、...}, -要求: -问题1、根据集合A中的name查询出集合B中对应的属性信息; -问题2、根据集合B中的属性信息(单个属性,如age<20等),查询出集合A中对应的name。 +数组中超过出现次数超过一半的数字 +题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。 + +分析:这是一道广为流传的面试题,包括百度、微软和Google在内的多家公司 +都曾经采用过这个题目。要几十分钟的时间里很好地解答这道题,除了较好的编程能力之外, +还需要较快的反应和较强的逻辑思维能力。 + ++-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)时间内找出其中任意一个重复数。 +类似问题:找出数组中唯一的重复元素 + +hash算法,空间要求多 (注意数组元素要位 int) +高级解法: 将问题转化为 `判断单链表中存在环` + + + +给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里 + +分析:扑克牌54张【2~10,J,Q,K,A,小王,大王】 + +1)产生随机数, 随机数 rand()%54 ,rand()每次运行都一样,要改为srand(time(NULL)) +2) 遍历数组, 随机数k属于区间[i,n],然后a[i] 和 随机数 a[k] 对换 + + + +在一维坐标轴上有n个区间段,求重合区间最长的两个区间段 +如【-7,21】,【4,23】,【14,100】,【54,76】 + +思路一:两两比较,复杂度 N^2 +思路二:先排序+分而治之 + + + +奇偶分离。给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。 +要求:空间复杂度O(1),时间复杂度为O(n) +如 [4,5,2,7,5] => [5,7,5,4,2], +空间复杂度O(1),得使用 交换排序 + +插入排序思想 +快速排序思想 + + + + + +1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次. +每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间, +能否设计一个算法实现? + +分析:难点在于 `不用辅助空间`。 + +思路一: sum(数组元素的总和)-sum(1~1000) 得到的差即为重复元素,N较大时注意总和溢出 +思路二: 异或操作(位运算) + + + + +找出数组中两个只出现一次的数字 +题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。 +请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。 + +分析:这是一道很新颖的关于位运算的面试题。 + +1) 任何一个数字异或自己都等于0,如果是只有1个只出现一次的的数字,问题就简单了。 +2) + + + + +用递归的方法判断整数组a[N]是不是升序排列 + +递归 isAscend(n-1) && a[N-1]< a[N] + + + + +在一个int数组里查找这样的数,它大于等于左侧所有数,小于等于右侧所有数。 +直观想法是用两个数组a、b。a[i]、b[i]分别保存从前到i的最大的数和从后到i的最小的数, +一个解答:这需要两次遍历,然后再遍历一次原数组, +将所有data[i]>=a[i-1]&&data[i]<=b[i]的data[i]找出即可。 +给出这个解答后,面试官有要求只能用一个辅助数组,且要求少遍历一次。 + + + + +一排N(最大1M)个正整数+1递增,乱序排列,第一个不是最小的,把它换成-1, +最小数为a且未知。求第一个被-1替换掉的数原来的值,并分析算法复杂度。 + +[4,3,5,2,7,6] +题目啥意思? + + + +正整数序列Q中的每个元素都至少能被正整数a和b中的一个整除,现给定a和b,需要计算出Q中的前几项,例如,当a=3,b=5,N=6时,序列为3,5,6,9,10,12 +(1)、设计一个函数void generate(int a,int b,int N ,int * Q)计算Q的前几项 +(2)、设计测试数据来验证函数程序在各种输入下的正确性。 + +分析: +这个输出序列是要 递增排列 +思路一: 类似对2各数组merge,取min( A[i],B[j]) .复杂度O(N) +不过这里要注意,去掉 a, b的公倍数。如 3,5 都有15可以整除 + + + + +在排序数组中,找出给定数字的出现次数。比如 [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,而大小王可以看成任意数字。 + +对这5个数(大小王看做0) +1)排序,如快排 +2)统计0的个数 +3)统计相邻元素空缺总数 + +isShunZi(int *a,int length)//5个是不是顺子 + + + + +2.n个骰子的点数。 +把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。 +输入n,打印出S的所有可能的值出现的概率。 + +如n=1,值【1,2,3,4,5,6】每个概率是 1/6 +如n=2,值【1~12】 概率。。 + + + + +把数组排成最小的数。 +题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。 +例如输入数组{32, 321},则输出这两个能排成的最小数字32132。 +请给出解决问题的算法,并证明该算法。 + +分析:这是09年6月份百度的一道面试题, +从这道题我们可以看出百度对应聘者在算法方面有很高的要求。 + + + + + + + +旋转数组中的最小元素。 + +题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 +输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。 +例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。 + +分析:这道题最直观的解法并不难。从头到尾遍历数组一次,就能找出最小的元素, +时间复杂度显然是O(N)。但这个思路没有利用输入数组的特性,我们应该能找到更好的解法。 + + + + + + +数值的整数次方。 +题目:实现函数double Power(double base, int exponent),求base的exponent次方。 +不需要考虑溢出。 + +分析:这是一道看起来很简单的问题。可能有不少的人在看到题目后30秒写出如下的代码: +double Power(double base, int exponent) +{ + double result = 1.0; + for(int i = 1; i <= exponent; ++i) + result *= base; + return result; +} + +上面的代码没有考虑 exponent<0的情况。 + + + + + + + + +////////////// 数列 /////////////// + 输入a1,a2,...,an,b1,b2,...,bn, 在O(n)的时间,O(1)的空间将这个序列顺序改为a1,b1,a2,b2,a3,b3,...,an,bn, 且不需要移动,通过交换完成,只需一个交换空间。 @@ -87,7 +391,27 @@ print_minimums(int *a,int length ,int k); 2在下排出现了1次,3在下排出现了0次.... 以此类推.. +0xX0+1xX1+2xX2....+9xX9 = 10; +X0+X1+...+ = 10; +转换成多元一次方程求通解的问题。(Matlib) + + + + +一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值 + 比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1; + {3,6}{2,4,3} m=2 + {3,3}{2,4}{6} m=3 所以m的最大值为3 + + + + +给出两个集合A和B,其中集合A={name}, +集合B={age、sex、scholarship、address、...}, +要求: +问题1、根据集合A中的name查询出集合B中对应的属性信息; +问题2、根据集合B中的属性信息(单个属性,如age<20等),查询出集合A中对应的name。 @@ -130,12 +454,6 @@ var b=[1, 2, 3, 4,5,40]; -一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值 - 比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1; - {3,6}{2,4,3} m=2 - {3,3}{2,4}{6} m=3 所以m的最大值为3 - - n支队伍比赛,分别编号为0,1,2。。。。n-1,已知它们之间的实力对比关系, @@ -151,11 +469,6 @@ n支队伍比赛,分别编号为0,1,2。。。。n-1,已知它们之间 -一个int数组,里面数据无任何限制,要求求出所有这样的数a[i], -其左边的数都小于等于它,右边的数都大于等于它。 -能否只用一个额外数组和少量其它空间实现。 - - 一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序 比如: input: 1,7,-5,9,-12,15 ans: -5,-12,1,7,9,15 要求时间复杂度O(N),空间O(1) 。(此题一直没看到令我满意的答案,一般达不到题目所要求的:时间复杂度O(N),空间O(1),且保证原来正负数之间的相对位置不变)。 @@ -183,24 +496,15 @@ updated:设置一个起始点j, 一个翻转点k,一个终止点L 更多请参考此文,程序员编程艺术第二十七章:重新排列数组(不改变相对顺序&时间O(N)&空间O(1),半年未被KO)http://blog.csdn.net/v_july_v/article/details/7329314。 -给定一个实数数组,按序排列(从小到大),从数组从找出若干个数,使得这若干个数的和与M最为接近,描述一个算法,并给出算法的复杂度。 - -1. 有无序的实数列V[N],要求求里面大小相邻的实数的差的最大值,关键是要求线性空间和线性时间 - -3. 有一个函数int getNum(),每运行一次可以从一个数组V[N]里面取出一个数,N未知,当数取完的时候,函数返回NULL。现在要求写一个函数int get(),这个函数运行一次可以从V[N]里随机取出一个数,而这个数必须是符合1/N平均分布的,也就是说V[N]里面任意一个数都有1/N的机会被取出,要求空间复杂度为O(1) - - - - -21、搜狗笔试题:一个长度为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]的积(就是除掉当前元素,其他所有元素的积)。程序要求:具有线性复杂度,且不能使用除法运算符。 +搜狗笔试题:一个长度为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]时,直接出结果。 -22、后2012年4月6日的腾讯暑期实习生招聘笔试中,出了一道与上述21题类似的题,原题大致如下: +后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]; 要求: @@ -234,7 +538,6 @@ b[0] *= a[1]; -94.微软笔试题 求随机数构成的数组中找到长度大于=3的最长的等差数列9 d- x' W) w9 ?" o3 b0 R 输出等差数列由小到大: 如果没有符合条件的就输出 @@ -246,65 +549,6 @@ b[0] *= a[1]; -93.在一个int数组里查找这样的数,它大于等于左侧所有数,小于等于右侧所有数。 -直观想法是用两个数组a、b。a[i]、b[i]分别保存从前到i的最大的数和从后到i的最小的数, -一个解答:这需要两次遍历,然后再遍历一次原数组, -将所有data[i]>=a[i-1]&&data[i]<=b[i]的data[i]找出即可。 -给出这个解答后,面试官有要求只能用一个辅助数组,且要求少遍历一次。 - - - - -第19题: -题目:定义Fibonacci数列如下: - / 0 n=0 -f(n)= 1 n=1 - / f(n-1)+f(n-2) n=2 - -输入n,用最快的方法求该数列的第n项。 -分析:在很多C语言教科书中讲到递归函数的时候,都会用Fibonacci作为例子。 -因此很多程序员对这道题的递归解法非常熟悉,但....呵呵,你知道的。。 - - -寻址重复的元素 -1.a~z包括大小写与0~9组成的N个数 -用最快的方式把其中重复的元素挑出来。 - - - - -74.数组中超过出现次数超过一半的数字 -题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。 - -分析:这是一道广为流传的面试题,包括百度、微软和Google在内的多家公司 -都曾经采用过这个题目。要几十分钟的时间里很好地解答这道题,除了较好的编程能力之外, -还需要较快的反应和较强的逻辑思维能力。 - -+-1 ,计数法 - - - - -题目大意如下: -一排N(最大1M)个正整数+1递增,乱序排列,第一个不是最小的,把它换成-1, -最小数为a且未知求第一个被-1替换掉的数原来的值,并分析算法复杂度。 - - -4.给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里。 - - - - -在一维坐标轴上有n个区间段,求重合区间最长的两个区间段。 - - - - -16、正整数序列Q中的每个元素都至少能被正整数a和b中的一个整除,现给定a和b,需要计算出Q中的前几项,例如,当a=3,b=5,N=6时,序列为3,5,6,9,10,12 -(1)、设计一个函数void generate(int a,int b,int N ,int * Q)计算Q的前几项 -(2)、设计测试数据来验证函数程序在各种输入下的正确性。 - - 57、求最大重叠区间大小 @@ -319,148 +563,48 @@ f(n)= 1 n=1 -给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。 -要求:空间复杂度O(1),时间复杂度为O(n)。 - - -48、找出数组中唯一的重复元素 - - -1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次. -每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间, -能否设计一个算法实现? - - -2.用递归的方法判断整数组a[N]是不是升序排列 - - -给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。 -要求:空间复杂度O(1),时间复杂度为O(n)。 - - -35. -求一个矩阵中最大的二维矩阵(元素和最大).如: -1 2 0 3 4 -2 3 4 5 1 -1 1 5 3 0 -中最大的是: -4 5 -5 3 -要求:(1)写出算法;(2)分析时间复杂度;(3)用C写出关键代码 - - - -1.对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)某一个元素也加一, - -现给出一正数矩阵,判断其是否能够由一个全零矩阵经过上述运算得到。 - - - -38、顺时针打印矩阵 -题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。 -例如:如果输入如下矩阵: - -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在内的多家公司在面试或者笔试里采用过这道题。 - - - - -一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5} -是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。 - - - - -俩个闲玩娱乐。 -1.扑克牌的顺子 -从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。 -2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。 - -2.n个骰子的点数。 -把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。 -输入n,打印出S的所有可能的值出现的概率。 - - - -68.把数组排成最小的数。 -题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。 -例如输入数组{32, 321},则输出这两个能排成的最小数字32132。 -请给出解决问题的算法,并证明该算法。 - -分析:这是09年6月份百度的一道面试题, -从这道题我们可以看出百度对应聘者在算法方面有很高的要求。 - - - - -69.旋转数组中的最小元素。 - -题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 -输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。 -例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。 - -分析:这道题最直观的解法并不难。从头到尾遍历数组一次,就能找出最小的元素, -时间复杂度显然是O(N)。但这个思路没有利用输入数组的特性,我们应该能找到更好的解法。 - - - - -71.数值的整数次方。 -题目:实现函数double Power(double base, int exponent),求base的exponent次方。 -不需要考虑溢出。 - -分析:这是一道看起来很简单的问题。可能有不少的人在看到题目后30秒写出如下的代码: -double Power(double base, int exponent) -{ - double result = 1.0; - for(int i = 1; i <= exponent; ++i) - result *= base; - return result; -} - - - - - - - -61.找出数组中两个只出现一次的数字 -题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。 -请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。 - -分析:这是一道很新颖的关于位运算的面试题。 - - - -64. 寻找丑数。 -题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。 -例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。 -求按从小到大的顺序的第1500个丑数。 - -分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。 - - - - -65.输出1到最大的N位数 -题目:输入数字n,按顺序输出从1最大的n位10进制数。 -比如输入3,则输出1、2、3一直到最大的3位数即999。 +输出1到最大的N位数 +题目:输入数字n,按顺序输出从1最大的n位10进制数。比如输入3,则输出1、2、3一直到最大的3位数即999。 分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。 +输入4,输出: 1,2,3,。。9999 +输入5,输出: 1,2,3,4,...99999 - +玄机一: 整数溢出 -一个有序数列,序列中的每一个值都能够被2或者3或者5所整除,1是这个序列的第一个元素。求第1500个值是多少? + + +和为n连续正数序列。 +题目:输入一个正数n,输出所有和为n连续正数序列。 + +例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。 + +print_continuous_sequence_sum(int n) + + +思路一:枚举法。从1开始一直加到等于n,再从2开始。。一直到 n/2+1,在每一轮,都要逐步比较。复杂度O(N^2) + +思路二: a[small,big] sum[small,big]>N small往前移动,否则,big往前移动。 O(N)复杂度搞定 + + + + +一个整数数列,元素取值可能是1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。 +设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于N+1。 +复杂度最好是O(n),如果是O(n2)则不得分。 + +分析:列出所有的数对,如输入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,... @@ -477,83 +621,106 @@ unsigned long value_in_sequence(int index) -和为n连续正数序列。 -题目:输入一个正数n,输出所有和为n连续正数序列。 + +寻找丑数 +题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。 +例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。 +求按从小到大的顺序的第1500个丑数。 -例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。 +分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。 + +这里的因子应该不包含本身,因此这个序列应该是这样: +1,2,3,4,5,6,8,9,10,12,15,16,18,20,28.... -54.调整数组顺序使奇数位于偶数前面。 - +调整数组顺序使奇数位于偶数前面。 题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分, - 所有偶数位于数组的后半部分。要求时间复杂度为O(n)。 - -47.创新工场: -求一个数组的最长递减子序列 比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2} +思路:两边向中间扫描,如果第一个指针是偶数,第二个指针是奇数,就交换;如果第一个是偶数,第二个也偶数,第二个指针向前移;反之,第一个指针向后移 + +void reorder(int *data, int length); -有一个整数数组,请求出两两之差绝对值最小的值, -记住,只要得出最小值即可,不需要求出是哪两个数。 +求一个数组的最长递减子序列。 比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2} +`动态规划` + + + + +有一个整数数组,请求出两两之差绝对值最小的值,记住,只要得出最小值即可,不需要求出是哪两个数。 如 [-1, 3, 5, 9] 绝对值最小的是2(5-3) + 最短区间问题 如果是有重复元素,那最小值就是0了 +解题思路: 可以将这个问题转化为 ”求最大字段和“ 问题。。。 - -12、一个整数数列,元素取值可能是0~65535中的任意一个数,相同数值不会重复出现。0是例外,可以反复出现。 -请设计一个算法,当你从该数列中随意选取5个数值,判断这5个数值是否连续相邻。 +一个整数数列,元素取值可能是0~65535中的任意一个数,相同数值不会重复出现。0是例外,可以反复出现。请设计一个算法,当你从该数列中随意选取5个数值,判断这5个数值是否连续相邻。 注意: - 5个数值允许是乱序的。比如: 8 7 5 0 6 - 0可以通配任意数值。比如:8 7 5 0 6 中的0可以通配成9或者4 - 0可以多次出现。 - 复杂度如果是O(n2)则不得分。 - - -一个整数数列,元素取值可能是1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。 -设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于N+1。 -复杂度最好是O(n),如果是O(n2)则不得分。 +这个问题跟”扑克牌顺子“判断问题一样,通过比较0的个数和相邻数字之间间隔总和来判断所有数是否连续。 -56、一个含n个元素的整数数组至少存在一个重复数, -请编程实现,在O(n)时间内找出其中任意一个重复数。 - - - - -在排序数组中,找出给定数字的出现次数 -比如 [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); - - - - -53、最长递增子序列 - +最长递增子序列 题目描述:设L=是n个不同的实数的序列,L的递增子序列是这样一个子序列 - -Lin=,其中k1,其中k1< k2<…< km且aK1< ak2<…< akm。 求最大的m值。 +如【5,6,7,3,2,8】 最长子序列 【5,6,7,8】 +`动态规划` + + + +/////////////////////// 矩阵 ///////////////////////////////// + + +求一个矩阵中最大的二维矩阵(元素和最大).如: +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在内的多家公司在面试或者笔试里采用过这道题。 diff --git a/算法问题选编/测试代码/10.c b/算法问题选编/测试代码/10.c new file mode 100644 index 0000000..acc94dc --- /dev/null +++ b/算法问题选编/测试代码/10.c @@ -0,0 +1,47 @@ + + +#include "stdio.h" + +void generate(int a,int b,int N ,int * Q){ + + int p=1,q=1; + int tmpA,tmpB; + + for (int i = 0; i < N; ++i) + { + tmpA = a*p; + tmpB = b*q; + + if (tmpA>tmpB) + { + Q[i]=tmpB; + q++; + + } + else if(tmpAn){ + sum-=small; + small++; + if (sum==n) + { + printf("%d, %d\n",small,big); + } + + } + + big++; + sum+=big; + + } + + + +} + + + +int main(int argc, char const *argv[]) +{ + + + print_continuous_sequence_sum(115); + + return 0; +} + diff --git a/算法问题选编/测试代码/12.c b/算法问题选编/测试代码/12.c new file mode 100644 index 0000000..9550fb0 --- /dev/null +++ b/算法问题选编/测试代码/12.c @@ -0,0 +1,55 @@ + +#include "stdio.h" + + +void reorder(int *data, int length){ + + int i=0; + int j=length-1; + + while(i HASHLEN) + value = value % HASHLEN; + } + return value; +} + +这个hash函数要确保 不同的字符串 hash出不同的一个 整数。 -有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。 -请用5分钟时间,找出重复出现最多的前10条。 -寻找热门查询,300万个查询字符串中统计最热门的10个查询 +### 文件中的内容排序并去重复 +类似的题目有: +大量的URL字符串(电话号码),如何从中去除重复的,优化时间空间复杂度 + + + + +linux中的几个命令: +sort 将文件内容排序 +uniq 检查和删除文件内容中重复出现的行列 +comm 比较2个已经排序的文件 + +对于较大文件 G级别的文件 +split -b 把大文件分割成小文件 +然后用 sort 分别排序 +sort -m 合并结果 +然后在 uniq 命令去重 -文件中的内容排序并去重复 - - -一个文件中包含了1亿个随机整数,如何快速的找到最大(小)的100万个数字?(时间复杂度:O(n lg k)) -分析: 堆排序 - - -3.2 在O(n lg k)时间内,将k个排序表合并成一个排序表,n为所有有序表中元素个数。 +### 在O(n lg k)时间内,将k个排序表合并成一个排序表,n为所有有序表中元素个数。 【解析】取前100 万个整数,构造成了一棵数组方式存储的具有小顶堆,然后接着依次取下一个整数,如果它大于最小元素亦即堆顶元素,则将其赋予堆顶元素,然后用Heapify调整整个堆,如此下去,则最后留在堆中的100万个整数即为所求 100万个数字。该方法可大大节约内存。 -搜索中的智能提示(如 "中国" ,"zhongguo",考虑拼音) +### 搜索中的智能提示(如 "中国" ,"zhongguo",考虑拼音) 把2万个汉字排序,弄成一个超长的字符串 用Int16索引汉字的所有拼音 @@ -114,11 +131,6 @@ Int64 建立汉字和拼音的关联--汉字有多音字,需要把多个拼音 - -3.大量的URL字符串,如何从中去除重复的,优化时间空间复杂度 - - - 2.一个文件,内含一千万行字符串,每个字符串在1K以内, 要求找出所有相反的串对,如abc和cba。 @@ -130,7 +142,7 @@ Int64 建立汉字和拼音的关联--汉字有多音字,需要把多个拼音 -2.给出一个文件,里面包含两个字段{url、size}, +给出一个文件,里面包含两个字段{url、size}, 即url为网址,size为对应网址访问的次数, 要求: 问题1、利用Linux Shell命令或自己设计算法, @@ -141,7 +153,7 @@ Int64 建立汉字和拼音的关联--汉字有多音字,需要把多个拼音 -3.有10个文件,每个文件1G, +有10个文件,每个文件1G, 每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。 要求按照query的频度排序. diff --git a/算法问题选编/链表.md b/算法问题选编/链表.md index 98600e4..6756a68 100644 --- a/算法问题选编/链表.md +++ b/算法问题选编/链表.md @@ -135,6 +135,8 @@ struct ListNode 该题以及它的变体经常出现在各大公司的面试、笔试题中。 + +从尾到头打印链表