面試總結之-查找算法分析

先說明一下~這裏的查找主要指二分,不指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問你動態怎麼辦,那你就聊聊動態的,儘量避免寫代碼了(雖然線段樹比紅黑樹好寫。。。。。)

 

發佈了12 篇原創文章 · 獲贊 6 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章