diff --git a/9 Algorithms Job Interview/4 数值问题.md b/9 Algorithms Job Interview/4 数值问题.md index 60bedec..afe39d6 100644 --- a/9 Algorithms Job Interview/4 数值问题.md +++ b/9 Algorithms Job Interview/4 数值问题.md @@ -51,7 +51,6 @@ int one_appear_count_by_binary(int num){ - ## 把十进制数(long型)分别以二进制和十六进制形式输出,不能使用printf系列 分析: @@ -103,4 +102,45 @@ tmp[i] = tmp[i]>=0 ? tmp[i] : tmp[i]+16; +## 输出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的数称作丑数(Ugly Number)。 +例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。 +求按从小到大的顺序的第1500个丑数。 + +分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。 + +这里的因子应该不包含本身,因此这个序列应该是这样: +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` + + + + + + diff --git a/9 Algorithms Job Interview/5 数组数列问题.md b/9 Algorithms Job Interview/5 数组数列问题.md index fad9f1d..c4ecd31 100644 --- a/9 Algorithms Job Interview/5 数组数列问题.md +++ b/9 Algorithms Job Interview/5 数组数列问题.md @@ -19,173 +19,28 @@ -## 使用归并排序对一个 int 类型的数组排序 - -比如 [1, 6, 2, 2, 2, 3] +## Fibonacci数列 + +题目:定义Fibonacci数列如下: ``` -void sort(int * a, int length) + 0 n=0 + f(n)= 1 n=1 + f(n-1)+f(n-2) n=2 ``` +输入n,用最快的方法求该数列的第n项。 +虽然fibonacci数列是`递归`的经典应用,但递归效率很差,会有很多重复的计算,复杂度是成指数递增的,我测试了下计算50的时候已经要300s了。 -## 数组中超过出现次数超过一半的数字 +思路二:从下往上计算,复杂度O(N),一个循环就搞定 -题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。 +思路三: logN -`+-1 计数法` -## 找数组里面重复的一个数 - -找数组里面重复的一个数,一个含n个元素的整数数组至少存在一个重复数,请编程实现,在O(n)时间内找出其中任意一个重复数。 - -1. hash算法,空间要求多 (注意数组元素要是int类型),数的大小范围和数组长度N都可以是无穷大,这里使用hash算法,空间复杂度是O(n),空间可能无法满足条件。 -2. 先排序,复杂度n(logn);然后遍历一遍就可以知道哪些数重复了。 -2. 高级解法: 将问题转化为 `判断单链表中存在环` - - -类似问题:找出数组中唯一的重复元素 - - - -## 数组中找出某数字出现的次数 - -在排序数组中,找出给定数字的出现次数。比如 [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); -``` - - - - -## 大于K的最小正整数 - -给定一个集合A=[0,1,3,8](该集合中的元素都是在0,9之间的数字,但未必全部包含),指定任意一个正整数K,请用A中的元素组成一个大于K的最小正整数。 - -比如,A=[1,0] K=21 那么输出结构应该为100。 - - - - - -## 查找最小的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 大得时候用 *小根堆*` - - -## 找第k大的数 - -比如 1,2,3,4,5,6,7和8这8个数字,低3大的数字是 6 - -``` -int topK(int * a, int length, int k) -``` - - - -## 最长公共子序列 (动态规划的经典题目) - -【最大/小差问题】 -求相邻元素的最大差值,有无序的实数列V[N],要求求里面大小相邻的实数的差的最大值,关键是要求线性空间和线性时间 - -如 【9,-1,-11,2】 最大差值 = 2-(-11) = 13 - -最小差 hash合并 -最大差 hash分解 -桶排序 - -`桶排序` 比快排还快,最耗空间 - - - -## 找出和为m的2个数 - -输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。 -要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。 -例如输入数组【1、2、4、7、11、15=和数字15。由于4+11=15,因此输出4和11。 - - -``` -//返回0找到,返回-1没找到 -int findaddends(int *data,int length,int sum,int *a,int *b); -``` - -分析: -数组升序排列,查找可用二分查找,时间复杂度`O(logn)`,这样问题就变成了找其中一个加数的问题,复杂度为`N*logN` - -巧妙解法:数组2端向中间扫描,复杂度O(N) - - -## 求连续子数组和为m的组合 - -数组 [2,3,1,2,4,3]求连续子数组sum=7, 结果[1,2,4] [3,4] - -* 暴力遍历 -* 滑动窗口, 左指针,右指针 - -``` -if summax){ - max=maxendinghere; - } - - } - - return max; - } -``` +2. 直接二分查找 @@ -259,56 +75,6 @@ print_sum_detials(int n , int sum) -## 数组分隔成和相等的2个子集 - -给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等 (eetcode 416, 难度中等) - -思路: 动态规划 - - - -## 奇偶分离 - -给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。 -要求:空间复杂度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数组里查找这样的数,它大于等于左侧所有数,小于等于右侧所有数。 @@ -340,32 +106,6 @@ print_sum_detials(int n , int sum) -## 扑克牌的顺子 - -从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。 -2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。 - -对这5个数(大小王看做0) -1)排序,如快排 -2)统计0的个数 -3)统计相邻元素空缺总数 - -``` -//5个是不是顺子 -isShunZi(int *a,int length) -``` - - - -## n个骰子的点数 - -把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。 -输入n,打印出S的所有可能的值出现的概率。 - -如n=1,值`【1,2,3,4,5,6】`每个概率是 `1/6` -如n=2,值`【1~12】` 概率。。 - - ## 把数组排成最小的数 @@ -390,15 +130,6 @@ isShunZi(int *a,int length) -## 分割数组 - -一个int数组,里面数据无任何限制,要求求出所有这样的数a[i],其左边的数都小于等于它,右边的数都大于等于它。 -能否只用一个额外数组和少量其它空间实现。 - -`快速排序` - - - ## 约瑟夫环问题 n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始, @@ -418,69 +149,6 @@ n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始, -## 圆形和正方形是否相交 - -用最简单, 最快速的方法计算出下面这个圆形是否和正方形相交 -3D坐标系 原点(0.0,0.0,0.0) -圆形: -半径r = 3.0 -圆心o = (*.*, 0.0, *.*) - -正方形: -4个角坐标; -1:(*.*, 0.0, *.*) -2:(*.*, 0.0, *.*) -3:(*.*, 0.0, *.*) -4:(*.*, 0.0, *.*) - -分析: - -2个形状不相交: - - - -## 两个序列和只差最小 - -有两个序列a,b,大小都为n,序列元素的值任意整数,无序; -要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。 -例如: -``` -var a=[100,99,98,1,2, 3]; -var b=[1, 2, 3, 4,5,40]; -``` - - - -## 重新排列使负数排在正数前面 - -一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序 - -比如: 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 -从右侧起 -起始点在第一个出现的负数, 翻转点在起始点后第一个出现的正数,终止点在翻转点后出现的第一个负数(或结束) -如果无翻转点, 则不操作 -如果有翻转点, 则待终止点出现后, 做翻转, 即ab => ba 这样的操作 -翻转后, 负数串一定在左侧, 然后从负数串的右侧开始记录起始点, 继续往下找下一个翻转点 - -例子中的就是 - -1, 7, -5, 9, -12, 15 -第一次翻转: 1, 7, -5, -12,9, 15 => 1, -12, -5, 7, 9, 15 -第二次翻转: -5, -12, 1, 7, 9, 15 - - N维翻转空间占用为O(1)复杂度是2N;在有一个负数的情况下, 复杂度最大是2N, ;在有i个负数的情况下, 复杂度最大是2N+2i, 但是不会超过2N+N实际的复杂度在O(3N)以内 - 但从最终时间复杂度分析,此方法是否真能达到O(N)的时间复杂度,还待后续考证。感谢John_Lv,MikovChain。2012.02.25。 - -1, 7, -5, -6, 9, -12, 15(后续:此种情况未能处理) -1 7 -5 -6 -12 9 15 -1 -12 -5 -6 7 9 15 --6 -12 -5 1 7 9 15 - -更多请参考此文,程序员编程艺术第二十七章:重新排列数组(不改变相对顺序&时间O(N)&空间O(1),半年未被KO)http://blog.csdn.net/v_july_v/article/details/7329314。 - - ## 求最大重叠区间大小 @@ -498,116 +166,8 @@ updated:设置一个起始点j, 一个翻转点k,一个终止点L 比如两对括号可以有两种:()()和(()) - -## 输出1到最大的N位数 - -题目:输入数字n,按顺序输出从1最大的n位10进制数。比如输入3,则输出1、2、3一直到最大的3位数即999。 - -分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。 -输入4,输出: 1,2,3,。。9999 -输入5,输出: 1,2,3,4,...99999 - -玄机一: 整数溢出 - - - - -## 和为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)` 复杂度搞定 - - -## 找出和为N+1的2个数 - -一个整数数列,元素取值可能是`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的数称作丑数(Ugly Number)。 -例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。 -求按从小到大的顺序的第1500个丑数。 - -分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。 - -这里的因子应该不包含本身,因此这个序列应该是这样: -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}` - -`动态规划` - - - -## 如何求2个集合的交集 - -1.每个集合里面是否有重复元素? - -思路一:hash,复杂度O(M+N) - ## 两两之差绝对值最小的值 @@ -635,46 +195,6 @@ int one_appear_count(int n); -## 最长递增子序列 - -题目描述:设L=是n个不同的实数的序列,L的递增子序列是这样一个子序列 -Lin=< aK1,ak2,…,akm >,其中k1< k2<…< km且aK1< ak2<…< akm。 -求最大的m值。 - -如【5,6,7,3,2,8】 最长子序列 【5,6,7,8】 - -`动态规划` - - - - -## Fibonacci数列 - -题目:定义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 - - - - - -## 一个整数数组,长度为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 - - - ////////////// 数列 /////////////// diff --git a/9 Algorithms Job Interview/5.1 数列-排序.md b/9 Algorithms Job Interview/5.1 数列-排序.md new file mode 100644 index 0000000..c4d2950 --- /dev/null +++ b/9 Algorithms Job Interview/5.1 数列-排序.md @@ -0,0 +1,128 @@ +# 5.1 数列-排序 + + +## 使用归并排序对一个 int 类型的数组排序 + + +比如 [1, 6, 2, 2, 2, 3] + +``` +void sort(int * a, int length) +``` + + + +## 用递归的方法判断整数组a[N]是不是升序排列 + +递归 isAscend(n-1) && a[N-1]< a[N] + + + + +## 求一个数组的最长递减子序列 + + 比如`{9,4,3,2,5,4,3,2}`的最长递减子序列为`{9,5,4,3,2}` + +`动态规划` + + + + + +## 扑克牌的顺子 + +从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。 +2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。 + +对这5个数(大小王看做0) +1)排序,如快排 +2)统计0的个数 +3)统计相邻元素空缺总数 + +``` +//5个是不是顺子 +isShunZi(int *a,int length) +``` + + + + +## 分割数组 + +一个int数组,里面数据无任何限制,要求求出所有这样的数a[i],其左边的数都小于等于它,右边的数都大于等于它。 +能否只用一个额外数组和少量其它空间实现。 + +`快速排序` + + + + + +## 调整数组顺序使奇数位于偶数前面 + +题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分, +所有偶数位于数组的后半部分。要求时间复杂度为O(n)。 + +思路:两边向中间扫描,如果第一个指针是偶数,第二个指针是奇数,就交换;如果第一个是偶数,第二个也偶数,第二个指针向前移;反之,第一个指针向后移 + +``` +void reorder(int *data, int length); +``` + + + +## 奇偶分离 + +给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。 +要求:空间复杂度O(1),时间复杂度为O(n) +如 [4,5,2,7,5] => [5,7,5,4,2], +空间复杂度O(1),得使用 交换排序 + +插入排序思想 +快速排序思想 + + + +1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次. +每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间, +能否设计一个算法实现? + +分析:难点在于 `不用辅助空间`。 + +思路一: `sum(数组元素的总和)-sum(1~1000)` 得到的差即为重复元素,N较大时注意总和溢出 +思路二: 异或操作(位运算) + + + + + +## 重新排列使负数排在正数前面 + +一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序 + +比如: 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 +从右侧起 +起始点在第一个出现的负数, 翻转点在起始点后第一个出现的正数,终止点在翻转点后出现的第一个负数(或结束) +如果无翻转点, 则不操作 +如果有翻转点, 则待终止点出现后, 做翻转, 即ab => ba 这样的操作 +翻转后, 负数串一定在左侧, 然后从负数串的右侧开始记录起始点, 继续往下找下一个翻转点 + +例子中的就是 + +1, 7, -5, 9, -12, 15 +第一次翻转: 1, 7, -5, -12,9, 15 => 1, -12, -5, 7, 9, 15 +第二次翻转: -5, -12, 1, 7, 9, 15 + + N维翻转空间占用为O(1)复杂度是2N;在有一个负数的情况下, 复杂度最大是2N, ;在有i个负数的情况下, 复杂度最大是2N+2i, 但是不会超过2N+N实际的复杂度在O(3N)以内 + 但从最终时间复杂度分析,此方法是否真能达到O(N)的时间复杂度,还待后续考证。感谢John_Lv,MikovChain。2012.02.25。 + +1, 7, -5, -6, 9, -12, 15(后续:此种情况未能处理) +1 7 -5 -6 -12 9 15 +1 -12 -5 -6 7 9 15 +-6 -12 -5 1 7 9 15 + +更多请参考此文,程序员编程艺术第二十七章:重新排列数组(不改变相对顺序&时间O(N)&空间O(1),半年未被KO)http://blog.csdn.net/v_july_v/article/details/7329314。 + + diff --git a/9 Algorithms Job Interview/5.2 数列-nsum问题.md b/9 Algorithms Job Interview/5.2 数列-nsum问题.md new file mode 100644 index 0000000..784d7f4 --- /dev/null +++ b/9 Algorithms Job Interview/5.2 数列-nsum问题.md @@ -0,0 +1,185 @@ +#数列-nsum问题 + + + + +## 找出和为N+1的2个数 + +一个整数数列,元素取值可能是`1~N`(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。 +设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于`N+1`。 +复杂度最好是 `O(n)` ,如果是 `O(n2)`则不得分。 + + +分析:列出所有的数对,如输入15,输出`【1,14】【2,13】【3,12】。。。` + +``` +int print_sequence_sum(int n) +``` + + +## 找出和为m的2个数 + +输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。 +要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。 +例如输入数组【1、2、4、7、11、15=和数字15。由于4+11=15,因此输出4和11。 + + +``` +//返回0找到,返回-1没找到 +int findaddends(int *data,int length,int sum,int *a,int *b); +``` + +分析: +数组升序排列,查找可用二分查找,时间复杂度`O(logn)`,这样问题就变成了找其中一个加数的问题,复杂度为`N*logN` + +巧妙解法:数组2端向中间扫描,复杂度O(N) + + + + + +## 求子数组的最大和(最大字段和) + +输入一个整形数组,数组里有正数也有负数。 +数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。 +求所有子数组的和的最大值。要求时间复杂度为O(n)。 + +例如输入的数组为`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; + 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; + } +``` + + + + + +## 和为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)` 复杂度搞定 + + + + +## 求连续子数组和为m的组合 + +数组 [2,3,1,2,4,3]求连续子数组sum=7, 结果[1,2,4] [3,4] + +* 暴力遍历 +* 滑动窗口, 左指针,右指针 + +``` +if sum是n个不同的实数的序列,L的递增子序列是这样一个子序列 +Lin=< aK1,ak2,…,akm >,其中k1< k2<…< km且aK1< ak2<…< akm。 +求最大的m值。 + +如【5,6,7,3,2,8】 最长子序列 【5,6,7,8】 + +`动态规划` + + + + + + +## 在从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. 分析规律 + + + + diff --git a/9 Algorithms Job Interview/7 二叉树.md b/9 Algorithms Job Interview/7 二叉树.md index 065c72d..a7fba16 100644 --- a/9 Algorithms Job Interview/7 二叉树.md +++ b/9 Algorithms Job Interview/7 二叉树.md @@ -7,6 +7,18 @@ * 子树 +二元树的结点定义如下: +``` +struct SBinaryTreeNode // a node of the binary tree +{ + int m_nValue; // value of node + SBinaryTreeNode *m_pLeft; // left child of node + SBinaryTreeNode *m_pRight; // right child of node +}; +``` + + + ## 把二元查找树转变成排序的双向链表 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。要求`不能创建任何新的结点`,只调整指针的指向。 @@ -21,21 +33,13 @@ 转换成双向链表 `4=6=8=10=12=14=16` -首先我们定义的二元查找树 节点的数据结构如下: -``` - struct BSTreeNode -{ - int m_nValue; // value of node - BSTreeNode *m_pLeft; // left child of node - BSTreeNode *m_pRight; // right child of node -}; -``` 分析:题目要求不能创建任何节点,也就是只能调整各个节点的指向 思路一: + ## 在二元树中找出和为某一值的所有路径 题目:输入一个整数和一棵二元树。从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。打印出和与输入整数相等的所有路径。 @@ -50,37 +54,11 @@ ``` 则打印出两条路径:`10, 12` 和 `10, 5, 7` 。 -二元树节点的数据结构定义为: - -``` -struct BinaryTreeNode // a node in the binary tree -{ - int m_nValue; // value of node - BinaryTreeNode *m_pLeft; // left child of node - BinaryTreeNode *m_pRight; // right child of node -}; -``` 1. 累加当前节点,累加和大于给定值 2. 不为叶节点,左子树入栈 -## 判断整数序列是不是二元查找树的后序遍历结果 - -题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。 -如果是返回true,否则返回false。 -例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果: -``` - 8 - / \ - 6 10 - /\ / \ - 5 7 9 11 -``` -因此返回true。如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。 - - - ## 一棵排序二叉树,令 f=(最大值+最小值)/2,设计一个算法,找出距离f值最近、大于f值的结点。 复杂度如果是O(n2)则不得分。 @@ -108,72 +86,6 @@ struct BinaryTreeNode // a node in the binary tree /\ /\ 11 9 7 5 ``` -定义二元查找树的结点为: -``` -struct BSTreeNode // a node in the binary search tree (BST) -{ - int m_nValue; // value of node - BSTreeNode *m_pLeft; // left child of node - BSTreeNode *m_pRight; // right child of node -}; -``` - - -## 按层打印二叉树 - -输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印。 -例如输入 - -``` - 8 - /\ - 6 10 -/ \ /\ -5 7 9 11 -``` - -输出`8 6 10 5 7 9 11` - - - - -## 二元树的深度 - -题目:输入一棵二元树的根结点,求该树的深度.从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 -例如:输入二元树: -``` - - 10 - / \ - 6 14 - / / \ - 4 12 16 -``` - -输出该树的深度3。 - -二元树的结点定义如下: -``` -struct SBinaryTreeNode // a node of the binary tree -{ - int m_nValue; // value of node - SBinaryTreeNode *m_pLeft; // left child of node - SBinaryTreeNode *m_pRight; // right child of node -}; -``` - -分析:这道题本质上还是考查二元树的遍历。对于一颗完全二叉树,要求给所有节点加上一个pNext指针,指向同一层的相邻节点;如果当前节点已经是该层的最后一个节点,则将pNext指针指向NULL;给出程序实现,并分析时间复杂度和空间复杂度。 - - - -## 把一个有序整数数组放到二叉树中? - -分析:本题考察二叉搜索树的建树方法,简单的递归结构。 - -关于树的算法设计一定要联想到递归,因为树本身就是递归的定义。 -而,学会把递归改称非递归也是一种必要的技术。 -毕竟,递归会造成栈溢出,关于系统底层的程序中不到非不得以最好不要用。 -但是对某些数学问题,就一定要学会用递归去解决。 @@ -181,16 +93,6 @@ struct SBinaryTreeNode // a node of the binary tree 设计一个算法,找出二叉树上任意两个结点的最近共同父结点。复杂度如果是O(n2)则不得分 -二叉树的结点定义如下: - -``` -struct TreeNode -{ - int m_nvalue; - TreeNode* m_pLeft; - TreeNode* m_pRight; -}; -``` 输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点。 @@ -198,18 +100,6 @@ struct TreeNode - -## 俩种方法实现二叉树的前序遍历。 - -递归和非递归 - - -## 恢复树结构 - -有一棵树(树上结点为字符串或者整数),请写代码将树的结构和数据写到一个文件中,并能通过读取该文件恢复树结构。 - - - ## 求一个二叉树中任意两个节点间的最大距离 两个节点的距离的定义是 这两个节点间边的个数,比如某个孩子节点和父节点间的距离是1,和相邻兄弟节点间的距离是2,优化时间空间复杂度。 diff --git a/9 Algorithms Job Interview/7.1 二叉树-遍历.md b/9 Algorithms Job Interview/7.1 二叉树-遍历.md new file mode 100644 index 0000000..376ceab --- /dev/null +++ b/9 Algorithms Job Interview/7.1 二叉树-遍历.md @@ -0,0 +1,67 @@ +# 二叉树-遍历 + + + + +## 俩种方法实现二叉树的前序遍历。 + +递归和非递归 + + + + +## 按层打印二叉树 + +输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印。 +例如输入 + +``` + 8 + /\ + 6 10 +/ \ /\ +5 7 9 11 +``` + +输出`8 6 10 5 7 9 11` + + + + +## 二元树的深度 + +题目:输入一棵二元树的根结点,求该树的深度.从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 +例如:输入二元树: +``` + + 10 + / \ + 6 14 + / / \ + 4 12 16 +``` + +输出该树的深度3。 + + + +分析:这道题本质上还是考查二元树的遍历。对于一颗完全二叉树,要求给所有节点加上一个pNext指针,指向同一层的相邻节点;如果当前节点已经是该层的最后一个节点,则将pNext指针指向NULL;给出程序实现,并分析时间复杂度和空间复杂度。 + + + + +## 判断整数序列是不是二元查找树的后序遍历结果 + +题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。 +如果是返回true,否则返回false。 +例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果: +``` + 8 + / \ + 6 10 + /\ / \ + 5 7 9 11 +``` +因此返回true。如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。 + + diff --git a/9 Algorithms Job Interview/7.2 二叉树-建树.md b/9 Algorithms Job Interview/7.2 二叉树-建树.md new file mode 100644 index 0000000..d843691 --- /dev/null +++ b/9 Algorithms Job Interview/7.2 二叉树-建树.md @@ -0,0 +1,24 @@ +# 二叉树-建树 + + + + + +## 把一个有序整数数组放到二叉树中? + +分析:本题考察二叉搜索树的建树方法,简单的递归结构。 + +关于树的算法设计一定要联想到递归,因为树本身就是递归的定义。 +而,学会把递归改称非递归也是一种必要的技术。 +毕竟,递归会造成栈溢出,关于系统底层的程序中不到非不得以最好不要用。 +但是对某些数学问题,就一定要学会用递归去解决。 + + + + +## 恢复树结构 + +有一棵树(树上结点为字符串或者整数),请写代码将树的结构和数据写到一个文件中,并能通过读取该文件恢复树结构。 + + + diff --git a/9 Algorithms Job Interview/97 其他.md b/9 Algorithms Job Interview/97 其他.md index 28f2057..ff0d20f 100644 --- a/9 Algorithms Job Interview/97 其他.md +++ b/9 Algorithms Job Interview/97 其他.md @@ -38,3 +38,24 @@ + +## 圆形和正方形是否相交 + +用最简单, 最快速的方法计算出下面这个圆形是否和正方形相交 +3D坐标系 原点(0.0,0.0,0.0) +圆形: +半径r = 3.0 +圆心o = (*.*, 0.0, *.*) + +正方形: +4个角坐标; +1:(*.*, 0.0, *.*) +2:(*.*, 0.0, *.*) +3:(*.*, 0.0, *.*) +4:(*.*, 0.0, *.*) + +分析: + +2个形状不相交: + +