深入理解鏈表(Linked List)與資料結構 - 以 JavaScript 實作

閱讀時間約 25 分鐘


會開始寫有關演算法的文章,一部分是知道自己在演算法沒有這麼熟悉,另一個部分,則是發現現在面試考演算法的比例變高了。因此也下定決心,在這個過程慢慢地把演算法補齊。今天要介紹的資料結構便是——鏈表(Linked List)。


如何理解 Linked List 與 Array 的差異?

與常見的數組(Array)不同,我們可以用書櫃和貨櫃車的貨櫃來比喻。想像數組就像是一個書櫃,其中的書籍排列整齊,每本書都在自己的位置上。當你需要找一本書時,只要知道它的位置,就可以直接拿到它。這就像是在 Array 中,可以直接透過 index訪問元素。

但當你需要在書櫃中放入或拿出一本書時,可能需要移動其他書籍來為新書騰出空間,或移動被拿出的書籍所留下的空白,這就像 Array 在插入或刪除元素時,需要移動其他元素一樣。

鏈表更像是一列貨櫃車上的貨櫃。每個貨櫃都連接到下一個,但它們不一定需要排列在一條直線上。當你想增加或移除一個貨櫃時,只需要解開它與前後貨櫃的連接,然後將它移出或加入即可。這就像在鏈表中添加或刪除節點那樣,不需要移動其他節點,只需要更改一些鏈接即可。

不過,如果你需要找到特定順位的貨櫃,你就會需要從頭開始計算,直到找到目標為止,這也說明了 Linked List 在搜尋上的特性。


這一篇文章有什麼?

本文將深入探討鏈表的核心概念,並使用 JavaScript 來說明如何實現和操作鏈表。我們將一步一步說明鏈表的六大方法:

  1. append(添加到末尾)
  2. prepend(添加到開頭)
  3. insert(插入元素至特定位置)
  4. remove(刪除元素)
  5. find(查找元素)
  6. reverse(反轉鏈表)

每個方法都將通過詳細的程式碼和解釋,讓您不僅學會如何使用這些方法,還能深入理解其背後的原理。接著,就讓我們開始吧!


鏈表(Linked List)的核心概念

鏈表(Linked List)是一種數據結構,它由一系列節點(Node)組成,每個節點包含數據和指向下一節點(next)的連接。也是因為結構的特性,使得元素的插入和刪除等操作,可以在任何位置有效率地進行,不同於需要連續記憶體空間的數組(Array)。

例如 React 的 hooks 儲存,就是使用 Linked List 的結構進行儲存,這也是為什麼 hook 不能被放在條件式之中的主因。但礙於本篇的主題,之後有機會再開一篇。


為何 Linked List 插入與刪除優於 Array?

鏈表與數組的主要區別在於數據存儲和訪問方式。數組是連續存儲的,這意味著它們可以快速訪問任何位置的元素(時間複雜度 O(1) ),但在中間插入或刪除元素時可能效率較低。相反,鏈表在插入和刪除操作上更有優勢,但對於隨機訪問與搜尋來說效率較低。


常見的單鏈表與雙鏈表

鏈表可以分為兩類:單鏈表(Singly Linked List)和雙鏈表(Doubly Linked List)。單鏈表的節點僅包含指向下一節點(next)的鏈接,而雙鏈表的節點則包含兩個鏈接,分別指向前一個(prev)和下一個節點(next)。

  1. 單鏈表:在這種類型的鏈表中,每個節點只含有指向下一節點的鏈接。這意味著它們只能單向遍歷。單鏈表結構簡單,使用的記憶體較少,但在某些操作中,如反向遍歷或查找前節點時效率會很差。
  2. 雙鏈表:與單鏈表不同,雙鏈表的節點不僅有指向下一節點的鏈接,還有指向前一節點的鏈接。這種結構使得雙向遍歷成為可能,提高了某些操作的效率,例如反向遍歷和在特定位置插入或刪除節點。然而,這種靈活性是以增加額外內存為代價,因為每個節點需要額外的記憶體空間,來存儲前驅節點的引用。

因此,在選擇使用哪種類型的鏈表時,就會需要思考何種限制與需求需要被優先滿足。



實現鏈表的基礎結構

在 JavaScript 中實現一個鏈表,最重要的有兩個核心元素:

  1. 節點類(Node class)
  2. 鏈表類(LinkedList class)

首先需要創建一個節點類(Node class),用於表示鏈表中的每個節點。隨後,我們將建立一個鏈表類(LinkedList class),用於管理這些節點並提供鏈表操作的方法。


節點類(Node class)的實現

節點類通常包含兩個屬性:value(用於存儲數據)和 next(用於指向鏈表中的下一個節點)。以下會各自實現單向列表(Singly Linked List)與雙向列表(Doubly Linked List)。兩者唯一的差異,就在於每一個節點,需要多一個 prev 來記錄前一個 Node 的指向。

// Singly Node
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}

// Doubly Node
class DoublyNode {
constructor(value) {
this.value = value;
this.prev = null;
this.next = null;
}
}

鏈表類(Linked List class)的初始結構

鏈表類則負責組織節點(Node),並提供各種操作鏈表的方法。在這個基本結構中,head 屬性指向鏈表的第一個節點,且因為尚未有任何節點,因此 tail 也指向 head 的 null。而 size 屬性則用於追蹤鏈表中的節點總數。

class LinkedList {
constructor() {
this.head = null;
this.tail = this.head;
this.size = 0;
}
}



如何實作 Linked List 常見的方法

接下來,筆者將六個常見的方法「append」、「prepend」、「insert」、「remove」、「find」和「reverse」等鏈表操作,以實際的程式碼逐行說明。但也要提醒大家,實際上不只有一種實現方式,這篇文章的只是其中一種實現方式,提供給大家參考與對照。


append - 在鏈表尾部添加元素

append 這個方法,主要用於在鏈表的末尾添加一個新節點。這個操作首先檢查是否存在頭節點(head)。如果沒有,新節點將成為頭節點。

接著,因為我們有設定 tail 來直接找到最後一個節點,不然就需要從頭遍歷鏈表,找到最後一個節點,並將其 next 指針指向新節點。

而雙向鏈表需要額外做的步驟,在於將新節點的 prev 與原先 tail 的節點相連,讓我們可以從新的節點找到前一個的節點。

// Singly Linked List
class LinkedList {
append(item) {
const newNode = new Node(item);
if (this.head === null) { // 如果為空鏈表
this.head = newNode; // 將 newNode 新增至 head
this.tail = this.head; // 將 newNode 新增至 tail
} else {
this.tail.next = newNode; // 將 newNode 接在 tail 的後方
this.tail = newNode; // 將 tail 換成 newNode
}
this.size++;
return this; // 方便鏈式調用
}
}

// Doubly Linked List
class DoublyLinkedList {
append(item) {
const newNode = new Node(item);
if (this.head === null) {
this.head = newNode;
this.tail = this.head;
} else {
newNode.prev = this.tail; // 將 newNode 的前節點設定為原先的 tail 節點
this.tail.next = newNode;
this.tail = newNode;
}
this.size++;
return this;
}
}



prepend - 在鏈表頭部添加元素

prepend 方法將新節點添加到鏈表的開頭。對於單向鏈表,這涉及將新節點的 next 指向當前的頭節點,然後更新頭節點為新節點。對於雙向鏈表,還需要設置新節點的前驅節點為 null 並更新原頭節點的 prev 為新節點。

// Singly Linked List
class LinkedList {
prepend(item) {
const newNode = new Node(item);
newNode.next = this.head; // 將 newNode 的 next 指向當前的頭節點 head。
this.head = newNode; // 更新 head 為 newNode。
return this;
}
}

// Doubly Linked List
class DoublyLinkedList {
prepend(item) {
const newNode = new DoublyNode(item);
if (!this.head) { // 如果鏈表為空,則將 newNode 設為頭節點和尾節點。
this.head = newNode;
this.tail = newNode;
} else {
newNode.next = this.head; // 將 newNode.next 指向當前的頭節點
this.head.prev = newNode; //將原頭節點的 prev 指向 newNode
this.head = newNode; // 更新 head 為 newNode
}
return this;
}
}



insert - 從鏈表的特定位置插入元素

在單向鏈表中,這需要遍歷鏈表到特定的 index 元素後,再更新前一節點的 next 指針以指向插入的新節點。並將新節點的 next 與原先的下一個節點連結在一起。而在雙向鏈表中,除了要更新前一個節點的 next 指針,也要更新插入節點的 prev ,以及下一個的 prev 指針。

// Singly Linked List
class LinkedList {
insert(item, index) {
if (index < 0 || index >= this.size) return null;
if (index === 0) return this.prepend(item);
if (index === this.size - 1) this.append(item);

const newNode = new Node(item);
let current = this.head;

for (let i = 0; i < index; i++) { // 找到要插入的前一個節點
current = current.next;
}

newNode.next = current.next; // 將 newNode 的 next 指向當前的頭節點 head。
current.next = newNode; // 更新 head 為 newNode。
this.size++; // 更新長度
return this;
}
}

// Doubly Linked List
class DoublyLinkedList {
insert(item, index) {
if (index < 0 || index >= this.size) return null;
if (index === 0) return this.prepend(item);
if (index === this.size - 1) this.append(item);

const newNode = new Node(item);
let current = this.head;
let previous = null;

for (let i = 0; i < index; i++) { // 找到插入的當前節點
previous = current;
current = current.next;
}

newNode.next = current; // 將 newNode 的 next 指向當前節點。
newNode.prev = previous; // 將新節點的 prev 接上上一個節點
current.prev = newNode; // 將當前節點的 prev 接上 newNode
previous.next = newNode;

this.size++; // 更新長度
return this;
}
}



remove - 從鏈表中刪除給定 index 元素

remove 方法用於從鏈表中刪除元素。在單向鏈表中,需要遍歷整個鏈表,以找到要刪除的元素 index,並更新前一節點的 next 指針以刪除目標節點。而在雙向鏈表中,除了更新前一節點的 next 指針,還需要更新後一節點的 prev 指針。

// Singly Linked List
class LinkedList {
remove(index) {
if (!this.head) return null; // 如果鏈表為空,直接返回
if (index < 0 || index >= this.size) return null; // index 為負數或超出範圍

// 如果頭節點是要刪除的節點
if (index === 0) {
const removedNode = this.head;
this.head = this.head.next; // 更新 head 節點
this.size--;
return removedNode;
}

let current = this.head; // 記錄當前的 Node
let previous = null; // 記錄上一個節點
let counter = 0; // 記錄遍歷到第幾次

// 找到 index 的當前 Node,且確認該節點不為空(超出範圍)​
while (counter < index && current !== null) {
[previous, current] = [current, current.next];
counter++;
}

// 如果確定當前節點不為空​,則將上一個節點的 next 接在 current 的下一個節點
if (current !== null) previous.next = current.next;

this.size--;
return this;
}
}

// Doubly Linked List
class DoublyLinkedList {
remove(index) {
if (!this.head) return null; // 如果鏈表為空,直接返回
if (index < 0 || index >= this.size) return null; // index 為負數

// 如果頭節點是要刪除的節點
if (index === 0) {
const removedNode = this.head;
this.head = this.head.next; // 更新頭節點

if (this.head) this.head.prev = null; // 如果新的頭節點存在,更新其 prev 指針
else this.tail = null; // 如果移除後沒有 Node,則將 tail 一併設為 null

this.size--;
return removedNode;
}

let current = this.head; // 從頭開始計算
let counter = 0; // 用以計數

while (counter < index && current !== null) {
current = current.next;
counter++;
}

const removedNode = current; // 保存被移除的節點

// 處理目標不為最後一個節點時,將下一個節點的 prev 接上上一個節點
if (current.next) current.next.prev = current.prev;

// 如果不為第一個節點,則將前一個節點的 next 接上下一個節點
if (current.prev !== null) current.prev.next = current.next;

// current 為最後一個節點的情況,將前一個節點更新為 tail
if (current === this.tail) this.tail = current.prev;

this.size--;

return removedNode; // 如果沒有找到,返回鏈表本身
}
}



find - 在鏈表中查找元素

find 方法用於在鏈表中查找特定元素。無論是單向鏈表還是雙向鏈表,此方法的基本操作相同:從頭節點開始遍歷,直到找到匹配的元素或達到鏈表末尾。

// Singly Linked List
class LinkedList {
find(value) {
let current = this.head;
while (current) {
if (current.value === value) return current; // 找到匹配的元素,返回該節點
current = current.next;
}
return null; // 沒有找到,返回 null
}
}

// Doubly Linked List
class DoublyLinkedList {
find(value) {
let current = this.head;
while (current) {
if (current.value === value) return current; // 找到匹配的元素,返回該節點
current = current.next;
}
return null; // 沒有找到,返回 null
}
}



reverse - 反轉鏈表

reverse 方法將鏈表的元素順序反轉。在單向鏈表中,此操作需要更改每個節點的 next 指針。在雙向鏈表中,還需要更改每個節點的 prev 指針。

// Singly Linked List with iteration
class LinkedList {
reverse() {
let current = this.head;
let prev = null;
let next = null;

while (current) {
next = current.next; // 保存下一個節點
current.next = prev; // 反轉當前節點的 next 指針
prev = current; // 移動 prev 節點
current = next; // 移動到下一個節點
}

this.head = prev; // 更新頭節點
return this;
}
}

// Singly Linked List with Recursion
class LinkedList {
reverse() {
// 定義 recursion 的函式
function reverseList(head) {
// 確定當前節點或下一個節點為空,則直接返回傳入的當前節點​
if (head === null || head.next === null) return head;
const reversedList = reverseList(head.next);
head.next.next = head; // 將下個節點的 next 接上當前節點
head.next = null; // 將當前節點的 next 暫時移除連接(因為往後執行時會再接上)
return reversedList;
}

// 處理無節點,或僅有一個節點的情況​
if (this.head === null || this.head.next === null) return this;

const originHead = this.head; // 記得原先的 head
this.head = reverseList(this.head);
this.tail = originHead; // 將原本的 head 換成 tail
return this;
}
}

// Doubly Linked List
class DoublyLinkedList {
reverse() {
let current = this.head;
let temp = null;

while (current) {
// 交換 prev 和 next 指針
[current.next, current.prev] = [current.prev, current.next];
current = current.prev; // 移動到原本的下一個節點
}

if (temp) this.head = temp.prev; // 更新頭節點
return this;
}
}

其中,要注意的是遞迴(Recursion)會將欲執行的函式疊在 Stack 中,因此最後執行的函式會被優先處理。換句話說,最後執行的結果就會是終止函式,接著才從終止函式中往回執行,直到處理第一個函式。



Linked List 的應用場景

鏈表(Linked List)作為一種靈活且強大的數據結構,目前搜尋到的有以下是幾個經典應用場景:

  1. 動態數據存儲:由於鏈表可以在運行時動態地增加或減少節點,它們非常適合用於不確定大小的數據集合,例如用戶生成的內容列表或實時消息體系。
  2. 實現隊列和堆棧:鏈表非常適合實現如 Queue 和 Stack 這樣的抽象數據類型。尤其是在需要頻繁進行插入和刪除操作的場景中,Linked List 的效能會優於 Array。
  3. 圖的表示:在圖形算法中,鏈表可用來有效地表示圖的邊和節點,尤其是在實現鄰接表時。
  4. 記憶體管理:操作系統中的記憶體管理,也常使用鏈表來追蹤空閒的記憶體塊。



結語

第一次在了解與實作 Linked List 時,花了非常多時間在了解其核心的觀念,以及如何對 Linked List 的資料結構進行操作,這些都讓一開始學習 Linked List 有著一定的門檻。筆者自己也是在一點一滴的實作中,才慢慢知道如何操作 Linked List,甚至慢慢能夠寫出常見的方法。

因此這一篇文章,希望能夠給剛接觸 Linked List 的人一些切入方向,並透過文章中的逐步說明,了解 Linked List 這一個資料結構的美妙!

如果以上內容有任何錯誤或建議,都歡迎直接留言或來信交流!


Reference 參考資料

此處作為整理前端(Frontend)和相關的 HTML、CSS、JavaScript、React 等前端觀念與技巧,全部都會收錄在這個專題之中。同時也會將相關的技術與反思記錄在此,歡迎各位讀者互相交流。
留言0
查看全部
發表第一個留言支持創作者!
「px」,即像素,是最基本的單位,它常被用於指定字體大小、邊框粗細等。「em」和「rem」通常用於調整相對大小,「em」在子元素中的適用,而「rem」則以根元素為參考。另一方面,「vh」和「vw」分別代表視窗的高度和寬度百分比,特別適合實現響應式設計。「vmin」和「vmax」則根據視窗的最小或最大
在 React 測試生態系統中,React Testing Library 成為了方便好用的選擇,因其強調測試應該關注於使用者的操作與觀察元件行為,而不是測試細節實現。
在軟體領域中,"Thunk" 是一個常用的術語,它指的是一種用於延遲計算,或將運算延後執行的程式碼片段。它通常用於函數式編程,或編譯器的設計中。Redux 透過 createAsyncThunk 實作了該非同步/異步操作,並提供數個 API 協助我們使用 Redux。
React 表單驗證是一種技術與使用者體驗的設計,讓使用者能夠即時檢查輸入的資料並修正,提升使用者的使用體驗,並確保資料的正確性。
useContext 是一種 React hook,讓我們能夠直接取用其他元件的 Context,而無須層層傳遞 props,進而使程式碼簡潔易讀。
在傳統開發的過程中,很容易會搞混一般的 this 和箭頭函式(arrow function)中的 lexcial "this" 兩者的差異。本文就以實際的例子來說明各自的差異,以及在未來使用時需要注意哪一些細節。
「px」,即像素,是最基本的單位,它常被用於指定字體大小、邊框粗細等。「em」和「rem」通常用於調整相對大小,「em」在子元素中的適用,而「rem」則以根元素為參考。另一方面,「vh」和「vw」分別代表視窗的高度和寬度百分比,特別適合實現響應式設計。「vmin」和「vmax」則根據視窗的最小或最大
在 React 測試生態系統中,React Testing Library 成為了方便好用的選擇,因其強調測試應該關注於使用者的操作與觀察元件行為,而不是測試細節實現。
在軟體領域中,"Thunk" 是一個常用的術語,它指的是一種用於延遲計算,或將運算延後執行的程式碼片段。它通常用於函數式編程,或編譯器的設計中。Redux 透過 createAsyncThunk 實作了該非同步/異步操作,並提供數個 API 協助我們使用 Redux。
React 表單驗證是一種技術與使用者體驗的設計,讓使用者能夠即時檢查輸入的資料並修正,提升使用者的使用體驗,並確保資料的正確性。
useContext 是一種 React hook,讓我們能夠直接取用其他元件的 Context,而無須層層傳遞 props,進而使程式碼簡潔易讀。
在傳統開發的過程中,很容易會搞混一般的 this 和箭頭函式(arrow function)中的 lexcial "this" 兩者的差異。本文就以實際的例子來說明各自的差異,以及在未來使用時需要注意哪一些細節。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
面對老年睡眠問題,了解自然生理變化至關重要。我們的視交叉上核隨年齡老化,影響晝夜節律。學習如何適應這些變化,以及提高褪黑激素水平,是維持健康睡眠模式的關鍵。探索有效策略,保證高齡生活中的良好睡眠質量。
Thumbnail
當我們身邊有人正與憂鬱症抗爭時,我們經常困惑於如何提供幫助。許多時候,即使心懷善意,我們的舉動也可能不但沒有幫助,反而造成更多困擾。這是為什麼呢?憂鬱症不僅僅是思想上的問題,而是一個複雜的心理狀態。當我們試圖強迫一個無力前行的人走路時,最終往往導致對方跌倒。
Thumbnail
在軟體開發領域,MVC(Model-View-Controller)是一種被廣泛使用的設計模式,它有助於將應用程序的不同部分進行組織、分離,以實現更好的可維護性和可擴展性。本文將深入介紹MVC模式的核心概念,以及如何在您的項目中應用這種強大的架構。
Thumbnail
今天我們會開始深入探討到EVM(以太坊虛擬機)上的一點點基礎知識。 若您是一個常常在etherscan.io上追蹤交易的朋友,一定對上面提到的各項資訊並不陌生。但我們觀察一筆交易時會發現,etherscan.io又額外提供了Internel Txns,這又是什麼呢?本篇將一同來探討。
Thumbnail
在前幾篇的文章中。有和各位分享了使用Ganache來模擬一個以太坊主網進行交易,也嘗試使用了web3.py來開發區塊鏈互動程式。相信對您而言,一定有更深入探索區塊鏈交易的需求。本篇想和各位介紹一個Ganache的神奇功能:解鎖任意帳號
Thumbnail
在上一篇文章中,有和您分享了使用Ganache模擬了一個以太坊主網。想必您已經迫不及待的想對它進行更深入的了解了。本篇預計會使用Python來和您建立好的模擬網路進行互動。在開發套件的細節上我們並不會很深入的地為您進行解釋,而會透過一些簡單的例子來引導一個學習的方向。
Thumbnail
關於乙太坊上的交易,通常我們都會到etherscan.io這個網站來進行確認。但初入區塊鏈的朋友們往往看到裡面一大堆的資訊,除了from和to外可能都是一頭霧水。想多了解些相關知識又不知如何下手才好,因此動念寫下一些本人初學區塊鏈時可能用來切入的一些工具,來和各位同好分享。
Thumbnail
為什麼某些公司值得高本益比?而某些卻不行?低本益比就表示物超所值?而高本益比就表示價格昂貴嗎?為什麼有時候買高本益比反而賺錢,而買低本益比反而套牢呢?以下分享我們對於本益比的看法。
Thumbnail
為什麼在台灣我們感受不到權貴的存在?有沒有一本書可以快速讓人建立「到底誰是誰?誰又是誰的誰?」的輪廓?一些天篷權貴的參考資料與大家共享。
Thumbnail
咖啡萃取示意圖 Hello,大家好我是蟹老闆。經過上一篇文章的介紹,我們對於研磨度有了初步的認知。研磨度影響著咖啡粉的表面積大小。而表面積的大小又影響著甚麼呢?咖啡的"萃取"。但好與不好的萃取定義是甚麼?咖啡師或沖煮老手們在沖煮時,又怎麼去理解萃取這件事情呢?我們一起看下去吧~
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
面對老年睡眠問題,了解自然生理變化至關重要。我們的視交叉上核隨年齡老化,影響晝夜節律。學習如何適應這些變化,以及提高褪黑激素水平,是維持健康睡眠模式的關鍵。探索有效策略,保證高齡生活中的良好睡眠質量。
Thumbnail
當我們身邊有人正與憂鬱症抗爭時,我們經常困惑於如何提供幫助。許多時候,即使心懷善意,我們的舉動也可能不但沒有幫助,反而造成更多困擾。這是為什麼呢?憂鬱症不僅僅是思想上的問題,而是一個複雜的心理狀態。當我們試圖強迫一個無力前行的人走路時,最終往往導致對方跌倒。
Thumbnail
在軟體開發領域,MVC(Model-View-Controller)是一種被廣泛使用的設計模式,它有助於將應用程序的不同部分進行組織、分離,以實現更好的可維護性和可擴展性。本文將深入介紹MVC模式的核心概念,以及如何在您的項目中應用這種強大的架構。
Thumbnail
今天我們會開始深入探討到EVM(以太坊虛擬機)上的一點點基礎知識。 若您是一個常常在etherscan.io上追蹤交易的朋友,一定對上面提到的各項資訊並不陌生。但我們觀察一筆交易時會發現,etherscan.io又額外提供了Internel Txns,這又是什麼呢?本篇將一同來探討。
Thumbnail
在前幾篇的文章中。有和各位分享了使用Ganache來模擬一個以太坊主網進行交易,也嘗試使用了web3.py來開發區塊鏈互動程式。相信對您而言,一定有更深入探索區塊鏈交易的需求。本篇想和各位介紹一個Ganache的神奇功能:解鎖任意帳號
Thumbnail
在上一篇文章中,有和您分享了使用Ganache模擬了一個以太坊主網。想必您已經迫不及待的想對它進行更深入的了解了。本篇預計會使用Python來和您建立好的模擬網路進行互動。在開發套件的細節上我們並不會很深入的地為您進行解釋,而會透過一些簡單的例子來引導一個學習的方向。
Thumbnail
關於乙太坊上的交易,通常我們都會到etherscan.io這個網站來進行確認。但初入區塊鏈的朋友們往往看到裡面一大堆的資訊,除了from和to外可能都是一頭霧水。想多了解些相關知識又不知如何下手才好,因此動念寫下一些本人初學區塊鏈時可能用來切入的一些工具,來和各位同好分享。
Thumbnail
為什麼某些公司值得高本益比?而某些卻不行?低本益比就表示物超所值?而高本益比就表示價格昂貴嗎?為什麼有時候買高本益比反而賺錢,而買低本益比反而套牢呢?以下分享我們對於本益比的看法。
Thumbnail
為什麼在台灣我們感受不到權貴的存在?有沒有一本書可以快速讓人建立「到底誰是誰?誰又是誰的誰?」的輪廓?一些天篷權貴的參考資料與大家共享。
Thumbnail
咖啡萃取示意圖 Hello,大家好我是蟹老闆。經過上一篇文章的介紹,我們對於研磨度有了初步的認知。研磨度影響著咖啡粉的表面積大小。而表面積的大小又影響著甚麼呢?咖啡的"萃取"。但好與不好的萃取定義是甚麼?咖啡師或沖煮老手們在沖煮時,又怎麼去理解萃取這件事情呢?我們一起看下去吧~