雜湊表(Hash Table)& 字典樹(Trie)的程式碼和補充 Malloc 用法

更新於 發佈於 閱讀時間約 13 分鐘

圖片來源和參考內容:Hash Tables and Hash Functions

更深入的雜湊表筆記請參考:資料結構學習筆記:雜湊表(Hash Table)

想像一下,如果要在一個陣列中找出「Mia」,是否需要逐一檢查每個項目?有沒有更快的方法?

雜湊函數(Hash function)

雜湊函數(Hash function)就像是一台「資料壓縮機」,它把任意長度的資料壓縮成特定範圍的數值,稱為「雜湊值」。想像一下,有一大串名字要放進抽屜裡,為了不必每次翻箱倒櫃地找,雜湊函數就像一個貼標籤的助手。它會按照某種規則,給每個名字分配一個特定抽屜號碼,比如「Mia」可能會分到抽屜 8 號,「Sam」分到 4 號。

當我們想找某個名字時,只需照著這個編號打開對應的抽屜即可。但有時候,兩個不同名字會分到同一個抽屜,這就叫「衝突」。為解決衝突,可能會用指標將兩個名字串在一起,或是換個抽屜放置。這種方法讓雜湊表的搜尋效率高,即便資料量大,仍能迅速找到所需內容

雜湊表使用在通訊錄

假設我們要建立一個通訊錄,可以用 hash table(雜湊表)來存取人名,並利用字母的開頭來快速找到對應的位置。例如,我們可以建立一個包含 26 個索引值的 hash table。

如何知道該索引值呢?以 Mia 為例,假設 Mia 的位置是 8。這是如何計算出來的呢?

  1. 先將每個字母轉為 ASCII 值。
  2. 再用該值除以陣列大小(在這例子中為 11),取餘數作為索引位置,因此 Mia 的索引值是 4。
截圖至參考影片

截圖至參考影片


在這樣的表格中記錄資料索引值,便能方便找到每個人名。但這樣的設計也可能會遇到「衝突」(collision)的問題。例如,如果 Mia 和 Rae 的餘數都是 4,則 Rae 只能向後移動,導致查找 Rae 時需要進行線性搜尋。

raw-image


為了解決這個問題,可以使用指標(pointer)或「鏈結串列」來連接同一個位置的多個元素。當衝突發生時,會將新元素放到這個位置的鏈結串列中,並透過指標指向。這樣一來,我們可以在每個索引值的基礎上儲存多個元素,而不用重新排列整個表格,提升了搜尋效率。

例如下圖,Mia 跟 Sue 都的值都是 4 ,所以他們會放在同一列並用指標連結。

raw-image



字典樹(Trie)

另外,另一種資料結構——字典樹(Trie)——可以用來更直觀地儲存和搜尋文字。Trie 將每個字的字母作為節點,形成一個多層的分支結構,方便快速搜尋。例如,若有「Mia」和「Max」,這兩個名字會共享相同的前綴字母「M」,然後各自分支到不同的子節點「i」和「a」。

當名字清單更多時,也可以設計 「Mi」 或是 「Ma」「Mo」為單位搜尋取代先搜尋M再往下搜尋第二個字母,時間複雜度變低,速度變快,但是所佔用的空間就會多。

這種結構特別適合用於自動完成功能或搜尋大規模字詞資料,因為它可以避免重複存儲字母,同時提高搜尋效率。



程式碼演練:Leetcode 題目

其實,我是有問ChatGPT malloc 怎麼寫,當然google也可以

我前面這篇文章提到的,實在是太淺了... 寫了一個小時才生出來...

C 語言指標-程式碼圖解

https://leetcode.com/problems/two-sum

題目是給你目標值,請從陣列當中回傳兩個相加等於目標值的位置

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

這是 Leetcode 上 被評為簡單的題目,但是用 C 語言寫需要malloc記憶體,我完全卡再 malloc 的用法。

/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {// leetcode已寫好這行
for (int i = 0; i < numsSize; i++) {
for (int j = i + 1; j < numsSize; j++) {
if (nums[i] + nums[j] == target) {
int* result = (int*)malloc(2 * sizeof(int));
result[0] = i;
result[1] = j;
*returnSize = 2;
return result;
}
}
}
*returnSize = 0;
return NULL;
}

讓我來解釋,

int* result = (int*)malloc(2 * sizeof(int));

基本概念是要新增兩個整數的空間,拆成兩階段來看:

  • sizeof(int):這個函式會回傳一個整數的位元組大小。在大多數系統中,int 佔用 4 個位元組(Bytes),所以 sizeof(int) 通常是 4。
  • 2 * sizeof(int):表示分配一段空間,大小為兩個整數所需的空間(在大多數系統上就是 8 個位元組)。這樣我們就能在這段記憶體中存放兩個整數(即我們找到的兩個索引)。例[0,1]
  • 通常建立的記憶體儲存空間沒有指定資料型別,所以我這裡使用 (int*)void* 轉型為 int*,表示這段記憶體是一組整數result[0]result[1] 存取這兩個整數位置,也就是 twoSum 找到的兩個索引。

用雜湊表使時間複雜度變 O(n)

這裡我有偷看答案怎麼建立 hash table XD,因為 C 語言寫雜湊表對我來說太難了 ,所以我用 Python 來解釋,著重在介紹概念。

class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:#這行程式已經內建好了 
hashmap = {}
for i in range(len(nums)):
hashmap[nums[i]] = i
for i in range(len(nums)):
complement = target - nums[i] #complement是代表互補值的變數,後面可以直接拿來查表
if complement in hashmap and hashmap[complement] != i:
return [i, hashmap[complement]]
# If no valid pair is found, return an empty list
return []

hashmap = {}

  • 這行才是真正建立一個空的 hash table(即 hashmap)。
for i in range(len(nums)):

hashmap[nums[i]] = i

這裡的 for 迴圈只負責將 nums 中的每個數字及其索引全部存入 hashmap 中。在這個迴圈完成後,hashmap 會包含 nums 中所有數字及其對應的索引。

第二個回圈是開始檢查互補值是否存在於 hashmap 中。

第二個 for 迴圈會重新遍歷 nums,然後計算每個數字的 complement。它會檢查兩件事:

  1. complement 是否存在於 hashmap 中。
  2. hashmap[complement] != i(即 complement 的索引不能和當前數字的索引相同,不可以自己相加)。

運作範例

第一步:第一個 for 迴圈(建立 hashmap

  1. i = 0nums[i] = 2
    • hashmap[2] = 0,此時 hashmap 為 {2: 0}
  2. i = 1nums[i] = 7
    • hashmap[7] = 1,此時 hashmap 為 {2: 0, 7: 1}
  3. i = 2nums[i] = 11
    • hashmap[11] = 2,此時 hashmap 為 {2: 0, 7: 1, 11: 2}
  4. i = 3nums[i] = 15
    • hashmap[15] = 3,此時 hashmap 為 {2: 0, 7: 1, 11: 2, 15: 3}

到此為止,我們完成了 hashmap 的建立,hashmap 已經包含了 nums 陣列中每個數字的索引。(這段我請 ChatGPT 幫忙寫)

第二步:第二個 for 迴圈(查找符合條件的 complement

  1. i = 0nums[i] = 2
    • complement = target - nums[i] = 9 - 2 = 7
    • 7 在 hashmap 中,且 hashmap[7] != 0(7 的索引為 1,不是 0),滿足條件
    • 找到答案:return [0, 1]

最後結果

此時返回 [0, 1],即 nums 中索引為 01 的兩個數字 27 加起來等於 9,符合題目要求。

更快的方式:一次迴圈解決

(因方格子介面問題,註解的部分沒有反灰)

class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashmap = {}
for i in range(len(nums)):
complement = target - nums[i]
if complement in hashmap:
return [i, hashmap[complement]]
hashmap[nums[i]] = i
# Return an empty list if no solution is found
return []


運作範例

假設 nums = [2, 7, 11, 15]target = 9,這段程式碼的執行步驟如下:

  • 第一次迴圈 (i = 0nums[i] = 2):
    • complement = 9 - 2 = 7
    • 7 不在 hashmap 中,因此將 2 和它的索引 0 加入 hashmap:{2: 0}
  • 第二次迴圈 (i = 1nums[i] = 7):
    • complement = 9 - 7 = 2
    • 2 在 hashmap 中,表示 2 和 7 可以組成一組和為 9 的數字對。
    • 返回 [1, hashmap[2]],即 [1, 0]。
留言
avatar-img
留言分享你的想法!
avatar-img
越南放大鏡 X 下班資工系
31會員
84內容數
雙重身份:越南放大鏡 X 下班資工系 政大東南亞語言學系是我接觸越南語的起點,畢業後找越南外派工作的生活跟資訊時,發現幾乎都是清單式的分享,很難身歷其境。所以我希望「越南放大鏡」可以帶讀者看到更多細節和深入的觀察。 - 下班資工系則是自學資工系的課程內容,記錄實際操作的過程,學習理論的過程。希望可以跟讀者一起成長。
2025/04/24
本系列文章將循序漸進地介紹 JavaScript 的核心概念,從基礎語法到進階應用,例如非同步程式設計和 React 基礎。內容淺顯易懂,並使用生活化的比喻幫助讀者理解,搭配程式碼範例,適合 JavaScript 初學者學習。
Thumbnail
2025/04/24
本系列文章將循序漸進地介紹 JavaScript 的核心概念,從基礎語法到進階應用,例如非同步程式設計和 React 基礎。內容淺顯易懂,並使用生活化的比喻幫助讀者理解,搭配程式碼範例,適合 JavaScript 初學者學習。
Thumbnail
2025/04/21
本文介紹行動通訊網路的演進歷史,從1G到5G,並說明ITU與3GPP在制定通訊規格上的重要角色,以及5G的三大關鍵應用場景:URLLC、eMBB和mMTC。
Thumbnail
2025/04/21
本文介紹行動通訊網路的演進歷史,從1G到5G,並說明ITU與3GPP在制定通訊規格上的重要角色,以及5G的三大關鍵應用場景:URLLC、eMBB和mMTC。
Thumbnail
2025/04/11
這篇文章說明網路的七層模型、IP 位址、通訊埠、TCP/UDP 協定、HTTP 協定、HTTP 狀態碼以及 WebSocket,並解釋它們之間的關係與互動方式。文中包含許多圖表和範例,幫助讀者理解這些網路概念。
Thumbnail
2025/04/11
這篇文章說明網路的七層模型、IP 位址、通訊埠、TCP/UDP 協定、HTTP 協定、HTTP 狀態碼以及 WebSocket,並解釋它們之間的關係與互動方式。文中包含許多圖表和範例,幫助讀者理解這些網路概念。
Thumbnail
看更多
你可能也想看
Thumbnail
2025 vocus 推出最受矚目的活動之一——《開箱你的美好生活》,我們跟著創作者一起「開箱」各種故事、景點、餐廳、超值好物⋯⋯甚至那些讓人會心一笑的生活小廢物;這次活動不僅送出了許多獎勵,也反映了「內容有價」——創作不只是分享、紀錄,也能用各種不同形式變現、帶來實際收入。
Thumbnail
2025 vocus 推出最受矚目的活動之一——《開箱你的美好生活》,我們跟著創作者一起「開箱」各種故事、景點、餐廳、超值好物⋯⋯甚至那些讓人會心一笑的生活小廢物;這次活動不僅送出了許多獎勵,也反映了「內容有價」——創作不只是分享、紀錄,也能用各種不同形式變現、帶來實際收入。
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
給定一個字串陣列,請把它們所共有的字元伴隨著出現次數輸出。這篇文章介紹如何使用字典統計出現次數,和字典取交集的方法來解決此問題。並提供了複雜度分析和關鍵知識點。
Thumbnail
給定一個字串陣列,請把它們所共有的字元伴隨著出現次數輸出。這篇文章介紹如何使用字典統計出現次數,和字典取交集的方法來解決此問題。並提供了複雜度分析和關鍵知識點。
Thumbnail
複習一下: 我們學習了關於撰寫程式的相關觀念 條件分支(if/else) : 藉由條件分支讓程式執行相對應的功能。 迴圈(while loop ) :程式利用迴圈反覆執行某個區塊的程式碼。 字串處理 (string) : 每個程式都在處理資料,而字串是一種非常重要且常用的資料。 函式(fu
Thumbnail
複習一下: 我們學習了關於撰寫程式的相關觀念 條件分支(if/else) : 藉由條件分支讓程式執行相對應的功能。 迴圈(while loop ) :程式利用迴圈反覆執行某個區塊的程式碼。 字串處理 (string) : 每個程式都在處理資料,而字串是一種非常重要且常用的資料。 函式(fu
Thumbnail
題目會給定一組已經規定好的介面interface,要求我們實作HashSet這種資料結構。也就是一般數學和程式語言中所說的"集合"。
Thumbnail
題目會給定一組已經規定好的介面interface,要求我們實作HashSet這種資料結構。也就是一般數學和程式語言中所說的"集合"。
Thumbnail
班上闖來了一個陌生人!該如何快狠準揪出他?這道經典考題的解法,遠比你想的還要多種 ......
Thumbnail
班上闖來了一個陌生人!該如何快狠準揪出他?這道經典考題的解法,遠比你想的還要多種 ......
Thumbnail
陣列運用、擷取字串   對於陣列裡的內容值除了把資料存進去外,若想要知道陣列維度、陣列大小、複製陣列的值到另一個陣列中、清除陣列的值等等的相關處理,甚至比較常用到的可能還需要做資料排列、查找資料等等,此時C#有一些屬性方法可以幫助到我們,不用寫複雜的迴圈,來看一看有哪些吧~
Thumbnail
陣列運用、擷取字串   對於陣列裡的內容值除了把資料存進去外,若想要知道陣列維度、陣列大小、複製陣列的值到另一個陣列中、清除陣列的值等等的相關處理,甚至比較常用到的可能還需要做資料排列、查找資料等等,此時C#有一些屬性方法可以幫助到我們,不用寫複雜的迴圈,來看一看有哪些吧~
Thumbnail
在使用陣列時一開始也是需要做宣告的,那麼這次說的內容是一維陣列,因此一維陣列宣告內容包括:資料型態、陣列名稱、以及陣列的大小。那麼我們就來看一下它的語法是如何的: 說明:等號左邊是做宣告,而右邊是做建立的動作。 一、初始值設定 那麼初始值要怎麼設定呢?這邊有幾種方法,用例子帶大家來看一看: 1.有給
Thumbnail
在使用陣列時一開始也是需要做宣告的,那麼這次說的內容是一維陣列,因此一維陣列宣告內容包括:資料型態、陣列名稱、以及陣列的大小。那麼我們就來看一下它的語法是如何的: 說明:等號左邊是做宣告,而右邊是做建立的動作。 一、初始值設定 那麼初始值要怎麼設定呢?這邊有幾種方法,用例子帶大家來看一看: 1.有給
Thumbnail
  陣列(Array)是什麼?它是一個很好用的東西哦!當我們要處理100個學生的成績的時候,如果沒有陣列的話,那麼我們的變數就要命名100個不同的變數,這樣的程式雖然不是不能執行,想一想,是不是有一點要在命名上會想破頭殼呢?因為要想100個不同的變數麻~   你說:「那就變數後面加入編號就好啦~如
Thumbnail
  陣列(Array)是什麼?它是一個很好用的東西哦!當我們要處理100個學生的成績的時候,如果沒有陣列的話,那麼我們的變數就要命名100個不同的變數,這樣的程式雖然不是不能執行,想一想,是不是有一點要在命名上會想破頭殼呢?因為要想100個不同的變數麻~   你說:「那就變數後面加入編號就好啦~如
Thumbnail
Array 在記憶體中連續分配,而且元素類型是一樣的,長度不變 優點:讀取快,可以使用座標訪問 缺點:新增、刪除慢 記憶體: 📷 範例程式碼: ArrayList 不定長度,在記憶體中連續分配的,元素沒有類型限制,任何元素都是當成object處理,如果是值類型,會有裝箱的操作 優點:讀取快 缺點:
Thumbnail
Array 在記憶體中連續分配,而且元素類型是一樣的,長度不變 優點:讀取快,可以使用座標訪問 缺點:新增、刪除慢 記憶體: 📷 範例程式碼: ArrayList 不定長度,在記憶體中連續分配的,元素沒有類型限制,任何元素都是當成object處理,如果是值類型,會有裝箱的操作 優點:讀取快 缺點:
Thumbnail
陣列是Python語言的最基礎也最容易實作的資料結構,主要可以透過兩種方式在Python上實踐陣列,其中一種是靜態結構 - 串列(List),另一種則是動態結構 - 鏈結串列(Linked List)。 我們會依序介紹這兩種作法如何在Python上執行陣列的相關功能,並比較兩種方法之間的差異。
Thumbnail
陣列是Python語言的最基礎也最容易實作的資料結構,主要可以透過兩種方式在Python上實踐陣列,其中一種是靜態結構 - 串列(List),另一種則是動態結構 - 鏈結串列(Linked List)。 我們會依序介紹這兩種作法如何在Python上執行陣列的相關功能,並比較兩種方法之間的差異。
Thumbnail
這篇我們來看一個在程式開發很常見也很常用的一個東西:Array 陣列 Array在所有的程式開發中還蠻常見的,也一定會出現,因為有很多的資料都會是一長串的,需要有一個物件來去做集中管理。
Thumbnail
這篇我們來看一個在程式開發很常見也很常用的一個東西:Array 陣列 Array在所有的程式開發中還蠻常見的,也一定會出現,因為有很多的資料都會是一長串的,需要有一個物件來去做集中管理。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News