國外 IT 教育學院 Educative.io 創始人 Fahim ul Haq 寫過一篇過萬讚的文章《The top data structures you should know for your next coding interview》,總結了程式設計師面試中需要掌握的 8 種資料結構知識。
Fahim ul Haq 曾在 Facebook 和微軟任職,面試過不少程式設計師,所以這篇文章還是值得參考的。以下內容編譯自他的這篇《準備下次程式設計面試前你應該知道的資料結構》:
瑞典電腦科學家 Niklaus Wirth 在 1976 年寫了一本書,叫作《Algorithms + Data Structures = Programs》(演算法+資料結構=程式)。
即便在 40 年後的今天,這條等式仍然成立。這也是為何程式設計師求職者應該向面試官展示出已經透徹理解了資料結構知識。
幾乎所有的面試問題都要求求職者表現出已經熟練掌握資料結構,不管你是剛畢業的應屆生還是工作了多年的老手,都是這樣。
有時,面試問題會明確提到資料結構,比如“給定一個二叉樹”;有時則比較含蓄,比如“我們想追蹤和每位作者相關的書籍數量。”
學習資料結構知識很有必要,哪怕你只是想找份比現在的工作更好的一份差事。我們首先了解資料結構的基本知識。
什麼是資料結構?
簡單說,資料結構就是一個容器,以某種特定的佈局儲存資料。這個“佈局”使得資料結構在某些操作上非常高效,在另一些操作上則不那麼高效。你的目標就是理解資料結構,這樣就能為手頭的問題選擇最優的資料結構。
為什麼我們需要資料結構?
由於資料結構用來以有組織的形式儲存資料,而且資料是電腦科學中最重要的實體,因此資料結構的真正價值顯而易見。
無論你解決什麼問題,你都必須以這種或那種方式處理資料比如員工的工資,股票價格,購物清單,甚至簡單的電話簿等等。
根據不同的場景,資料需要以特定格式儲存。目前有一些資料結構可以滿足我們以不同格式儲存資料的需求。
常用的資料結構
我們首先列出最常用的資料結構,然後再挨個講解:
- 陣列
- 堆疊
- 佇列
- 連結串列
- 樹
- 圖
- 字典樹
- 雜湊表
陣列
陣列是一種最簡單和最廣泛使用的資料結構,其它資料結構比如堆疊和佇列都源自陣列。
下圖是一個大小為 4 的簡單陣列,包含幾個元素( 1 , 2 , 3,4)。
每個資料元素會被分配一個正的數值,叫作“索引”,它對應該元素在陣列中的位置。大部分程式語言都將初始索引定義為 0.
以下是兩種陣列:
- 一維陣列(如上所示)
- 多維陣列(陣列的陣列)
陣列的基本操作:
- Insert——在給定索引位置插入一個元素
- Get——返回給定索引位置的元素
- Delete——刪除給定索引位置的元素
- Size——獲取陣列內所有元素的總數
常問的陣列面試問題:
- 找到陣列中第二小的元素
- 找到陣列中第一個沒有重複的整數
- 合併兩個分類陣列
- 重新排列陣列中的正值和負值
堆疊
我們都熟悉很有名的撤銷(Undo)選項,它幾乎存在每個應用程式中。有沒有想過它是如何工作的?其思路就是,按照最後的狀態排列在先的順序將工作的先前狀態(限於特定數字)儲存在記憶體中。這隻用陣列是無法實現的,因此堆疊就有了用武之地。
可以把堆疊看作一堆垂直排列的書籍。為了獲得位於中間位置的書,你需要拿掉放在它上面的所有書籍。這就是 LIFO(後進先出)方法的工作原理。
這是一個包含三個資料元素(1,2 和 3)的堆疊影像,其中3位於頂部,首先把它刪除:
堆疊的基本操作:
- Push——在頂部插入元素
- Pop—— 從堆疊中刪除後返回頂部元素
- isEmpty——如果堆疊為空,則返回 true
- Top ——返回頂部元素,但不從堆疊中刪除
常見的堆疊面試問題:
- 使用堆疊計算字尾表示式
- 對堆疊中的值進行排序
- 檢查表示式中的括號是否平衡
佇列
與堆疊類似,佇列是另一種線性資料結構,以順序方式儲存元素。堆疊和佇列之間唯一的顯著區別是,佇列不是使用 LIFO 方法,而是應用 FIFO 方法,這是 First in First Out(先入先出)的縮寫。
佇列的完美現例項子:一列人在售票亭等候。如果有新人來,他們是從末尾加入佇列,而不是在開頭——站在前面的人將先買到票然後離開佇列。
下圖是一個包含四個資料元素(1,2,3 和 4)的佇列,其中 1 位於頂部,首先把它刪除:
佇列的基本操作:
- Enqueue() —— 向佇列末尾插入元素
- Dequeue() —— 從佇列頭部移除元素
- isEmpty() —— 如果佇列為空,則返回 true
- Top() —— 返回佇列的第一個元素
常問的佇列面試問題:
- 使用佇列來實現堆疊
- 顛倒佇列中前 k 個元素的順序
- 使用佇列生成從 1 到 n 的二進位制數
連結串列
連結串列是另一個重要的線性資料結構,剛一看可能看起來像陣列,但在記憶體分配,內部結構以及如何執行插入和刪除的基本操作方面有所不同。
連結串列就像一個節點鏈,其中每個節點包含資料和指向鏈中後續節點的指標等資訊。有一個頭指標,指向連結串列的第一個元素,如果列表是空的,那麼它只指向 null 或不指向任何內容。
連結串列用於實現檔案系統,雜湊表和鄰接表。下圖是連結串列內部結構的直觀展示:
下面是幾種型別的連結串列:
- 單連結串列(單向)
- 雙連結串列(雙向)
連結串列的基本操作:
- InsertAtEnd —— 在連結串列末尾插入指定元素
- InsertAtHead —— 在連結串列頭部插入指定元素
- Delete —— 從連結串列中刪除指定元素
- DeleteAtHead —— 刪除連結串列的第一個元素
- Search —— 返回連結串列中的指定元素
- isEmpty —— 如果連結串列為空,返回 true
常問的連結串列面試問題:
- 翻轉列表
- 檢測連結串列中的迴圈
- 返回連結串列中倒數第 n 個節點
- 移除連結串列中的重複值
圖
圖就是一組節點,以網路的形式互相連線。節點也被稱為頂點(vertices)。一對(x,y)就叫做一個邊,表示頂點 x 和頂點 y 相連。一個邊可能包含權重/成本,顯示從頂點 x 到 y 所需的成本。
圖的型別:
- 無向圖
- 有向圖
在程式語言中,圖可以表示為兩種形式:
- 鄰接矩陣
- 鄰接列表
常見的圖遍歷演算法:
- 廣度優先搜尋
- 深度優先搜尋
常問的圖面試問題:
- 實現廣度優先搜尋和深度優先搜尋
- 檢查一個圖是否為樹
- 計算一張圖中的邊的數量
- 找到兩個頂點之間的最短路徑
樹
樹是一種層級資料結構,包含了連線它們的頂點(節點)和邊。樹和圖很相似,但二者有個很大的不同點,即樹中沒有迴圈。
樹廣泛應用在人工智慧和複雜的演算法中,為解決各種問題提供高效的儲存機制。
下圖是一個簡單的樹,以及在樹型資料結構中所用的基本術語:
下面是幾種型別的樹:
- N 叉樹
- 平衡樹
- 二叉樹
- 二叉搜尋樹
- 平衡二叉樹
- 紅黑樹
- 2-3 樹
其中,二叉樹和二叉搜尋樹是最常用的樹。
常問的樹面試問題:
- 找到一個二叉樹的高度
- 找到一個二叉搜尋樹中第 k 個最大值
- 找到距離根部“k”個距離的節點
- 找到一個二叉樹中給定節點的祖先(ancestors)
字典樹
字典樹,也叫“字首樹”,是一種樹形結構,在解決字串相關問題中非常高效。其提供非常快速的檢索功能,常用於搜尋字典中的單詞,為搜尋引擎提供自動搜尋建議,甚至能用於IP路由選擇。下面展示了“top”“thus”和“their”這三個詞是如何儲存在字典樹中的:
這些單詞以從上到下的方式儲存,其中綠色節點“p”,“s”和“r”分別表示“top”,“thus”和“their”的末尾。
常見的字典樹面試問題:
- 計算字典樹中的總字數
- 列印儲存在字典樹中的所有單詞
- 使用字典樹對陣列的元素進行排序
- 使用字典樹從字典中形成單詞
- 構建一個T9字典
雜湊表雜湊是一個用於唯一標識物件並在一些預先計算的唯一索引(稱為“金鑰”)儲存每個物件的過程。因此,物件以“鍵值”對的形式儲存,這些項的集合被稱為“字典”。可以使用該鍵值搜尋每個物件。有多種不同的基於雜湊的資料結構,但最常用的資料結構是雜湊表。
雜湊表通常使用陣列實現。
雜湊資料結構的效能取決於以下三個因素:
- 雜湊函式
- 雜湊表的大小
- 碰撞處理方法
下圖展示瞭如何在陣列中對映雜湊。該陣列的索引是通過雜湊函式計算的。
常問的雜湊面試問題:
- 找到陣列中的對稱對
- 追蹤遍歷的完整路徑
- 檢視一個陣列是否為另一個陣列的子集
- 檢查給定陣列是否不相交
以上就是你在準備程式設計面試前需要掌握的8種資料結構。
在上面的 8 種資料結構中,每種結構都有對應的面試問題,接下來的一段時間我會將這三十一道問題依舊使用動畫的形式解析清楚。