測一測你的演算法階段學習成果

2021-09-16 18:57:36 字數 4307 閱讀 9712

《資料結構與演算法之美》專欄最重要的基礎篇馬上就要講完了,我從前面的文章中挑選了幾個例子,稍加修改,組成了一套測試題。你先不要著急看答案,自己先想一想怎麼解決,測一測自己對之前的知識掌握的程度。

如果有**卡殼或者不怎麼清楚的,可以回過頭再複習一下。正所謂溫故知新,這種通過實際問題查缺補漏的學習方法,非常利於你鞏固前面講的知識點,你可要好好珍惜這次機會哦!

相關章節

17 | 跳表:為什麼redis一定要用跳表來實現有序集合?

題目解析

這個問題既要通過id來查詢,又要通過積分來查詢,所以,對於獵頭這樣乙個物件,我們需要將其組織成兩種資料結構,才能支援這兩類操作。

我們按照id,將獵頭資訊組織成雜湊表。這樣,就可以根據id資訊快速的查詢、刪除、更新獵頭的資訊。我們按照積分,將獵頭資訊組織成跳表這種資料結構,按照積分來查詢獵頭資訊,就非常高效,時間複雜度是o(logn)。

我剛剛講的是針對第乙個、第二個操作的解決方案。第三個、第四個操作是類似的,按照排名來查詢,這兩個操作該如何實現呢?

我們可以對剛剛的跳表進行改造,每個索引結點中加入乙個span欄位,記錄這個索引結點到下乙個索引結點的包含的鍊錶結點的個數。這樣就可以利用跳表索引,快速計算出排名在某一位的獵頭或者排名在某個區間的獵頭列表。

實際上,這些就是redis中有序集合這種資料型別的實現原理。在開發中,我們並不需要從零開始**實現乙個雜湊表和跳表,我們可以直接利用redis的有序集合來完成。

電商交易系統中,訂單資料一般都會很大,我們一般都分庫分表來儲存。假設我們分了10個庫並儲存在不同的機器上,在不引入複雜的分庫分表中介軟體的情況下,我們希望開發乙個小的功能,能夠快速地查詢金額最大的前k個訂單(k是輸入引數,可能是1、10、1000、10000,假設最大不會超過10萬)。如果你是這個功能的設計開發負責人,你會如何設計乙個比較詳細的、可以落地執行的設計方案呢?

為了方便你設計,我先交代一些必要的背景,在設計過程中,如果有其他需要明確的背景,你可以自行假設。

相關章節

12 | 排序(下):如何用快排思想在o(n)內查詢第k大元素?

題目解析

解決這個題目的基本思路我想你應該能想到,就是借助歸併排序中的合併函式,這個我們在排序(下)以及堆的應用那一節中講過。

我們從每個資料庫中,通過select order by limit語句,各取區域性金額最大的訂單,把取出來的10個訂單放到優先順序佇列中,取出最大值(也就是大頂堆堆頂資料),就是全域性金額最大的訂單。然後再從這個全域性金額最大訂單對應的資料庫中,取出下一條訂單(按照訂單金額從大到小排列的),然後放到優先順序佇列中。一直重複上面的過程,直到找到金額前k(k是使用者輸入的)大訂單。

從演算法的角度看起來,這個方案非常完美,但是,從實戰的角度來說,這個方案並不高效,甚至很低效。因為我們忽略了,資料庫讀取資料的效能才是這個問題的效能瓶頸。所以,我們要儘量減少sql請求,每次多取一些資料出來,那一次性取出多少才合適呢?這就比較靈活、比較有技巧了。一次性取太多,會導致資料量太大,sql執行很慢,還有可能觸發超時,而且,我們題目中也說了,記憶體有限,太多的資料載入到記憶體中,還有可能導致oom。

所以,一次性不能取太多資料,也不能取太少資料,到底是多少,還要根據實際的硬體環境做benchmark測試去找最合適的。

我們知道,cpu資源是有限的,任務的處理速度與執行緒個數並不是線性正相關。相反,過多的執行緒反而會導致cpu頻繁切換,處理效能下降。所以,執行緒池的大小一般都是綜合考慮要處理任務的特點和硬體環境,來事先設定的。

當我們向固定大小的執行緒池中請求乙個執行緒時,如果執行緒池中沒有空閒資源了,這個時候執行緒池如何處理這個請求?是拒絕請求還是排隊請求?各種處理策略又是怎麼實現的呢?

相關章節

題目解析

這個問題的答案涉及佇列這種資料結構。佇列可以應用在任何有限資源池中,用於排隊請求,比如資料庫連線池等。實際上,對於大部分資源有限的場景,當沒有空閒資源時,基本上都可以通過「佇列」這種資料結構來實現請求排隊。

這個問題的具體答案,在佇列那一節我已經講得非常詳細了,你可以回去看看,這裡我就不贅述了。

這個功能並不複雜,它是通過維護乙個很大的ip位址庫來實現的。位址庫中包括ip位址範圍和歸屬地的對應關係。比如,當我們想要查詢202.102.133.13這個ip位址的歸屬地時,我們就在位址庫中搜尋,發現這個ip位址落在[202.102.133.0, 202.102.133.255]這個位址範圍內,那我們就可以將這個ip位址範圍對應的歸屬地「山東東營市」顯示給使用者了。

[202.102.133.0, 202.102.133.255]  山東東營市 [202.102.135.0, 202.102.136.255]  山東煙台 [202.102.156.34, 202.102.157.255] 山東青島 [202.102.48.0, 202.102.48.255] 江蘇宿遷 [202.102.49.15, 202.102.51.251] 江蘇泰州 [202.102.56.0, 202.102.56.255] 江蘇連雲港
在龐大的位址庫中逐一比對ip位址所在的區間,是非常耗時的。假設在記憶體中有12萬條這樣的ip區間與歸屬地的對應關係,如何快速定位出乙個ip位址的歸屬地呢?

相關章節

15 | 二分查詢(上):如何用最省記憶體的方式實現快速查詢功能?

題目解析

這個問題可以用二分查詢來解決,不過,普通的二分查詢是不行的,我們需要用到二分查詢的變形演算法,查詢最後乙個小於等於某個給定值的資料。不過,二分查詢最難的不是原理,而是實現。要實現乙個二分查詢的變形演算法,並且實現的**沒有bug,可不是一件容易的事情,不信你自己寫寫試試。

關於這個問題的解答以及寫出bug free的二分查詢**的技巧,我們在二分查詢(下)那一節有非常詳細的講解,你可以回去看看,我這裡就不贅述了。

我們希望自助開發乙個簡單的系統,不希望借助和維護過於複雜的三方系統,比如資料庫(mysql、redis等)、分布式儲存系統(gfs、bigtable等),並且我們單台機器的效能有限,比如硬碟只有1tb,記憶體只有2gb,如何設計乙個符合我們上面要求,操作高效,且使用機器資源最少的儲存系統呢?

題目解析

這個問題可以分成兩部分,第一部分是根據元資訊的搜尋功能,第二部分是判重。

第一部分,我們可以借助搜尋引擎中的倒排索引結構。關於倒排索引我會在實戰篇詳細講解,我這裡先簡要說下。

第二部分關於判重,我們要基於本身來判重,所以可以用雜湊演算法,對內容取雜湊值。我們對雜湊值建立雜湊表,這樣就可以通過雜湊值以及雜湊表,快速判斷是否存在。

我這裡只說說我的思路,這個問題中還有詳細的記憶體和硬碟的限制。要想給出更加詳細的設計思路,還需要根據這些限制,給出乙個估算。詳細的解答,我都放在在雜湊演算法(下)那一節裡到了,你可以自己回去看。

我們知道,雜湊表的查詢效率並不能籠統地說成是o(1)。它跟雜湊函式、裝載因子、雜湊衝突等都有關係。如果雜湊函式設計得不好,或者裝載因子過高,都可能導致雜湊衝突發生的概率公升高,查詢效率下降。

在極端情況下,有些惡意的攻擊者,還有可能通過精心構造的資料,使得所有的資料經過雜湊函式之後,都雜湊到同乙個槽裡。如果我們使用的是基於鍊錶的衝突解決方法,那這個時候,雜湊表就會退化為鍊錶,查詢的時間複雜度就從o(1)急劇退化為o(n)。

如果雜湊表中有10萬個資料,退化後的雜湊表查詢的效率就下降了10萬倍。更直觀點說,如果之前執行100次查詢只需要0.1秒,那現在就需要1萬秒。這樣就有可能因為查詢操作消耗大量cpu或者執行緒資源,導致系統無法響應其他請求,從而達到拒絕服務攻擊(dos)的目的。這也就是雜湊表碰撞攻擊的基本原理。

如何設計乙個可以應對各種異常情況的工業級雜湊表,來避免在雜湊衝突的情況下,雜湊表效能的急劇下降,並且能抵抗雜湊碰撞攻擊?

題目解析

我經常把這道題拿來作為面試題考察候選人。雜湊表可以說是我們最常用的一種資料結構了,程式語言中很多資料型別,都是用雜湊表來實現的。儘管很多人能對雜湊表都知道一二,知道有幾種雜湊表衝突解決方案,知道雜湊表操作的時間複雜度,但是理論跟實踐還是有一定距離的。光知道這些基礎的理論並不足以開發乙個工業級的雜湊表。

所以,我在雜湊表(中)那一節中詳細給你展示了乙個工業級的雜湊表要處理哪些問題,以及如何處理的,也就是這個問題的詳細答案。

這六道題你回答得怎麼樣呢?或許你還無法100%回答正確,沒關係。其實只要你看了解析之後,有比較深的印象,能立馬想到哪節課裡講過,這已經說明你掌握得不錯了。畢竟想要完全掌握我講的全部內容還是需要時間沉澱的。對於《資料結構與演算法之美》這門專欄的學習,你一定不要心急,慢慢來。只要方向對了就都對了,剩下就交給時間和努力吧!

測一測你的演算法階段學習成果

資料結構與演算法之美 專欄最重要的基礎篇馬上就要講完了,我從前面的文章中挑選了幾個例子,稍加修改,組成了一套測試題。你先不要著急看答案,自己先想一想怎麼解決,測一測自己對之前的知識掌握的程度。n如果有 卡殼或者不怎麼清楚的,可以回過頭再複習一下。正所謂溫故知新,這種通過實際問題查缺補漏的學習方法,非...

測一測你的演算法階段學習成果

資料結構與演算法之美 專欄最重要的基礎篇馬上就要講完了,我從前面的文章中挑選了幾個例子,稍加修改,組成了一套測試題。你先不要著急看答案,自己先想一想怎麼解決,測一測自己對之前的知識掌握的程度。n如果有 卡殼或者不怎麼清楚的,可以回過頭再複習一下。正所謂溫故知新,這種通過實際問題查缺補漏的學習方法,非...

測一測你的演算法階段學習成果

資料結構與演算法之美 專欄最重要的基礎篇馬上就要講完了,我從前面的文章中挑選了幾個例子,稍加修改,組成了一套測試題。你先不要著急看答案,自己先想一想怎麼解決,測一測自己對之前的知識掌握的程度。如果有 卡殼或者不怎麼清楚的,可以回過頭再複習一下。正所謂溫故知新,這種通過實際問題查缺補漏的學習方法,非常...