今天來介紹五種Tree:
1.左偏樹(Leftist Tree)
2.通用樹(Generic Trees)
3.字典樹(Trie Tree)
4.線段樹 (Segment Tree)
5.區間樹 (Interval Tree)
也稱為左堆(leftist heap)特別適用於實作優先權佇列(Priority Queue)。它的名稱來自於其特殊的性質,稱為「左偏性質」或「左傾性質」,這使得在執行合併操作時效能優於其他堆資料結構。
首先,我們需要定義 Leftist Tree 節點的結構,包括節點值、左子樹、右子樹以及左偏值。
struct LeftistNode {
int value;
LeftistNode* left;
LeftistNode* right;
int dist; // 左偏值,即左子樹的深度
LeftistNode(int val) : value(val), left(nullptr), right(nullptr), dist(0) {}
};
將一個新的節點插入到 Leftist Tree 中,通常插入操作可以簡單地視為將新節點當作一棵左偏樹,然後再進行合併操作。
操作的步驟如下:
a. 創建一個包含要插入的元素的左偏樹(可以視為只有一個節點的左偏樹)。
b. 將這個新的左偏樹與現有的 Leftist Tree 進行合併操作。
LeftistNode* insert(LeftistNode* root, int val) {
LeftistNode* newNode = new LeftistNode(val);
return merge(root, newNode);
}
從 Leftist Tree 中刪除並返回具有最小優先級值的節點。
操作的步驟如下:
a. 找到具有最小優先級值的根節點,這通常是 Leftist Tree 的根節點。
b. 刪除這個根節點。
c. 合併根節點的左子樹和右子樹,得到新的 Leftist Tree。
d. 返回被刪除的節點作為最小值。
LeftistNode* deleteMin(LeftistNode* root) {
if (!root) return nullptr;
LeftistNode* leftChild = root->left;
LeftistNode* rightChild = root->right;
delete root;
return merge(leftChild, rightChild);
}
合併操作是 Leftist Tree 最重要的操作之一,它用於將兩個 Leftist Trees 合併為一個新的 Leftist Tree。
操作的步驟如下:
a. 比較兩個 Leftist Trees 的根節點,將具有較小優先級值的節點設為新樹的根節點。
b. 將根節點的左子樹和右子樹分別合併,得到新的左子樹和右子樹。
c. 遞迴執行合併操作,直到確保左偏性質得以滿足。
d. 返回新的合併後的 Leftist Tree。
LeftistNode* merge(LeftistNode* a, LeftistNode* b) {
if (!a) return b;
if (!b) return a;
// 確保 a 的值小於 b,如果不是,則交換 a 和 b
if (a->value > b->value) {
swap(a, b);
}
// 遞歸地合併 a 的右子樹和 b
a->right = merge(a->right, b);
// 更新左偏值,確保左子樹的左偏值不大於右子樹
if (!a->left || a->left->dist < a->right->dist) {
swap(a->left, a->right);
}
// 更新左偏值
a->dist = (a->right ? a->right->dist + 1 : 0);
return a;
}
總結來說,Leftist Tree 在合併操作和提取最小值操作上表現出色,特別適用於需要高效處理優先級佇列的應用。然而,它的效能在其他操作上可能較差,並且不適用於所有應用情境。選擇使用 Leftist Tree 還是其他資料結構應取決於特定問題的需求和性能要求。
又稱N元樹(N-ary Tree),是一種樹狀資料結構,與二元樹不同,每個節點可以擁有多個子節點,而不僅僅是兩個。在N-ary Tree中,每個節點最多可以有N個子節點,其中N可以是任意正整數。這種資料結構具有多個有趣的特點和廣泛的應用。
使用動態數組來儲存孩子的地址。我們可以隨機存取任何孩子的地址,向量的大小也不固定。
class TreeNode {
public:
int val; // 節點的數值
std::vector<TreeNode*> children; // 子節點的指針陣列
TreeNode(int value) : val(value) {}
};
以下是一些常見的N-ary Tree的應用場景:
如果需要表示多層次結構的資料,N-ary Tree可能更適合。如果需要高效的查找操作和平衡性,則可能更適合使用Binary Tree,尤其是平衡二叉樹實現。
也被稱為字典樹(Dictionary Tree)或前綴樹(Prefix Tree),是一種獨特的樹狀資料結構,專門用於高效地儲存、檢索和查找以字串形式表示的資訊。它的名稱"trie"源自英文單字"retrieval"的倒寫,強調了它在字串搜尋方面的優越性。
字典樹(Trie)是一種特殊的樹狀數據結構,有時也稱為單詞搜尋樹,類似於一本強大的字典,能夠迅速地插入、查找和處理字符串數據,尤其在處理大量字符串的情況下表現出色。
Trie最引人注目的特點在於它精妙地利用了字串的特性,將每個字串的共同前綴作為儲存和檢索的基礎,這樣可以節省儲存空間並加速檢索操作。 Trie以樹狀結構表示,每個節點(Node)代表一個字母(或字母的一部分)。整棵樹的高度取決於最長的字串的長度,再加上根節點。
當我們從根節點開始沿著樹的分支向下遍歷時,每個節點都代表了一個可能的字串。不同於一般的二元樹,Trie不僅限於葉子節點才包含字符串信息,每個節點都可以表示一個或多個字符串的共同前綴,這使得我們既能節省內存,又能迅速查找字符串。
Trie Tree的優勢體現在前綴匹配方面,它能夠迅速檢索帶有特定前綴的字串,這對於字典、自動完成、拼字檢查以及相似字串的查找非常重要。在Trie中,每個節點最多有字母表大小的子節點,這使得它非常適合處理字串資料。
Trie Tree是一種強大且高效的資料結構,特別適用於處理字串資料。它在字典、搜尋建議、自動完成、拼字檢查以及其他與字串相關的應用領域中具有廣泛的應用。需要注意的是,雖然Trie Tree非常強大,但在某些情況下可能會佔用較多的記憶體。為了優化記憶體使用,可以使用一些技巧,如壓縮Trie或Radix Tree等。
1.高效的字串檢索:Trie Tree 以其出色的前綴匹配能力而著稱,能夠快速查找帶有特定前綴的字串,適用於字典、搜尋建議和拼字檢查等應用程式。
2.儲存共同前綴:Trie Tree 利用字串的共同前綴來儲存數據,這有助於節省儲存空間,因為共同前綴只需儲存一次。
3.多元樹結構:Trie Tree 是一種多元樹,每個節點可以有多個子節點,代表不同的字元或字元的一部分。
4.根節點:Trie Tree 的根節點代表空字串或空前綴,是整棵樹的起點。
5.高度:Trie Tree 的高度取決於最長的字串的長度,這有助於快速找到和插入。
6.高效率的插入和刪除操作:將字串插入 Trie Tree 是相對簡單的操作,刪除操作也可以執行,特別是需要刪除不再需要的字串時。
首先,定義 Trie Tree 節點結構,通常包括子節點指針數組、是否為結束節點的標記等。
struct TrieNode {
TrieNode* children[26]; // 子節點,假定處理小寫字母
bool isEndOfWord; // 是否是字串結尾
TrieNode() {
memset(children, 0, sizeof(children));
isEndOfWord = false;
}
};
插入操作用於將一個字串插入 Trie Tree 中。我們遍歷字串的每個字符,從 Trie Tree 的根節點開始,依次插入字符對應的節點。如果某個節點不存在,則創建一個新的節點。最後,在字串的最後一個字符節點上設置結尾標誌。
void insert(TrieNode* root, string word) {
TrieNode* node = root;
for (char ch : word) {
int index = ch - 'a'; // 假定處理小寫字母
if (!node->children[index]) {
node->children[index] = new TrieNode();
}
node = node->children[index];
}
node->isEndOfWord = true;
}
從根節點開始,依照字串的字元順序依序遍歷Trie。如果在遍歷過程中遇到不存在的字元節點,或是最後一個字元節點沒有標記字串結束的標誌,就表示字串不存在於Trie中。
bool search(TrieNode* root, string word) {
TrieNode* node = root;
for (char ch : word) {
int index = ch - 'a'; // 假定處理小寫字母
if (!node->children[index]) {
return false;
}
node = node->children[index];
}
return node->isEndOfWord;
}
首先,需要執行查找操作以確定字串是否存在於Trie中。然後,從根節點開始,按照字串的字元順序依次遍歷Trie,找到字串的最後一個字元節點,並將其結束標誌清除。如果這個節點沒有其他子節點,可以將其從Trie中刪除。接著,遞歸地檢查父節點,如果發現沒有其他子節點,也可以刪除父節點,以此類推,直到不再有可以刪除的節點。
bool deleteWord(TrieNode* root, string word, int depth) {
if (!root) {
return false;
}
if (depth == word.length()) {
if (root->isEndOfWord) {
root->isEndOfWord = false;
return true;
}
return false;
}
int index = word[depth] - 'a'; // 假定處理小寫字母
bool canDelete = deleteWord(root->children[index], word, depth + 1);
if (canDelete) {
delete root->children[index];
root->children[index] = nullptr;
return !root->isEndOfWord && isEmptyNode(root);
}
return false;
}
bool isEmptyNode(TrieNode* node) {
for (int i = 0; i < 26; ++i) {
if (node->children[i]) {
return false;
}
}
return true;
}
首先,我們需要確定 Trie Tree 中是否存在以特定前綴為起點的子樹。這可以通過查詢 Trie Tree 中是否存在該前綴的最後一個字符所對應的節點來實現。如果該節點存在,則表示該前綴存在於 Trie Tree 中。
bool isPrefixExists(TrieNode* root, string prefix) {
TrieNode* node = root;
for (char ch : prefix) {
int index = ch - 'a'; // 假定處理小寫字母
if (!node->children[index]) {
return false; // 前綴不存在
}
node = node->children[index];
}
return true; // 前綴存在
}
void findAllWithPrefix(TrieNode* node, string currentWord, vector<string>& result) {
if (!node) {
return;
}
if (node->isEndOfWord) {
result.push_back(currentWord);
}
for (int i = 0; i < 26; ++i) { // 假定處理小寫字母
if (node->children[i]) {
char ch = 'a' + i;
findAllWithPrefix(node->children[i], currentWord + ch, result);
}
}
}
vector<string> findAllWordsWithPrefix(TrieNode* root, string prefix) {
vector<string> result;
TrieNode* node = root;
for (char ch : prefix) {
int index = ch - 'a'; // 假定處理小寫字母
if (!node->children[index]) {
return result; // 前綴不存在,返回空列表
}
node = node->children[index];
}
// 遍歷以前綴為起點的所有字串
findAllWithPrefix(node, prefix, result);
return result;
}
遍歷操作可以用於取得Trie中儲存的所有字串。通常使用深度優先搜尋或廣度優先搜尋來遍歷Trie,以檢索或列舉所有字串。
Trie是一種強大的資料結構,特別適用於處理大量字串資料和實作字串搜尋相關的功能。它常用於搜尋引擎、拼字檢查、自動完成、字典、路由表和許多其他應用中。理解Trie的基本原理和操作可以幫助解決與字串相關的問題。
特性 | Trie Tree | N-ary Tree |
---|---|---|
結構 | 每個節點可以有多個子節點 | 每個節點可以有多個子節點 |
用途 | 主要用於高效儲存和檢索字串數據 | 通常用於儲存和檢索各種類型的數據,不僅限於字串數據 |
節點分支數 | 每個節點最多有字母表大小的子節點(通常為 26) | 子節點的數量可以根據需要靈活定義 |
根節點 | 通常代表空字串或空前綴 | 代表樹的起始點,可以包含與資料相關的資訊 |
高度 | 取決於最長字串的長度 | 取決於樹的結構和深度 |
尋找效率 | 適用於快速尋找帶有特定前綴的字串 | 尋找效率取決於樹的平衡性和子節點的數量 |
插入和刪除操作 | 對容易插入和刪除字串 | 插入和刪除操作的複雜性取決於樹的結構和規則 |
主要應用領域 | 字典、搜尋建議、拼字檢查等與字串相關的應用 | 資料庫、作業系統、檔案系統等各種應用領域 |
需要注意的是,Trie Tree 主要用於處理字串數據,特別擅長在字典、搜尋建議和拼字檢查等應用程式中,而 N-ary Tree 可以更通用地用於多種資料類型和領域。兩者都是多元樹結構,但 Trie Tree 通常在節點分支數和字串處理方面有一些特殊化的優勢。在選擇使用哪種資料結構時,應根據特定的應用需求和資料類型來決定。
三元搜尋樹是一種特殊的字典樹
三元搜尋樹的表示:
與 trie(標準)資料結構的每個節點包含 26 個子節點指標不同,三元搜尋樹中的每個節點僅包含 3 個指標:
Ternary Search Tree的每個節點代表一個字元,不同於BST的二元性質。每個節點都有三個子節點,通常稱為"左"、"中"和"右"子節點,代表當前字元小於、等於和大於節點所代表的字元情況。這種三分支的結構有助於有效地處理字串相關操作。
// 定義 Ternary Search Tree 的節點結構
struct TernaryNode {
char data; // 節點的字元值
bool isEndOfWord; // 標記是否為字串結尾
TernaryNode* left; // 左子樹(小於當前字元的節點)
TernaryNode* middle; // 中間子樹(等於當前字元的節點)
TernaryNode* right; // 右子樹(大於當前字元的節點)
TernaryNode(char c) {
data = c;
isEndOfWord = false;
left = middle = right = nullptr;
}
};
Ternary Search Tree通常具有自平衡性質,確保樹的高度相對較小,這有助於保持高效的查找性能。在插入新的字串時,樹會自動調整以維護平衡。
相對於其他資料結構如哈希表,Ternary Search Tree具有更好的記憶體效率,因為它不需要為每個鍵分配記憶體,而是利用樹的結構儲存字串。
插入新的字串是一個遞歸的過程,我們從樹的根節點開始,根據當前字元的大小關係,選擇向左、中間或右子樹分支。如果節點不存在,則創建一個新節點。在插入過程中,我們不僅要插入字元,還要設置isEnd標誌以指示字串的結尾。
TSTNode* insert(TSTNode* root, const char* word) {
char currentChar = *word;
if (!root) {
root = new TSTNode;
root->data = currentChar;
}
if (currentChar < root->data) {
root->left = insert(root->left, word);
} else if (currentChar > root->data) {
root->right = insert(root->right, word);
} else {
if (*word == '\0') {
root->isEnd = true; // 標記字串結尾
} else {
root->mid = insert(root->mid, word + 1);
}
}
return root;
}
搜尋操作也是一個遞歸過程,我們根據當前字元的大小關係,選擇向左、中間或右子樹分支,直到找到完全匹配的字串或查詢不到匹配的字串。
bool search(TSTNode* root, const char* word) {
char currentChar = *word;
if (!root) {
return false;
}
if (currentChar < root->data) {
return search(root->left, word);
} else if (currentChar > root->data) {
return search(root->right, word);
} else {
if (*word == '\0' && root->isEnd) {
return true; // 找到完全匹配的字串
} else {
return search(root->mid, word + 1);
}
}
}
沿著"中"子樹移動,直到找到前綴匹配的位置,然後繼續搜索子樹。
void prefixSearch(TSTNode* root, const char* prefix, std::string currentWord) {
if (!root) {
return;
}
char currentChar = *prefix;
if (currentChar < root->data) {
prefixSearch(root->left, prefix, currentWord);
} else if (currentChar > root->data) {
prefixSearch(root->right, prefix, currentWord);
} else {
if (*prefix == '\0') {
// 找到前綴匹配,繼續搜索子樹
traverse(root->mid, currentWord + root->data);
} else {
prefixSearch(root->mid, prefix + 1, currentWord + root->data);
}
}
}
總的來說,Ternary Search Tree是一個強大的字串相關操作工具,特別適用於需要快速查詢、前綴搜尋和模糊搜尋的情況。然而,它的空間需求較高,且不適用於所有類型的資料,因此在選擇使用它時需要根據應用需求來考慮其優缺點。
線段樹(Segment Tree 或 Seg Tree)是一種獨特的二叉搜索樹,它專門用於儲存和處理關於「線段」或「區間」的性質。儘管這兩個術語有時會與另一種資料結構,即區間樹(Interval Tree)混淆使用,但線段樹和區間樹在本質上有重要區別。線段樹的主要應用包括查詢某區間被多少個線段覆蓋、計算區間內的極值等,並且能夠高效地維護這些性質,即使在資料集經常更動的情況下。
線段樹的核心概念是「分割」。普通的二叉搜索樹(BST)根據數據值進行分割,而線段樹則專注於處理線段。它能夠有效地分割和合併線段,使其能夠執行一些普通 BST 難以實現的操作。此外,線段樹的時間和空間複雜度也與普通 BST 不同。
Segment Tree(線段樹)是一種用於高效處理區間查詢問題的資料結構,主要應用於以下情境:
區間查詢:尋找給定區間內的某個數值或計算區間內的某個數值屬性。
單點更新:對資料集中的一個元素的值進行更新。
線段樹(Segment Tree)具有以下關鍵特性:
線段樹(Segment Tree)的結構特徵如下:
struct SegmentTreeNode {
int start, end;
int sum; // 存儲區間的總和,可以根據需要更改為其他操作
SegmentTreeNode* left;
SegmentTreeNode* right;
SegmentTreeNode(int s, int e) : start(s), end(e), sum(0), left(nullptr), right(nullptr) {}
};
// 建立線段樹
void build(int node, int left, int right) {
if (left == right) {
// 叶子节点,将原始数组的值存储到线段树中
segmentTree[node] = arr[left];
} else {
int mid = (left + right) / 2;
// 递归构建左子树和右子树
build(2 * node, left, mid);
build(2 * node + 1, mid + 1, right);
// 在此处合并左右子树的信息(例如,求和、找最大值等)
segmentTree[node] = merge(segmentTree[2 * node], segmentTree[2 * node + 1]);
}
}
// 查詢操作
int query(int node, int left, int right, int L, int R) {
if (R < left || L > right) {
// 待查詢區間與當前節點區間無交集
return 0; // 或其他適當的初始值,視具體問題而定
} else if (L <= left && right <= R) {
// 當前節點區間完全包含在待查詢區間內
return segmentTree[node];
} else {
int mid = (left + right) / 2;
// 否則,分別查詢左子樹和右子樹
int leftResult = query(2 * node, left, mid, L, R);
int rightResult = query(2 * node + 1, mid + 1, right, L, R);
// 在此處合併左右子樹的結果(例如,求和、找最大值等)
return merge(leftResult, rightResult);
}
}
若要更新原始陣列中的某個元素,請先在線段樹中找到對應的葉子節點。
更新葉子節點的值,並逐級向上更新父節點,以確保線段樹的資訊仍然有效。
// 更新操作
void update(int node, int left, int right, int index, int newValue) {
if (left == right) {
// 找到叶子节点,更新其值
segmentTree[node] = newValue;
} else {
int mid = (left + right) / 2;
if (index <= mid) {
// 如果要更新的元素在左子树中
update(2 * node, left, mid, index, newValue);
} else {
// 如果要更新的元素在右子树中
update(2 * node + 1, mid + 1, right, index, newValue);
}
// 在此處合併左右子樹的信息(例如,求和、找最大值等)
segmentTree[node] = merge(segmentTree[2 * node], segmentTree[2 * node + 1]);
}
}
Segment Tree 是一個強大的資料結構,適用於解決多種區間查詢問題。但它也有一些限制,特別是在記憶體佔用和資料集頻繁變化的情況下。在選擇使用 Segment Tree 時,需要權衡其優點和缺點,並考慮問題的特定要求。
Interval Tree(區間樹)是一種用於處理區間重疊和查詢相關問題的樹狀資料結構。它主要用於儲存和檢索一維區間的資料,並提供有效的方法來解決許多與區間有關的問題,包括區間查詢、重疊區間檢測、日程安排和時間表問題等。
首先,我們需要定義Interval Tree的結構。通常,每個節點都包含以下信息:
struct Interval {
int low; // 區間的左端點
int high; // 區間的右端點
// 其他與區間相關的資訊
};
struct IntervalTreeNode {
Interval interval; // 節點所代表的區間
int maxHigh; // 子樹中的最大右端點
IntervalTreeNode* left;
IntervalTreeNode* right;
};
建立Interval Tree的過程通常是遞迴的。我們可以按照以下步驟來建立:
IntervalTreeNode* buildIntervalTree(vector<Interval>& intervals) {
if (intervals.empty()) {
return nullptr;
}
// 找到區間中點
int mid = intervals.size() / 2;
// 根據中點建立根節點
IntervalTreeNode* root = new IntervalTreeNode;
root->interval = intervals[mid];
// 分割區間,遞迴建立左子樹和右子樹
vector<Interval> leftIntervals(intervals.begin(), intervals.begin() + mid);
vector<Interval> rightIntervals(intervals.begin() + mid + 1, intervals.end());
root->left = buildIntervalTree(leftIntervals);
root->right = buildIntervalTree(rightIntervals);
// 更新子樹中的最大右端點
root->maxHigh = max(root->interval.high, max(root->left ? root->left->maxHigh : INT_MIN, root->right ? root->right->maxHigh : INT_MIN));
return root;
}
Interval Tree的主要功能是執行區間查詢。給定一個查詢區間,Interval Tree可以有效地找到與該區間重疊的所有區間。這可以幫助解決許多與時間區間、日程安排和資料篩選相關的問題。
vector<Interval> searchIntervalTree(IntervalTreeNode* root, Interval& queryInterval) {
vector<Interval> result;
if (root == nullptr) {
return result;
}
// 如果查詢區間與根節點的區間重疊,則將根節點加入結果
if (doOverlap(root->interval, queryInterval)) {
result.push_back(root->interval);
}
// 如果左子樹中的最大右端點大於查詢區間的左端點,則遞迴查詢左子樹
if (root->left && root->left->maxHigh >= queryInterval.low) {
vector<Interval> leftResult = searchIntervalTree(root->left, queryInterval);
result.insert(result.end(), leftResult.begin(), leftResult.end());
}
// 遞迴查詢右子樹
vector<Interval> rightResult = searchIntervalTree(root->right, queryInterval);
result.insert(result.end(), rightResult.begin(), rightResult.end());
return result;
}
插入操作用於將一個新的區間插入到Interval Tree中,並保持樹的平衡性。以下是插入操作的步驟解釋:
// 插入操作
Node* insert(Node* root, Interval new_interval) {
if (root == nullptr) {
return new Node(new_interval);
}
// 更新子樹的最大結束點
if (new_interval.end > root->max_end) {
root->max_end = new_interval.end;
}
// 遞迴插入左子樹或右子樹
if (new_interval.start < root->interval.start) {
root->left = insert(root->left, new_interval);
} else {
root->right = insert(root->right, new_interval);
}
return root;
}
刪除操作允許您從Interval Tree中移除一個指定的區間
IntervalTreeNode* deleteInterval(IntervalTreeNode* root, Interval target) {
if (root == nullptr) {
return root;
}
// 遞迴地搜尋要刪除的區間
if (target.low < root->interval.low) {
root->left = deleteInterval(root->left, target);
} else if (target.low > root->interval.low) {
root->right = deleteInterval(root->right, target);
} else {
// 找到要刪除的區間
if (root->left == nullptr) {
IntervalTreeNode* temp = root->right;
delete root;
return temp;
} else if (root->right == nullptr) {
IntervalTreeNode* temp = root->left;
delete root;
return temp;
}
// 如果有兩個子節點,找到右子樹的最小節點
IntervalTreeNode* minValueNode = root->right;
while (minValueNode->left != nullptr) {
minValueNode = minValueNode->left;
}
// 將右子樹的最小節點複製到當前節點
root->interval = minValueNode->interval;
// 刪除右子樹的最小節點
root->right = deleteInterval(root->right, minValueNode->interval);
}
// 更新maxHigh
if (root != nullptr) {
root->maxHigh = max(root->interval.high, max(
(root->left != nullptr) ? root->left->maxHigh : INT_MIN,
(root->right != nullptr) ? root->right->maxHigh : INT_MIN
));
}
return root;
}
Interval Tree可用於檢測新插入的區間是否與現有區間重疊。這可以避免插入重複的區間。
// 檢查兩個區間是否重疊
bool isOverlap(Interval a, Interval b) {
return (a.low <= b.high && b.low <= a.high);
}
// 在Interval Tree中查找重疊的區間
void findOverlap(IntervalTreeNode* root, Interval interval) {
if (root == nullptr)
return;
// 檢查根節點是否重疊
if (isOverlap(root->interval, interval)) {
std::cout << "重疊區間:[" << root->interval.low << ", " << root->interval.high << "]" << std::endl;
}
// 如果左子樹存在且其最大右端點大於查詢區間的左端點,則可能有重疊
if (root->left != nullptr && root->left->maxHigh >= interval.low) {
findOverlap(root->left, interval);
}
// 同理,如果右子樹存在且其最小左端點小於查詢區間的右端點,則可能有重疊
if (root->right != nullptr && root->right->interval.low <= interval.high) {
findOverlap(root->right, interval);
}
}
總結來說,Interval Tree是一個強大的資料結構,特別適用於解決一維區間查詢和相關問題。儘管它有一些實作上的複雜性和記憶體使用的問題,但在適當的應用場景中,它可以提供高效的解決方案。
不要輕言放棄,多堅持一下,也許你會發現更美麗的世界。(又要面臨連假的考驗了,加油!