講個大部分資料結構和演算法教科書中都不會講的問題

小爭哥發表於2019-03-29

關注我的微信公眾號:小爭哥,獲取更多、更新的技術、非技術分享。
作者:前Google工程師,5萬人訂閱《資料結構和演算法之美》專欄作者。
希望通過我加速你的技術、職場進步。

大部分資料結構和演算法書籍中,在講某種資料結構和演算法的時候,都會拿整數作為要處理的資料的型別。實際上,在真實的軟體開發中,資料結構中儲存的資料、演算法要處理的資料,往往都不是簡單的整數,而是”物件“。這裡的”物件“很好理解,就是程式語言的中的”類與物件“中的物件。比如下面這幾行Java程式碼,我們用紅黑樹來儲存Order訂單物件。

// 紅黑樹來儲存訂單,key是訂單ID,value是訂單物件
private TreeMap<Long, Order> id2OrderMap = new TreeMap<>(); 
 
public void add(Order order) { // 新增一個訂單
 id2OrderMap.put(order.id, order);
}
 
private class Order { // 訂單類
 public long id;
 public long userId;
 public long createTime;
 // ...more fields...
}
複製程式碼

不過,你可能會有疑問,既然在真實的軟體開發中,資料結構和演算法要處理的都是物件,那為啥書本中都可以按照拿整數型別,而不是物件來講解呢?

我們知道,資料結構和演算法要解決的都是”更快“、”更省“的問題。更快指程式碼的執行速度,更省指儲存空間的消耗。

”更快“這裡你先簡單理解為”查詢更快“,當然還有增刪改以及一些統計操作,不過,都差不多,你理解了”查詢“,其他的操作也就都理解了。我們在一組資料中,查詢一個想要的資料的時候,都是通過某個鍵值(key)來查詢的。

這裡的鍵值怎麼理解呢?我們拿剛剛的訂單的例子來說明一下。我們希望在這組訂單中,按照訂單ID來快速的查詢訂單。那我們就把訂單的ID看做鍵值(key),並且按照key來組織成紅黑樹這種資料結構。紅黑樹中儲存的是key值和訂單物件的記憶體地址,而訂單物件本身是儲存在另外的記憶體空間中的(像Java這種語言,就是儲存在堆記憶體中的)。

為了方便你理解,我畫了一張圖,你可以對比著文字描述看下。

講個大部分資料結構和演算法教科書中都不會講的問題

那你可能說,那我要是再想通過訂單的使用者ID來查詢訂單呢?這個時候該怎麼辦呢?

你就可以再以使用者ID作為鍵值,建立另外一個紅黑樹。實際上,這就是我們之前文章中提到的多重索引結構。如果翻譯成Java程式碼的話,就是下面這一個樣子。

// 兩個索引
private TreeMap<Long, Order> id2OrderMap = new TreeMap<>();
private TreeMap<Long, Order> userId2OrderMap = new TreeMap<>();
 
public void add(Order order) { // order在外部建立,記憶體中只有一份
 id2OrderMap.put(order.id, order);
 userId2OrderMap.put(order.id, order);
}
 
private class Order {
 public long id;
 public long userId;
 public long createTime;
 // ...more fields...
}
複製程式碼

如果畫成圖的話,就是下面這個樣子。你會發現,訂單物件是隻有一份的,而索引結構有兩份,一份是按照訂單的ID作為鍵值建立的,另一份是按照訂單的使用者ID作為鍵值建立的。

講個大部分資料結構和演算法教科書中都不會講的問題
你可能會說,兩份索引是不是比較浪費記憶體空間啊?實際上,並不會。

從我前面的講解中,你應該已經知道,索引中儲存的只是就鍵值和物件的記憶體地址,如果鍵值是大小比較小的整數,而物件是比較大的物件(資料本身),那索引所佔用記憶體空間比起物件所佔用的記憶體空間來說,要小很多。

還是剛才那個訂單的例子,如果一個訂單物件中包含很多欄位,大小是1KB,而紅黑樹索引中,每個節點(對應一個訂單物件)的大小要遠小於1KB(這個你可以自己算下)。所以,所佔用記憶體空間會遠小於訂單資料本身儲存所需的記憶體空間。

也就是說,在實際上的軟體開發中,如果你要儲存、要處理的是大物件,那索引所佔的記憶體空間,是可以忽略的。當然,這個要權衡實際上情況來看,不可生搬硬套。

回到開頭的問題,既然真實的軟體開發中,資料結構和演算法處理的資料型別都是物件,那為什麼大部分資料結構和演算法教科書中,都是拿整數型別來講解呢?

因為我們在構建資料結構的時候,是按照鍵值構建,演算法在執行的時候,也是按照鍵值來處理資料。鍵值一般都是整數(有時候是字串,處理起來一樣),我們拿整數型別來講解,簡單明瞭,足夠了。

相關文章