校招準備系列9-筆試面試演算法題
筆試演算法題
可以先對一些簡單情形進行手工模擬,查詢規律 有時先對陣列進行排序可以使運算變得簡單,提高效率 字串問題、括號匹配問題,可以考慮逆向思維,從右往左看 從初態到某一狀態A最少需要幾步?可以考慮從狀態A回到初態的逆過程需要幾步 括號匹配,標準匹配正負之和count為0,允許一次交換則count下限調整為-1. 排列組合,組合數,排列數 數字的奇偶性 數的質因數分解 連結串列判環:快慢指標。連結串列相交:首尾交錯相連,p、q走到相遇為止。 裝箱問題,將貨物按重量從大到小排序,每次嘗試放最大的,放不下就開一個新的箱子。
動態規劃
動態規劃需要找到遞推式,即狀態從i轉移到j的公式(狀態也可能是二維的(i1,j1)->(i2,j2)) 動態規劃時,dp陣列中儲存的不一定是題目的要求,可以是“以每個位置結尾這種思維方式” 動態規劃:換錢的最少貨幣數(p191),換錢的方法數(程式設計師程式碼面試指南p196,擴充套件:每種貨幣最多拿一次的情況),0/1揹包,完全揹包,裝箱問題
找錢的可能數(組合):每種錢幣0到無窮個,錢幣升序排列,for j=1,n:{dp[i] = dp[i] + dp[i-k]};0~1個,錢幣降序排列,for j=n,1{dp[i] = dp[i] + dp[i-k]} 走樓梯的方法數(排列):迭代法
面試演算法題
findkth,第k小的元素
- 先對陣列整體排序,再取第k個元
- 素。時間複雜度O(nlogn)素。時間複雜度O(nlogn)
- k次氣泡排序or選擇排序。時間複雜度O(kn)
#include <iostream> using namespace std; // arr[]為陣列,start、end分別為陣列第一個元素和最後一個元素的索引 // povitIndex為陣列中任意選中的數的索引 int partition(int arr[], int start, int end) { int pivot = arr[end]; int storeIndex = start; //這個迴圈比一般的寫法簡潔高效,呵呵維基百科上看到的 for(int i = start; i < end; ++i) { if(arr[i] < pivot) { swap(arr[i], arr[storeIndex]); ++storeIndex; } } swap(arr[storeIndex], arr[end]); return storeIndex; } int findkth(int arr[],int start, int end,int k){ int pivot_index = partition(arr,start,end); if(pivot_index < k){ return findkth(arr,pivot_index+1,end,k); } else if(pivot_index > k){ return findkth(arr,start,pivot_index-1,k); } else{ return arr[pivot_index]; } } double findmid(int arr[],int len){ if(len%2==1){ return findkth(arr,0,len-1,(len-1)/2); } else{ cout<<"even"<<endl; return (findkth(arr,0,len-1,len/2) + findkth(arr,0,len-1,len/2-1))/2.0; } }
找中位數的方法(要求時間複雜度O(1),空間複雜度O(n))
用findkth實現
找主元素(出現次數超過一半的數)
打擂臺。1.如果擂臺上沒人,新人成為擂主。2.同一組人上臺,擂主戰鬥力+1。3.不同組的人上臺挑戰,擂主戰鬥力-1。4.最後留在臺上的是中位數。
partial_sort()
partition一次的時間複雜度,為O(n)。總時間複雜度O(nlogn)
兩個有序陣列,a[],b[],大小分別為m,n,求第k 大的數
top-K問題
- 全部排序,O(nlogn)
- 資料規模小,可以完全放到記憶體中,則可以用partition(但不是partial_sort),平均時間複雜度O(n),最差時間複雜度O(n2)
- 對輸入內容進行部分排序,即只對前K大的元素進行排序(這K個元素即為所求)。此時我們可以選擇氣泡排序或選擇排序進行處理,即每次冒泡(選擇)都能找到所求的一個元素。這類策略的時間複雜度是O(Kn)。
- 建立size為k的最小堆,每次遇到新元素則和堆頂元素(前k個元素中最小的那個)比較,如果比他大,則替換他,新元素下沉到合適的位置(O(logk))。總時間複雜度O(k)+O(nlogk)=O(nlogk).
- 可以先對資料進行hash分段,每一段生成一個最小堆,再對這些最小堆用優先順序佇列(多路歸併)進行處理。
棧與佇列
兩個棧實現佇列。A棧用於入佇列,B棧用於出佇列,若B為空,則將A中的所有元素倒入B,再出佇列。
兩個佇列實現棧。如果“棧”非空,則必有一佇列非空。如果“棧”空,則
- 入棧:非空佇列入佇列操作
- 出棧:非空隊列出佇列n-1個,匯入另一佇列中。將最後一個元素出佇列。(完成空/非空佇列轉換)
最大值棧:用一個max棧來同步儲存可能的最大值在棧頂位置。入棧:max(當前最大值(即棧頂元素),新元素)。出棧:照常。 最大值佇列(滑動視窗最大值):用一個max佇列來同步儲存可能的最大值在佇列頭位置。入佇列:將max佇列尾部比新元素小的元素依次出佇列,然後新元素入佇列。
-
出佇列:如果出佇列的索引值和max佇列索引值相同,則出佇列。
-
2-5-3-4
-
-5--4
(劍指offer都有的,可以去看看)
大資料併發實時排序並求玩家的排名(有玩家ID,求該玩家的排名)
線段樹or樹形陣列(樹形分割槽),累加子分割槽的排名
1:戰力排行榜,幾乎實時得找出前100的玩家。(最大堆 和 計數排序),不知道有沒有修改和查詢都是O(1)得演算法?樹形陣列。 a. 一直維持一個size為100的最小堆,外加一個hashmap(儲存玩家ID到節點的對映)。 儲存堆頂(第100名的戰力)的值,其餘玩家在更新積分時,如果超過了該值,插入堆(替換堆頂元素,下沉到合適位置) b. 戰力分佈集中在某個區間,例如0~200,那麼可以用計數排序的思想。使用陣列來儲存各個戰力的玩家,陣列的每個元素是一個連結串列。外加一個hashmap(儲存玩家ID到節點的對映) c. 戰力分佈很分散。將b中的陣列換成連結串列,連結串列的每個節點是一個連結串列。外加一個hashmap(儲存玩家ID到節點的對映)。 每次更新戰力,只需要沿著連結串列前後找相鄰戰力連結串列,放置到戰力連結串列中合適的位置或者新開一個連結串列。