給定一棵二叉樹(以擴展前序序列 `#` 表示空節點構建),要求將其線索化為雙向鏈表(中序線索化),並實現查找兩節點的最近公共祖先(LCA)。
template <typename Type>
class ThreadedTree {
struct Node {
Type data;
Node *left, *right;
bool ltag, rtag; // true=線索, false=孩子
Node(Type d=Type()) : data(d), left(nullptr), right(nullptr), ltag(false), rtag(false) {}
};
Node *root;
// 中序線索化核心遞歸
void _InThread(Node *&p, Node *&pre) {
if (!p) return;
_InThread(p->left, pre);
if (pre && !pre->right) { pre->right = p; pre->rtag = true; }
if (p && !p->left) { p->left = pre; p->ltag = true; }
pre = p;
_InThread(p->right, pre);
}
// 構建雙向鏈表(以 root 為頭節點)
void _BuildDList(Node *p) {
Node *cur = First(p); // 最左節點
root = cur;
while (cur) { cout << cur->data << ' '; cur = cur->right; }
}
Node* First(Node *p) { // 中序第一個節點
if (!p) throw Exception();
while (p->left) p = p->left;
return p;
}
};
利用線索化後的中序序列特性,結合棧模擬遞歸:
Node* _GetLCA(Node *t, Type v1, Type v2) {
stack st;
if (_Exists(t, v1) && _Exists(t, v2)) st.push(t);
while (true) {
if (_Exists(t->left, v1) && _Exists(t->left, v2)) {
t = t->left; st.push(t);
} else if (_Exists(t->right, v1) && _Exists(t->right, v2)) {
t = t->right; st.push(t);
} else {
if (!st.empty()) { t = st.top(); st.pop(); return t; }
return nullptr;
}
}
}
bool _Exists(Node *t, Type val) { // 判斷子樹是否包含值
if (!t) return false;
if (t->data == val) return true;
return _Exists(t->left, val) || _Exists(t->right, val);
}
| 技術點 | 說明 |
|---|---|
| 線索化類型 | 中序線索化,左線索指向前驅,右線索指向後繼 |
| 異常處理 | 自定義 `Exception` 類處理空指針訪問 |
| LCA 邏輯 | 從根向下,只要兩節點在同一子樹繼續深入,否則當前節點即 LCA |
| 棧的作用 | 記錄訪問路徑,回溯時取棧頂即為 LCA |
// 擴展前序:ABCD###EF##G##HI##J#K##
// 對應樹結構:
// A
// / \
// B H
// / \ / \
// C E I J
// / / \ \
// D F G K
// LCA(C, D) = C
// LCA(F, G) = E