您的位置:首页 > 职场人生

面试总结之-查找算法分析

2013-05-17 10:51 337 查看
先说明一下~这里的查找主要指二分,不指DFS,BFS(那些我后面放到搜索部分)。
查找的分析
关于查找,最最最重要的是二分查找,也是这系列所有博客中最重要的部分,除了二分查找之外,还有平衡树,但是不会让你写平衡树= =!一般就是对于STL中set,map的使用,STL的东西一般也不会一定要你用,但是如果面试时什么都自己现场写的话肯定来不及,也容易出错,记住几个常用的数据结构的接口还是比较重要的,不懂的可以上去这里看看:http://www.cplusplus.com/reference/stl/,主要的是set,
map, deque, list, vector, stack。
先是最简单的二分查找,二话不说先上代码。
Code[0]:

int BinarySearch(int* a,int n,inttar){ //n是最后一个下标,tar是要找的数
int i = 0, j = n;
while(i<=j){
int mid = i + (j-i)/2;
if(a[mid] == tar) return mid;
if(a[mid] > tar) j = mid-1;
else i = mid+1;
}
return -1;
}


这是一个一般的BinarySearch:找到target就返回下标,找不到就返回-1,代码简单易懂,注意while循环条件有个等于号(不然你会漏了元素没有判断);但是考这个的可能性并不是很高,因为太简单了,一般会是一些变种,比如:找出大于等于target的最小值,小于等于target的最大值,大于target的最小值,小于target的最大值 = =!(绕晕了),不要急,后面一个个分析,据我所知,面试时要写前面几种二分,还很多人会写错边界条件的。
一个例子就是:给定一个有序数组,问数字大小在[a,b]区间的有多少个。这个做法就是找出大于等于a的最小值的下标i,找出小于等于b的最大值下表j,返回j-i+1。如果问数字大小在区间(a,b),那上面找的值就没有“等于”了。
这四种二分看起来很麻烦,其实就是在一个框架下进行一点点改变就ok了。首先先给一个二分的框架:找大于等于target的最小下标。

Code[1]:

int BinarySearch(int* a,int n,inttar){
int i = 0, j = n;
while(i<=j){
int mid = i + (j-i)/2;
if(tar<=a[mid]) //等于往左走
j = mid-1;
else i = mid+1;
}
return i;  //返回i(i比j大)
}


跟上面标准的二分来比,这里不在循环里面return了,放到了循环外面return一个i。注意这个框架,有两个地方可以进行修改,分别对应上面讲到的四种二分要求:
1. if(tar<=a[mid]) VS if(tar<a[mid])
2. return i VS return j
从1,2中各选一个可以对应上面的某个二分要求,比如上面代码,用了if(tar<=a[mid])和return i,就对应“找大于等于target的最小下标”。当然这个东西是不用死记的,想想某个数组,包含有多个target,比如0 1 2 3 3 3 4 5 6,你要找大于等于3的最小下标,那当a[mid]等于3时,你自然应该搜索左半部分(如果你搜索右半部分,得到的3的下标将不是最小下标),最后结束时,i,j分别应该指向 2和3,i>j,所以应该要返回i。如果不能取等号,就应该返回j。总结一下就是:

二分查找总结-jiacongxu
等于target往右搜 ( if(tar<=a[mid]) )
等于target往左搜 ( if(tar<a[mid]) )
返回小的坐标( return j )
小于等于tar的最大值
小于tar的最大值
返回大的坐标( return i )
大于target的最小值
大于等于tar的最小值
这个表,记下来是没用的,在脑子里面跑一下二分的过程自然就明白什么情况应该用哪种二分,想象数组中有多个等于target的值。

最后这个程序在找不到符合要求的数字时返回的下标是溢出的,如果返回了小的坐标(j),溢出情况是返回值为-1,如果是返回大的坐标(i),溢出情况的返回值是n。
如果觉得理解了,可以上去做做这个题,直接就是上面提到的二分的两种,点进去,不要回头查代码,一次AC吧:
Search for a Range
这题也基本一样,你可以认为简单点(其实就是一样的):
Search Insert Position

接下来就是平衡树了。平衡树(具体说是红黑树)在STL里面的应用就是set和map(还有multiset和multimap),这东西做的事情跟二分查找差不多:用O(logn)的时间查找某个数或者查找不超过阈值的某个最大(最小)值,这些操作都跟上面的几种二分查找一一对应。但是平衡树的优势在于它是可以动态插入删除数据的(这个你用数组就不行了),缺点在于面试时你是没办法自己写一个平衡树的(要是面试官让你写个红黑树,你就把他吊起来打吧)。一个例子是:从数组中找和最接近给定值target的连续子数组(数组有负)。可以这样做:求出sum数组,对于sum[i],从[i+1,n-1]中查找一个最接近sum[i]+target的值,查找方法是,sum存到一个multiset里面(平衡树),每次i+1之后,把sum[i+1]从multiset中清除。时空复杂度O(nlogn)+O(n)。
关于红黑树(或者其他平衡树)的介绍可以看看这个文章:教你透彻了解红黑树
你只需要大概知道红黑树怎么个旋转法就好了,除非你自己感兴趣,不然不需要知道具体怎么代码实现,在面试时纸上写个红黑树太灭绝人性了。平衡树没什么好说的,主要因为面试不用写代码,只需要知道STL怎么用就好了。而怎么用STL就不说了,easy job,自己进去文章开头的那个连接看看自然就懂。上面给的两个题,当然你也可以用STL的set来坐坐看(杀鸡焉用牛刀~~),另外再给leetcode上面几道关于二分的题(不一定只能二分,但二分是一个可以接受的解):
Divide Two Integers 二分答案~
Median of Two Sorted Arrays 这个题个人觉得很不好做,大家要是有什么特别浅显又不容易错的方法,请贡献个代码吧
Search in Rotated Sorted Array 这个貌似是面试高频题,不难搞,思考清楚边界条件,一次AC吧
Sqrt(x) 这个题本身不难(尤其告诉你怎么做之后),不过它有一个把输入输出换成double的版本,思路一样,但是有一个很陷阱貌似很多人前赴后继的跳进去了,而且这个陷阱是不归路~~~
Search in Rotated Sorted Array II 这题放上来坑爹的,做做就知道怎么坑

其实讲起平衡树,总是跟哈希有千丝万缕关系,不过由于哈希太重要了,后面另起一篇讲了。他们之间的关系,看看STL里面,那些平衡树版本的数据结构时不时就蹦出一个哈希版本的(map 跟 hash_map)。最后加一句就是,红黑树跟有序数组这种差别(前面提到的动态和静态),在很多数据结构里面都体现了,比如预处理成数组的RMQ和线段树维护的RMQ,一般来说,面试时如果只需要静态的,你就写静态的,因为动态的不好写,然后也许面试官有个follow
up问你动态怎么办,那你就聊聊动态的,尽量避免写代码了(虽然线段树比红黑树好写。。。。。)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: