從零開始的算法入門科普(一),你應該知道的數據結構類型·其一


壹 ❀ 引

算法,對於大多數開發者而言,是一個既陌生又熟悉的詞匯。陌生在於它很高格調,學好算法似乎很難;而熟悉在於,開發中的一次排序,一次查找甚至一次計算都與算法脫不開關系。

不可否認,不管你使用的是哪種開發語言,學好算法是成為高級開發的必經之路。而像博主這種非計算機專業出身,大學沒學過數學的人而言,學算法尤為痛苦(苦笑)...

我之前打算通過刷LeetCode來提升算法能力,但因為缺乏算法基礎知識,啃得過慢。如果你有關注過我的博客更新,就能發現我之前有寫過三篇排序文章,很尷尬,寫了忘忘了寫反復之間我選擇暫停刷題。

最近我從公司借了一本不錯的算法入門圖書《演算法圖鑒》,大致翻閱,本書主要圍繞數據結構,排序,查找,安全算法等幾個章節展開,圖解的形式非常適合我這種小白。所以我打算花半個月左右時間將本書啃完,而這片文章算是我讀此書的開端。

2020.6.5更新,微信讀書已更新《演算法圖鑒》電子版,對於算法0基礎小白強烈推薦,無任何代碼全部以圖文展示--我的第一本算法書

學習不是一兩天,算法提升也是如此。有志者事竟成,願你我都能在算法上勇敢跨出第一步,那么本文開始。

貳 ❀ 算法與數據結構

數據結構(data structure)用於決定數據的存放順序與位置。有同學肯定納悶,說算法怎么又扯到數據結構了,讓我們通過一個例子來解釋算法與數據結構的緊密關系。

假設我們現在需要存儲一些用戶的電話號碼,信息就包括用戶名與用戶的手機號碼,我們首先想到的自然是如下結構:

姓名 電話號碼
張三 136****1734
李四 130****3312
王五 130****7213
李星星 135****1535
西西 173****9797

這樣存儲其實沒問題,如果再有新增,我們只需在結尾再加一行即可。好了,現在我們要查詢王五的電話號碼,由於存儲並無規則,所以我們只能從頭到尾查一遍,或者隨機性的查閱,運氣好一眼看到了。但如果數量過大,可能這個運氣就很難好起來了。

如何改進?有同學立馬想到手機通訊錄存儲方式,按照姓名首字母排序,這樣很棒,如下:

姓名 電話號碼
(L)李四 130****3312
(L)李星星 135****1535
(W)王五 130****7213
(X)西西 173****9797
(Z)張三 136****1734

現在要找到王五就非常簡單了,只要熟悉字母表順序,掃一眼就能快速定位到W開頭的姓氏,大大降低了查找難度。可問題又來了,現在要新增一條用戶數據汪倫,由於也屬於W開頭,所以得緊跟王五之后,但由於沒了空間,所以我們只能將西西與張三統一往后挪騰出空間,假設這個行為都是我們手動完成,想想就覺得麻煩。

還能再改進嗎?讓我們回顧上面兩種存儲方式,方式一查找麻煩但是新增方便,方式二查找方便但是新增麻煩,那要不我們將兩種方式結合下?如下:

姓名(L) 電話號碼
李四 130****3312
李星星 135****1535
姓名(W) 電話號碼
王五 130****7213
汪倫 130****3312
姓名(X) 電話號碼
西西 173****9797
姓名(Z) 電話號碼
張三 136****1734

現在我們將不同字母開頭的姓氏分別存在不同的表中,這樣查找和新增都非常方便了。你看,我們先不談算法,好的數據結構本質上就能大大提升算法效率,所以了解常見的數據結構很有必要。

叄 ❀ 常見數據結構類型

叄 ✿ 壹 鏈表List

鏈表是數據結構中的一種,這類結構的數據排成一條直線,便於查找和刪除,但存放數據比較費時。鏈表往往由數據和指針構成:

這是一個理想中的鏈表結構,每個數據都有指針指向下一個數據所在的位置。實際存儲可能沒有這么乖巧,因為有指針起到指向作用,因此即便數據分散開來也不影響鏈表存儲數據。

鏈表一大特點就是查找某個數據時,必須按照指針依次讀取數據,比如上圖中想要找到Red,必須先找到Blue再找到Yellow依次查找才行。

鏈表結構追加數據時,要做的就是將插入數據前的指針指向自己,並將新數據的指針指向原本緊接的數據即可,比如現在我們在Yellow與Red之間新增一個Purple:

假設我們現在要刪除Purple數據也很簡單,將Yellow指向Purple的指針指向Red即可:

雖然圖解上來看,Purple還有指針指向Red數據,但實際Purple已經變成了無法讀取的數據。

以上鏈表一大特點是作為數據結尾的Red並無指針,有沒有Red也有指針的情況呢?有,比如循環鏈表(circular list),循環鏈表並沒有頭尾的概念,每個數據都扮演着頭尾的角色。

除此之外,上面的鏈表數據都只有一個指針,事實上也存在兩個指針的情況,也就是雙向鏈表(bidirectional list),雙向鏈表的特點就是沒有前后之分,從數據讀取上來說比單向更方便,缺點是如果要新增數據或者刪除時,需要修改的指針會大大增多:

鏈表在查找上由於需要從頭開始找,數量越多查找時間越長,所以查找執行時間與數據個數n有關,因此查找時間為O(n),而新增數據時只需要修改制定的箭頭指向,這與數據個數無關,因此修改數據的執行時間為O(1)。

叄 ✿ 貳 數組Array

數組也是數據結構的一種,像我這種前端開發,與數組打交道的次數就多到數不清。數組結構的數據在追加數據以及讀取時會非常便利,但在插入數據以及刪除數據會耗時耗力。一個理想的數組結構如下:

上圖展示的其實就是一個包含了Blue、Yellow、Red三個字符串的數組a。數組中的數據具有連續性,所以我們可以通過索引訪問指定位置的數據。比如現在要找到Red鏈表得從頭開始查,而數組只用通過索引a[2]即可訪問,非常方便。

數組雖然查很便利,但如果要在數組中插入或刪除某個元素就會比鏈表遜色很多,比如我們要在Bule和Yellow中間插入Purple,如下圖:

首先數組中會新建追加數據的空間,之后依次將數據往后移動,直到將Blue后的空間騰出來,然后將Purple放進去,這就是一次數組的插入;同理,當我們要刪除Purple時,又得將Purple之后的數據往前挪,直到填補上前面的空缺,所以對比起來數組在中間插入與刪除上確實比鏈表要麻煩的多。

前面我們說了數組查詢可依賴索引,查詢時間與數組元素個數無關,所以查詢耗時為O(1),而修改數組,比如新增刪除,按照時間復雜度最壞的來計算,假設是在第一位前新增元素,或者刪除第一位元素,要做的操作就會受到數組元素個數n的影響,所以操作的時間復雜度為O(n)。

肆 ❀ 總

那么到這里,我們介紹數據類型中的鏈表與數組兩種數據,當然數據類型遠遠不止這兩種,因為時間問題(明天還要上班),后面的幾種類型我會在第二篇中介紹。

回顧上文,我們來將兩種數據結構做個對比:

讀取 新增 刪除
鏈表 慢 O(n) 快 O(1) 快 O(1)
數組 快 O(1) 慢 O(n) 慢 O(n)

你看,不同數據結構在不同操作上表現會完全不同,以數組和鏈表為例,如果我們希望數據知識單獨訪問,很明顯數組更為方便,而如果數組要頻繁操作,鏈表就更具優勢了,這就是數據結構的魅力。

由於本文記錄過程中畫圖確實有點費時,后續的數據結構我會在第二篇中介紹,加油加油,那么本文到這里正式結束。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM