字節跳動Java面試題精選——算法與數據結構「跳槽面試必備」 前言 正文——算法與數據結構 1. 如何判斷鏈表是否有環 2. 紅黑樹 3. 數據庫索引的實現 4. 一致性Hash 寫在最後

前言

爲了能夠在面試回答中優雅而不失體面回答面試考點,該文章借鑑了不同平臺對知識點的描述

  • 如有侵權請聯繫我
  • 文章的不足和錯誤請指正,好的建議也不要吝嗇,我都會採納並更正

字節跳動Java面試題精選——算法與數據結構【含答案】,點贊,評論,收藏一氣呵成!

金九銀十雖已過去大半,但還有金三銀四呀,最新整理一些算法與數據結構以及答案總結,希望對想進BAT的同學有幫助。

本文所涉及的面試題皆已整理成一份完整的PDF文檔,需要的朋友可點這裏直達,免費領取

更多面試題截圖見文末

正文——算法與數據結構

1. 如何判斷鏈表是否有環

方法1:快慢指針法 2.設兩個工作指針p、q,p總是向前走,但q每次都從頭開始走,對於每個節點,看p走的步數是否和q一樣。比如p從A走到D,用了4步,而q則用了14步。因而步數不等,出現矛盾,存在環。

2. 紅黑樹

二叉搜索樹:(Binary Search Tree又名:二叉查找樹,二叉排序樹)它或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;它的左、右子樹也分別爲二叉搜索樹。

紅黑樹是一棵二叉搜索樹,它在每個結點上增加一個存儲位來表示結點的顏色,可以是RED或BLACK。通過對任何一條從根到葉子的簡單路徑上各個結點的顏色進行約束,紅黑樹沒有一條路徑會比其他路徑長出2倍,所以紅黑樹是近似平衡的,使得紅黑樹的查找、插入、刪除等操作的時間複雜度最壞爲O(log n),但需要注意到在紅黑樹上執行插入或刪除後將不在滿足紅黑樹性質,恢復紅黑樹的屬性需要少量(O(log n))的顏色變更(實際是非常快速的)和不超過三次樹旋轉(對於插入操作是兩次)。雖然插入和刪除很複雜,但操 作時間仍可以保持爲 O(log n) 次。具體如何保證?引出紅黑樹的5個性質。

紅黑樹的5個性質:滿足以下五個性質的二叉搜索樹

  • 每個結點或是紅色的或是黑色的
  • 根結點是黑色的
  • 每個葉結點是黑色的
  • 如果一個結點是紅色的,則它的兩個子結點是黑色的
  • 對於每個結點,從該結點到其後代葉結點的簡單路徑上,均包含相同數目的黑色結點

插入操作

由於性質的約束,插入的結點都是紅色的。插入時性質1、3始終保持。破壞性質2當且僅當當前插入結點爲根節點。變一下顏色即可。如果是破壞性質4或5,則需要旋轉和變色來繼續滿足紅黑樹的性質。下面說一說插入的幾種情況,約定當前插入結點爲N,其父結點爲P,叔叔爲U,祖父爲G

情形1:樹空,直接插入違反性質1,將紅色改黑。

情形2:N的父結點爲黑,不必修改,直接插入

從情形3開始的情形假定N結點的父結點P爲紅色,所以存在G,並且G爲黑色。且N存在一個叔叔結點U,儘管U可能爲葉結點。

情形3:P爲紅,U爲紅(G結點一定存在且爲黑)這裏不論P是G的左孩子還是右孩子;不論N是P的左孩子還是右孩子。

首先把P、U改黑,G改紅,並以G作爲一個新插入的紅結點重新進行各種情況的檢查,若一路檢索至根節點還未結束,則將根結點變黑。

情形4:P爲紅,U爲黑或不存在(G結點一定存在且爲黑),且P爲G的左孩子,N爲P的左孩子(或者P爲G的右孩子,N爲P的右孩子,保證同向的)。 P、G右旋並將P、G變相反色。因爲P取代之前黑G的位置,所以P變黑可以理解,而G變紅是爲了不違反性質5。

情形5:P爲紅,U爲黑或不存在,且P爲G的左孩子,N爲P的右孩子(或P爲G的右孩子,N爲P的左孩子,保證是反向的),對N,P進行一次左旋轉換爲情形4

刪除操作比插入複雜一些,但最多不超過三次旋轉可以讓紅黑樹恢復平衡。

其他

  • 黑高從某個結點x出發(不含x)到達一個葉結點的任意一條簡單路徑上的黑色結點個數稱爲該結點的黑高。紅黑樹的黑高爲其根結點的黑高。
  • 一個具有n個內部結點的紅黑樹的高度h<=2lg(n+1)
  • 結點的屬性(五元組):color key left right p
  • 動態集合操作最壞時間複雜度爲O(lgn)

3. 數據庫索引的實現

數據庫系統還維護着滿足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找算法。這種數據結構,就是索引。

數據庫系統還維護着滿足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找算法。這種數據結構,就是索引。

B樹

爲了描述B-Tree,首先定義一條數據記錄爲一個二元組[key, data],key爲記錄的鍵值,對於不同數據記錄,key是互不相同的;data爲數據記錄。那麼B-Tree是滿足下列條件的數據結構:

  • d爲大於1的一個正整數,稱爲B-Tree的度。用來表示每個結點包含的關鍵字個數的上界和下界。可以證明h<=logd((N+1)/2)
  • h爲一個正整數,稱爲B-Tree的高度。
  • 每個非葉子節點由n-1個key和n個指針組成,其中d<=n<=2d。
  • 每個葉子節點最少包含一個key和兩個指針,最多包含2d-1個key和2d個指針,葉節點的指針均爲null 。
  • 所有葉節點具有相同的深度,等於樹高h。
  • key和指針互相間隔,節點兩端是指針。
  • 一個節點中的key從左到右非遞減排列。
  • 所有節點組成樹結構。
  • 每個指針要麼爲null,要麼指向另外一個節點。
  • 如果某個指針在節點node最左邊且不爲null,則其指向節點的所有key小於v(key1),其中v(key1)爲node的第一個key的值。
  • 如果某個指針在節點node最右邊且不爲null,則其指向節點的所有key大於v(keym),其中v(keym)爲node的最後一個key的值。
  • 如果某個指針在節點node的左右相鄰key分別是keyi和keyi+1且不爲null,則其指向節點的所有key小於v(keyi+1)且大於v(keyi)。

由於B-Tree的特性,在B-Tree中按key檢索數據的算法非常直觀:首先從根節點進行二分查找,如果找到則返回對應節點的data,否則對相應區間的指針指向的節點遞歸進行查找,直到找到節點或找到null指針,前者查找成功,後者查找失敗。

一個度爲d的B-Tree,設其索引N個key,則其樹高h的上限爲logd((N+1)/2),檢索一個key,其查找節點個數的漸進複雜度爲O(logdN)。從這點可以看出,B-Tree是一個非常有效率的索引數據結構。

B+樹

B-Tree有許多變種,其中最常見的是B+Tree,例如MySQL就普遍使用B+Tree實現其索引結構。

B+樹是B樹的變形,它把所有的data都放在葉子結點中,只將關鍵字和子女指針保存於內結點,內結點完全是索引的功能。

與B-Tree相比,B+Tree有以下不同點:

  • 每個節點的指針上限爲2d而不是2d+1。
  • 內節點不存儲data,只存儲key;葉子節點存儲data不存儲指針。

一般在數據庫系統或文件系統中使用的B+Tree結構都在經典B+Tree的基礎上進行了優化,增加了順序訪問指針。

在B+Tree的每個葉子節點增加一個指向相鄰葉子節點的指針

例如圖4中如果要查詢key爲從18到49的所有數據記錄,當找到18後,只需順着節點和指針順序遍歷就可以一次性訪問到所有數據節點,極大提到了區間查詢效率。

爲什麼B樹(B+樹)

一般來說,索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程中就要產生磁盤I/O消耗,相對於內存存取,I/O存取的消耗要高几個數量級,所以評價一個數據結構作爲索引的優劣最重要的指標就是在查找過程中磁盤I/O操作次數的漸進複雜度。換句話說,索引的結構組織要儘量減少查找過程中磁盤I/O的存取次數。

這涉及到磁盤存取原理、局部性原理和磁盤預讀。

先從B-Tree分析,根據B-Tree的定義,可知檢索一次最多需要訪問h個節點。數據庫系統的設計者巧妙利用了磁盤預讀原理,將一個節點的大小設爲等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。爲了達到這個目的,在實際實現B-Tree還需要使用如下技巧:

  • 每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁裏,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O
  • B-Tree中一次檢索最多需要h-1次I/O(根節點常駐內存),漸進複雜度爲O(h)=O(logdN)。一般實際應用中,出度d是非常大的數字,通常超過100,因此h非常小(通常不超過3)

綜上所述,用B-Tree作爲索引結構效率是非常高的。

而紅黑樹這種結構,h明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用局部性,所以紅黑樹的I/O漸進複雜度也爲O(h),效率明顯比B-Tree差很多。

至於B+Tree爲什麼更適合外存索引,原因和內節點出度d有關。

由於B+Tree內節點去掉了data域,因此可以擁有更大的出度,擁有更好的性能。

4. 一致性Hash

第一:簡單介紹 一致性哈希算法是分佈式系統中常用的算法。比如,一個分佈式的存儲系統,要將對象存儲到具體的節點上,如果採用普通的hash方法,將數據映射到具體的節點上,如key%N,N是機器節點數

  • 考慮到比如一個服務器down掉,服務器結點N變爲N-1,映射公式必須變爲key%(N-1)
  • 訪問量加重,需要添加服務器結點,N變爲N+1,映射公式變爲hash(object)%(N+1)

當出現1,2的情況意味着我們的映射都將無效,對服務器來說將是一場災難,尤其是對緩存服務器來說,因爲緩存服務器映射的失效,洪水般的訪問都將衝向後臺服務器。

第二點:hash算法的單調性

Hash 算法的一個衡量指標是單調性,單調性是指如果已經有一些內容通過哈希分派到了相應的緩衝中,又有新的緩衝加入到系統中。哈希的結果應能夠保證原有已分配的內容可以被映射到新的緩衝中去,而不會被映射到舊的緩衝集合中的其他緩衝區。

consistent hash 也是一種hash 算法,簡單的說,在移除 / 添加一個結點時,它能夠儘可能小的改變已存在的映射關係,儘可能的滿足單調性的要求。

第三點:將對象和服務器結點分別映射到環型空間

通常的一致性哈希做法是將 value 映射到一個 32 位的 key 值,也即是 0~2^32-1 次方的數值空間;我們可以將這個空間想象成一個首( 0 )尾( 2^32-1 )相接的圓環。

我們可以通過hash函數將我們的key映射到環型空間中,同時根據相同的哈希算法把服務器也映射到環型空間中,順便提一下服務器或者某個計算節點的 hash 計算,一般的方法可以使用機器的 IP 地址或者機器名作爲 hash 輸入。

第四點:將對象映射到服務器

在這個環形空間中,如果沿着順時針方向從對象的 key 值出發,直到遇見一個 服務器結點,那麼就將該對象存儲在這個服務器結點上,因爲對象和服務器的hash 值是固定的,因此這個 cache 必然是唯一和確定的。

這時候考察某個服務器down機或者需要添加服務器結點,也就是移除和添加的操作,我們只需要幾個對象的映射。

第五點:虛擬結點

Hash 算法的另一個指標是平衡性 (Balance)。平衡性是指哈希的結果能夠儘可能分佈到所有的緩衝中去,這樣可以使得所有的緩衝空間都得到利用。

對於上述的做法,可能導致某些對象都映射到某個服務器,使得分佈不平衡。爲此可以採用“虛擬結點”的做法。

“虛擬結點”( virtual node )是實際節點在 hash 空間的複製品,一實際結點對應了若干個“虛擬節點”,這個對應個數也成爲“複製個數”,“虛擬節點”在 hash 空間中以 hash 值排列。引入“虛擬結點”會讓我們的映射分佈更爲平衡一些。

  • 引入“虛擬結點”前: Hash(“192.168.1.1”);
  • 引入“虛擬結點”後: Hash(“192.168.1.1#1”); Hash(“192.168.1.1#2”);

寫在最後

本文所涉及的面試題皆已整理成一份完整的PDF文檔,需要的朋友可點這裏直達,免費領取

部分面試題截圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章