樹演算法(1)
阿新 • • 發佈:2020-07-19
容易忘的樹基本操作
以中序與任意其他方法遍歷建二叉樹
// 中序與後續為例 struct node { int data; node *l, *r; }; // 中序的hash陣列 int hashIn[MAX]; vector<int> in(MAX), post(MAX); node *creatTree(int postR, int inR, int len) { if (len <= 0) return nullptr; node *root = new node; root->data = post[postR]; int k = hashIn[post[postR]]; int rLen = inR - k, lLen = len - 1 - rLen; root->l = creatTree(postR - 1 - rLen, k - 1, lLen); root->r = creatTree(postR - 1, inR, rLen); return root; }
BST
前驅與後繼
inline node *precursor(node *root) { node *ans = nullptr; ans = root->l; while (ans->r) { ans = ans->r; } return ans; } inline node *successor(node *root) { node *ans = nullptr; ans = root->r; while (ans->l) { ans = ans->l; } return ans; }
刪除
刪除有兩種方法:
- 找到需要刪除的節點
z
後,根據下列兩種情況進行刪除z
無孩子,直接刪除。- 若有前驅或後繼,選擇其中一個的資料覆蓋需刪除的節點,再對前驅或後繼所在的子樹遞迴刪除
- 是第前一種方法的優化方案(代價是需要節點額外存父指標),情況2時不需要進行遞迴,由於前驅無右子節點,後繼無左子節點,所以選擇其中一個的資料覆蓋需刪除的節點後,用前驅(後繼)的左子節點(右子節點)替換掉前驅(後繼)。
// 直接使用方法2,並在`z`左右子節點都存在時,交替使用前驅和後繼 inline void transPlant(node *u, node *v) { if (u->p->l == u) u->p->l = v; else u->p->r = v; if (v) v->p = u->p; } node *findNext(node *root) { node *ans = nullptr; if (root->l && root->r) { static int cnt = 0; if (cnt & 1) ans = precursor(root); else ans = successor(root); ++cnt; } else if (root->l) ans = precursor(root); else if (root->r) ans = successor(root); return ans; } void deleteNode(node *&root, int data) { if (!root) return; if (data == root->data) { if (!root->l && !root->r) { delete root; root = nullptr; } else { node *next = findNext(root); root->data = next->data; if (next->l) transPlant(next, next->l); else transPlant(next, next->r); } } if (data < root->data) deleteNode(root->l, data); else deleteNode(root->r, data); }
AVL 樹
基本
struct node {
int data = 0, height = 1;
node *l = nullptr, *r = nullptr;
};
inline int getH(node *const root) {
if (root == nullptr) return 0;
return root->height;
}
inline int getBF(node *const root) { return getH(root->l) - getH(root->r); }
inline void updataH(node *const root) {
root->height = max(getH(root->l), getH(root->r)) + 1;
}
左右旋
void leftR(node *&root) {
if (root && root->r) {
node *tmp = root->r;
root->r = tmp->l;
updataH(root);
tmp->l = root;
updataH(tmp);
root = tmp;
}
}
void rightR(node *&root) {
if (root && root->l) {
node *tmp = root->l;
root->l = tmp->r;
updataH(root);
tmp->r = root;
updataH(tmp);
root = tmp;
}
}
插入
插入四種情況:
樹型 | 判定條件 | 調整方法 |
---|---|---|
LL | BF(root) == 2 && BF(root->child[0]) == 1 |
右旋root |
LR | BF(root) == 2 && BF(root->child[0]) == -1 |
左旋root->child[0] ,右旋root |
RR | BF(root) == -2 && BF(root->child[1]) == -1 |
左旋root |
RL | BF(root) == -2 && BF(root->child[1]) == 1 |
右旋root->child[1] ,左旋root |
刪除和插入比較多時,用紅黑樹
void insertNode(node *&root, int data) {
if (root == nullptr) {
root = new node;
root->data = data;
return;
}
if (data < root->data) {
insertNode(root->l, data);
updataH(root);
if (getBF(root) == 2) {
if (getBF(root->l) == -1) leftR(root->l);
rightR(root);
}
} else {
insertNode(root->r, data);
updataH(root);
if (getBF(root) == -2) {
if (getBF(root->r) == 1) rightR(root->r);
leftR(root);
}
}
}