掌握數據結構和算法,藍翔北大都不怕

$$ $$程序員

做者:祕塔科技HR 胖涵涵面試

絕大部分公司的技術面都會有一個流程是寫代碼。每到招聘季,一些大公司就會定個時間,統一作題,自動評分,而後直接帶走排名靠前的程序員,這種篩選候選人的方法既簡單又高效。算法

這些題目的考點包括棧、隊列、樹、圖、搜索、排序、動規、貪心……都是咱們在數據結構與算法的課上打過交道的。然而,當這些知識點隱藏在題目裏面的時候,你真的會作嗎?數組

今天胖涵涵帶你們看一套2018年北大的保研數算考試題,再次預習複習一下這些知識點。數據結構

歡迎關注祕塔科技公衆號。

A題:計算兩個日期之間的天數

樣例輸入:
2008 1 1
2009 1 1
樣例輸出:
366

這是一道大一上的常規練習題,俗稱簽到題。本題沒有算法上的考點,只是寫起來較爲瑣碎。
解題分三步走:code

  • 先看兩個日期之間相差的完整的年份,加365或366。
  • 再看相差的完整的月份,加28或29或30或31。
  • 再看剩下的不完整的月份,加上餘下的天數。

閏年的問題隨時都要考慮,題目已經提示咱們閏年的計算方法:排序

if 能被100整除:
    if 能被400整除:
        return 閏年
else:
    if 能被4整除:
        return 閏年

B題:迴文子串

給定一個字符串,尋找並輸出字符串中最長迴文子串。
迴文串即從左到右和從右到左讀都同樣的字符串。
若是字符串中包含多個迴文子串,則返回第一個。
樣例輸入:
 ab
 babadec
 scdedcd
 樣例輸出:
 a
 bab
 cdedc

假設字符串長度爲$n$,則共有$(n+1)n/2$個子串,再判斷每一個子串是不是迴文子串,複雜度爲$O(n^3)$。遞歸

這裏提供一個動態規劃的解法:
F[i,j]表示從$i$位置開始長度爲$j$的子串是否爲迴文串:若是是,F = 1;若是不是,F = 0
遞推式:
F[i,1] = 1
F[i,2] = (str[i] == str[i+1])
F[i,j] = (F[i+1,j-2] and str[i] == str[i+j])
最後在F[i,j]中找到符合題意的解。隊列

動規算法複雜度爲$O(n^2)$。本題寫起來較爲簡單。內存

C題:投骰子

(題目很長,順便考了英語。簡化版題目以下。)
給一個包含幾個骰子的圖像,肯定骰子上的點數,按升序輸出。
假設圖像僅包含三種不一樣的像素:背景骰子骰子上的點
定義:

  • 共邊的像素是相連的,即對角的像素不算相連
  • 僅由非背景像素組成的全部最大連通像素集是一個骰子,由骰子上的點像素組成的最大連通像素集是骰子上的一個
樣例輸入:
..............................
..............................
...............*..............
...*****......****............
...*X***.....**X***...........
...*****....***X**............
...***X*.....****.............
...*****.......*..............
..............................
........***........******.....
.......**X****.....*X**X*.....
......*******......******.....
.....****X**.......*X**X*.....
........***........******.....
..............................
(看出來了嗎?上圖共有4個骰子,左上是2點,右上是1點,左下是2點,右下是4點。)
樣例輸出:1 2 2 4

這是一道廣度優先搜索的題目,推薦用隊列實現。有些同窗在面試的時候會給出深度優先的方案,然而一旦數據量變大,遞歸那麼多層會內存溢出(俗稱爆棧)。
好了咱們已經知道了廣搜,然而本題實現起來仍是很麻煩。

  • 第一步,計算有幾個骰子。遍歷像素點,遇到第一個非背景像素,標記爲1號骰子,開始廣搜和它相連通的非背景像素,所有標記爲1。繼續遍歷像素點,遇到第二個未被標記的非背景像素,標記爲2號骰子,開始廣搜……
  • 第二步,計算有幾個骰子上的點。遍歷像素點,遇到第一個骰子上的點的像素,作好已訪問標記,開始廣搜。檢查和骰子上的點相連通的骰子號碼,對應號碼的骰子點數+1。繼續遍歷。
  • 排序,輸出。

D題:歐元效率

給定一系列硬幣的面額,計算交易特定金額所需的硬幣數量。咱們認爲 每次交易使用的硬幣數量越少越有效率
舉個例子,當硬幣面額爲1 2 5 10 20 50的時候,交易68元須要的硬幣數量爲3,即20+50-2=68。
每組給出6個面額,最小面額都是1,最大面額不超過100。
輸出交易金額1到100所需硬幣數量的平均值和最大值。
樣例輸入:
1 2 5 10 20 50
1 24 34 39 46 50
1 2 3 7 19 72
樣例輸出:
2.96 5
2.52 3
2.80 4
能夠看出,當面額組合爲1 24 34 39 46 50的時候,每次交易最多使用3枚硬幣,不考慮計算上的麻煩,買東西仍是很方便的。

本題是一道隱藏的揹包問題。直觀的看,把硬幣當作物品,面額當作物品的價值,每一個物品能夠拿0個,1個,甚至-1個。物品能夠取負數個,處理起來很是棘手,咱們須要把問題進行適當的轉化。

轉化過程:用$F(X)$表示總金額爲$X$所需的最少硬幣數量,$G(X)$表示交易金額爲$X$所需的最少硬幣數量。令$V = A - B$。其中$V$表示交易金額,$A$表示買家付的總金額,$B$表示賣家找回的總金額。則$G(V) = \min \{F(A_i) + F(B_i)\}$。

求$F$的過程是一個標準的揹包問題:
$F(0) =0.$
考慮面額爲$V_i$的硬幣,嘗試用$V_i$去替換小面額硬幣:$F(X) = \min \{F(X-V_i)+1, F(X)\}$。複雜度爲$6\cdot X$。

求出$F$後,再求$G$,總體複雜度爲$O(X)$。
下面討論$F(X)$的定義域,即買家最多付給賣家多少錢。簡單估算最多付100張,最大面額99,最大上限爲9900。這樣已經能夠順利AC這道題了,有興趣的同窗能夠嘗試繼續計算更小的上限。

E題:重要逆序對

給定N個數的序列$a_1,a_2,...,a_N$,定義一個數對$(a_i, a_j)$爲「重要逆序對」的充要條件爲 $i < j$ 且 $a_i > 2a_j$。求給定序列中「重要逆序對」的個數。
樣例輸入
10
0 9 8 7 6 5 4 3 2 1
樣例輸出
16

求逆序對最經典的方法的是藉助歸併排序。
在歸併兩個有序序列$a_1,a_2,...,a_m$,$b_1,b_2,...,b_n$以前,咱們觀察到,若$a_i$ > $b_j$,則$a_i,a_{i+1},...,a_m$均大於$b_j$ 且 與$b_j$造成逆序對的關係,在本次歸併過程當中,這$m+n$個數中與$b_j$造成逆序關係的一共有$m-i+1$個。歸併之後,這$m+n$個數組成的序列內再也不有逆序對。

本題求重要逆序對,把上述$a_i$ > $b_j$的判斷條件換成$a_i$ > $2b_j$便可。
歸併排序的複雜度爲$O(N\log N)$,歸併前計算逆序對的複雜度爲是線性的,則本題的複雜度也爲$O(N\log N)$。

F題:電車

每一個 路口有一些出口通往其餘的路口,出口處有一個方向轉換器。若是方向轉換器指向的方向不是電車想去的方向,則須要司機手動轉動方向轉換器,反之則不須要。
給出一張地圖、起始路口A和目標路口B,計算從A到B最少要轉動方向轉換器的次數。

本題是一道最短路徑的題目。
路口就是圖的結點,若是路口A通往路口B,則添加一條由A結點到B結點的有向邊。若是方向轉換器初始指向B,則這條邊的權重爲0,不然權重爲1。
有向圖建好了之後直接使用Dijkstra算法就能夠啦。

G題:食物鏈

已知有三類動物A,B,C,這三類動物的食物鏈構成了環形。A吃B, B吃C,C吃A。
有兩種說法對動物所構成的食物鏈關係進行描述:
第一種說法是"1 X Y",表示X和Y是同類。
第二種說法是"2 X Y",表示X吃Y。
某人對N個動物,用上述兩種說法,一句接一句地說出K句話,這K句話有的是真的,有的是假的。當一句話知足下列三條之一時,這句話就是假話,不然就是真話。
1)當前的話與前面的某些真的話衝突,就是假話;
2)當前的話中X或Y比N大,就是假話;
3)當前的話表示X吃X,就是假話。
根據給定的N和K句話,輸出假話的總數。

本題是並查集的經典練習題。建模方面有一點難度。
在同一集合中的結點表示相互之間能夠推導出食物鏈關係。咱們給邊Father[a] = b作標記$R(a, b)$:

  • 標記爲0表示兩者同類
  • 標記爲1表示b被a吃
  • 標記爲2表示a被b吃

咱們發現這樣的標記有一個好的性質:當Father[a] = b,Father[b] = c的時候,$R(a,c)=(R(a,b)+R(b,c)) \mod 3$。
有了這個性質做爲基礎,若X和Y在同一個集合裏面,以根節點爲中介,咱們就能夠推導出X和Y的關係,進而推斷話的真假。

H題:DFS樹

給定一個無向圖,以及從結點1開始進行DFS造成的樹。定義"T-Simple Circle"爲圖中的環,其中只含一條不屬於樹的邊。
求全部T-simple Circle的最小邊覆蓋中邊的數量。

本題屬於競賽題的難度了,小編調研了一下解決方案。

首先須要證實,每條不屬於樹的邊都能構成一個環,並且其中一個結點必定爲另外一個結點的祖先。
咱們在紙上畫一畫,假設存在一條不屬於樹的邊,兩個結點爲兄弟關係。那麼在DFS的過程當中必定會選中這條邊,原假設不成立。

而後原問題轉化爲:求樹種若干路徑的最小邊覆蓋。此時用貪心法,儘可能取這些路徑中深度最小的邊,就能保證每次取到的邊覆蓋的路徑最多。

相關文章
相關標籤/搜索