5分鐘瞭解資料結構

morningli發表於2022-02-13

  老外真的很喜歡創造新名詞。春節的時候我給我姐選顯示器,因為我姐是做設計的,我特地研究了一下顯示器的色域。我發現除了我們常用的sRGB外,還有個Adobe RGB。後來經過了解,原來是Photoshop的工程師在參考其他色彩標準的時候搞錯了引數,後來懶得改了,便自己申請了專利,搞成了一個新的色彩標準,後來隨著Adobe軟體的流行現在已經變成了行業標準。因為老外不厭其煩的創造新名詞,給我們造成了不小的困擾。比如面試的時候,面試官問你AVL樹,你知道什麼是平衡二叉樹,不知道什麼是AVL樹,面試官知道問AVL樹,卻不知道問平衡二叉樹,結果大家面面相覷。世界上最遠的距離,莫過於你跟面試官背的不是同一套八股文。以後遇到這樣的面試官,不妨問下他什麼是基數樹。基數樹不知道嗎,那trie樹總該知道吧,trie樹也不知道的話字典樹總該知道吧,字典樹也不知道那字首樹總該知道吧。相信面試官會被問的懷疑人生。

  言歸正傳,來講一下資料結構,下面是百度百科對資料結構的定義:

  資料結構(data structure)是帶有結構特性的資料元素的集合,它研究的是資料的邏輯結構和資料的物理結構以及它們之間的相互關係,並對這種結構定義相適應的運算,設計出相應的演算法,並確保經過這些運算以後所得到的新結構仍保持原來的結構型別。簡而言之,資料結構是相互之間存在一種或多種特定關係的資料元素的集合,即帶“結構”的資料元素的集合。“結構”就是指資料元素之間存在的關係,分為邏輯結構和儲存結構。
  資料的邏輯結構和物理結構是資料結構的兩個密切相關的方面,同一邏輯結構可以對應不同的儲存結構。演算法的設計取決於資料的邏輯結構,而演算法的實現依賴於指定的儲存結構。

  不知道大家覺得好不好理解,簡單說來,計算機基本上就做兩件事:儲存資料和處理資料。資料結構,是老外總結出來的一套行之有效的資料儲存方式,並圍繞著這些儲存方式設計出了一些高效的操作演算法。藉助資料結構,我們可以高效地管理多個資料,進而完成更復雜的功能。下圖列舉了一些常見的資料結構,後面會逐個進行講解:

列表

   通常一系列資料A0,A1,A2,A3,...,AN-1 可以採用表來儲存。表一般支援幾種操作,尋找某元素所在位置,在表的某個位置插入和刪除某元素,返回某個位置上的元素等。

   棧也叫LIFO(後進先出)表,元素的插入和訪問、刪除都只能從一個方向進行。通常支援入棧操作、出棧操作以及返回當前棧頂元素。

佇列

   佇列插入時再一端進行,刪除時只能在另一端進行,也叫FIFO(先進先出)表。通常支援入隊操作,出隊操作和返回對頭元素。

 

  樹是資料結構考核的重點,涉及了很多的概念,也發展出來很多的變種,需要花大力氣去學習。樹是一些節點的集合,這個集合可以是空集;如果不是空集,則樹由稱作根(root)的節點r和0個或者多個非空的(子)樹T1,T2,...,Tk組成,這些子樹中每一棵的根都被來自根r的一條有向邊(edge)所連線。每一棵子樹的根叫做根r的兒子(child),而r是每棵子樹的根的父親(parent)。沒有兒子的節點稱為樹葉(leaf),具有相同父親的節點為兄弟(siblings)。在上圖,A是B和C的父親,B和C是A的兒子,B和C是兄弟,D和F沒有兒子所以叫葉子。

  從節點n1到nk的路徑(path)定義為節點n1,n2,...,nk的一個序列,使得對於1<=i<k的節點ni是ni+1的父親。這條路徑的長(length)是該路徑上的邊的條數,即k-1。從每一個節點到他自己有一條長為0的路徑。注意,在一棵樹中從根到每個節點恰好存在一條路徑。對任意節點ni,ni的深度(depth)為從根到ni的唯一路徑的長。因此,根的深度為0。節點ni的高(height)是從ni到一片樹葉的最長路徑的長。一棵樹的高度(height of a tree)等於它的根的高。一棵樹的深度(depth of a tree)等於其最深的樹葉的深度,該深度總是等於這棵樹的高。

  樹經常被考核到的點是他的幾種遍歷策略,分別是先序遍歷、後序遍歷和中序遍歷,三種遍歷策略的區別主要是根節點是在什麼時候處理的。先序遍歷是先處理根節點,再處理他的兒子節點;後序遍歷是等他所有的子節點處理完後再處理根節點。中序遍歷只會出現在二叉樹中,二叉樹每個根節點最多有左右兩個兒子節點,處理順序分別是左二子節點、根節點、右兒子節點。

雜湊表

   雜湊表(hash table)常常也會根據英文音譯成雜湊表。雜湊表不像普通的表,資料都是一個跟一個緊密儲存的,而是根據一個雜湊演算法計算出元素儲存的位置再儲存。這樣做的好處是對應每個特定元素,雜湊表都能夠在O(1)的時間複雜度內通過雜湊演算法算出元素所在位置,從而實現元素的快速訪問。雜湊表關鍵的技術細節在於衝突的處理。雜湊函式給兩個不同的元素計算出來的位置值是相同時,我們稱作衝突(collision)。衝突的解決有幾種方式,分別是分離連結法(separate chaining,也叫拉鍊法,鏈地址法)、探測雜湊表(probing hash table,也叫開放定址雜湊法,open addressing hashing,包括線性探測法、平方探測法和雙雜湊)。網上還有各種各樣的衝突解決方法,如果大家感興趣可以留言評論,我會在後續的部落格詳細講解。

 

優先佇列

 

 

優先佇列設計的目的是找出、返回並刪除最小的元素,支援插入操作,刪除最小者操作。

 

這是Wikipedia上各種資料結構的時間複雜度對比,選擇資料結構時可以參考一下:

 :插入未排序陣列有時被引用為O ( n ),因為假設要插入的元素必須插入到陣列的一個特定位置,這需要將所有後續元素移動一個位置。然而,在經典陣列中,陣列用於儲存任意未排序的元素,因此任何給定元素的確切位置無關緊要,插入是通過將陣列大小增加 1 並將元素儲存在末尾來執行的陣列,這是一個O (1) 操作。同樣,刪除操作有時被引用為O ( n) 由於假設必須移動後續元素,但在經典的未排序陣列中,順序並不重要(儘管元素是按插入時間隱式排序的),因此可以通過將要刪除的元素與最後一個元素交換來執行刪除陣列中的元素,然後將陣列大小減 1,這是一個O (1) 操作。

  那麼多的資料結構我們要如何選擇呢?資料結構終究是服務於你的程式的,學習資料結構關鍵還是理解資料結構的設計思路,不用過於拘泥。以前上大學的時候我自己開發了一個電子詞典程式,當時並沒有學習過字典樹,但是根據電子詞典的需要設計出來的資料結構就是字典樹。現在的面試官很喜歡問各種術語各種概念,卻不知道最重要的其實還是問題的解決本身。當然也不是說不需要學習資料結構,既然我們要站在巨人的肩膀前進,至少我們得知道巨人的肩膀在哪是不是。下面總結了常用資料結構的一些權衡:

 

引用:

  • https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1450?fr=aladdin
  • https://en.wikipedia.org/wiki/Search_data_structure
  • https://en.wikipedia.org/wiki/Skip_list
  • 《資料結構與演算法分析——C++語言描述(第四版)》Mark Allen Weiss著

 

相關文章