You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hello-algo/zh-Hant/docs/chapter_data_structure/summary.md

6.0 KiB

comments
true

3.5   小結

1.   重點回顧

  • 資料結構可以從邏輯結構和物理結構兩個角度進行分類。邏輯結構描述了資料元素之間的邏輯關係,而物理結構描述了資料在計算機記憶體中的儲存方式。
  • 常見的邏輯結構包括線性、樹狀和網狀等。通常我們根據邏輯結構將資料結構分為線性(陣列、鏈結串列、堆疊、佇列)和非線性(樹、圖、堆積)兩種。雜湊表的實現可能同時包含線性資料結構和非線性資料結構。
  • 當程式執行時,資料被儲存在計算機記憶體中。每個記憶體空間都擁有對應的記憶體位址,程式透過這些記憶體位址訪問資料。
  • 物理結構主要分為連續空間儲存(陣列)和分散空間儲存(鏈結串列)。所有資料結構都是由陣列、鏈結串列或兩者的組合實現的。
  • 計算機中的基本資料型別包括整數 byteshortintlong ,浮點數 floatdouble ,字元 char 和布林 bool 。它們的取值範圍取決於佔用空間大小和表示方式。
  • 原碼、一補數和二補數是在計算機中編碼數字的三種方法,它們之間可以相互轉換。整數的原碼的最高位是符號位,其餘位是數字的值。
  • 整數在計算機中是以二補數的形式儲存的。在二補數表示下,計算機可以對正數和負數的加法一視同仁,不需要為減法操作單獨設計特殊的硬體電路,並且不存在正負零歧義的問題。
  • 浮點數的編碼由 1 位符號位、8 位指數位和 23 位分數位構成。由於存在指數位,因此浮點數的取值範圍遠大於整數,代價是犧牲了精度。
  • ASCII 碼是最早出現的英文字符集,長度為 1 位元組,共收錄 127 個字元。GBK 字符集是常用的中文字符集共收錄兩萬多個漢字。Unicode 致力於提供一個完整的字符集標準,收錄世界上各種語言的字元,從而解決由於字元編碼方法不一致而導致的亂碼問題。
  • UTF-8 是最受歡迎的 Unicode 編碼方法通用性非常好。它是一種變長的編碼方法具有很好的擴展性有效提升了儲存空間的使用效率。UTF-16 和 UTF-32 是等長的編碼方法。在編碼中文時UTF-16 佔用的空間比 UTF-8 更小。Java 和 C# 等程式語言預設使用 UTF-16 編碼。

2.   Q & A

Q:為什麼雜湊表同時包含線性資料結構和非線性資料結構?

雜湊表底層是陣列,而為了解決雜湊衝突,我們可能會使用“鏈式位址”(後續“雜湊衝突”章節會講):陣列中每個桶指向一個鏈結串列,當鏈結串列長度超過一定閾值時,又可能被轉化為樹(通常為紅黑樹)。

從儲存的角度來看,雜湊表的底層是陣列,其中每一個桶槽位可能包含一個值,也可能包含一個鏈結串列或一棵樹。因此,雜湊表可能同時包含線性資料結構(陣列、鏈結串列)和非線性資料結構(樹)。

Qchar 型別的長度是 1 位元組嗎?

char 型別的長度由程式語言採用的編碼方法決定。例如Java、JavaScript、TypeScript、C# 都採用 UTF-16 編碼(儲存 Unicode 碼點),因此 char 型別的長度為 2 位元組。

Q:基於陣列實現的資料結構也稱“靜態資料結構” 是否有歧義?堆疊也可以進行出堆疊和入堆疊等操作,這些操作都是“動態”的。

堆疊確實可以實現動態的資料操作,但資料結構仍然是“靜態”(長度不可變)的。儘管基於陣列的資料結構可以動態地新增或刪除元素,但它們的容量是固定的。如果資料量超出了預分配的大小,就需要建立一個新的更大的陣列,並將舊陣列的內容複製到新陣列中。

Q:在構建堆疊(佇列)的時候,未指定它的大小,為什麼它們是“靜態資料結構”呢?

在高階程式語言中我們無須人工指定堆疊佇列的初始容量這個工作由類別內部自動完成。例如Java 的 ArrayList 的初始容量通常為 10。另外擴容操作也是自動實現的。詳見後續的“串列”章節。

Q:原碼轉二補數的方法是“先取反後加 1”那麼二補數轉原碼應該是逆運算“先減 1 後取反”,而二補數轉原碼也一樣可以透過“先取反後加 1”得到這是為什麼呢

這是因為原碼和二補數的相互轉換實際上是計算“補數”的過程。我們先給出補數的定義:假設 a + b = c ,那麼我們稱 abc 的補數,反之也稱 bac 的補數。

給定一個 n = 4 位長度的二進位制數 0010 ,如果將這個數字看作原碼(不考慮符號位),那麼它的二補數需透過“先取反後加 1”得到


0010 \rightarrow 1101 \rightarrow 1110

我們會發現,原碼和二補數的和是 0010 + 1110 = 10000 ,也就是說,二補數 1110 是原碼 001010000 的“補數”。這意味著上述“先取反後加 1”實際上是計算到 10000 的補數的過程

那麼,二補數 111010000 的“補數”是多少呢?我們依然可以用“先取反後加 1”得到它


1110 \rightarrow 0001 \rightarrow 0010

換句話說,原碼和二補數互為對方到 10000 的“補數”,因此“原碼轉二補數”和“二補數轉原碼”可以用相同的操作(先取反後加 1 )實現。

當然,我們也可以用逆運算來求二補數 1110 的原碼,即“先減 1 後取反”:


1110 \rightarrow 1101 \rightarrow 0010

總結來看,“先取反後加 1”和“先減 1 後取反”這兩種運算都是在計算到 10000 的補數,它們是等價的。

本質上看,“取反”操作實際上是求到 1111 的補數(因為恆有 原碼 + 一補數 = 1111);而在一補數基礎上再加 1 得到的二補數,就是到 10000 的補數。

上述以 n = 4 為例,其可被推廣至任意位數的二進位制數。