HBLT
This commit is contained in:
6
BinaryTree/priorityQueue/maxHBLT/CMakeLists.txt
Normal file
6
BinaryTree/priorityQueue/maxHBLT/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
project(maxHblt)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
add_executable(maxHblt main.cpp)
|
191
BinaryTree/priorityQueue/maxHBLT/README.MD
Normal file
191
BinaryTree/priorityQueue/maxHBLT/README.MD
Normal file
@@ -0,0 +1,191 @@
|
||||
# 左高树合并 — 逐步图解(示例:根 50 与 根 45)
|
||||
|
||||
下面是你给出的两个左高树(Max-Leftist)的逐步合并图解。我把每一步的递归调用、比较、左右交换和 s 值(null path length,定义为:s(nullptr)=0,s(leaf)=1,节点 s = right.s + 1)都写清楚了。
|
||||
|
||||
---
|
||||
|
||||
## 初始两棵树与 s 值计算
|
||||
|
||||
**Tree A(根 50)**
|
||||
|
||||
```
|
||||
50(s=2)
|
||||
/ \
|
||||
40(s=2) 30(s=1)
|
||||
/ \
|
||||
20(s=1) 10(s=1)
|
||||
```
|
||||
|
||||
计算过程:
|
||||
|
||||
* s(20)=1, s(10)=1 → s(40)=right.s + 1 = 1 + 1 = 2
|
||||
* s(30)=1(叶子)
|
||||
* s(50)=right.s + 1 = 1 + 1 = 2
|
||||
|
||||
**Tree B(根 45)**
|
||||
|
||||
```
|
||||
45(s=2)
|
||||
/ \
|
||||
35(s=2) 25(s=1)
|
||||
/ \
|
||||
15(s=1) 5(s=1)
|
||||
```
|
||||
|
||||
计算过程:
|
||||
|
||||
* s(15)=1, s(5)=1 → s(35)=1 + 1 = 2
|
||||
* s(25)=1(叶子)
|
||||
* s(45)=right.s + 1 = 1 + 1 = 2
|
||||
|
||||
---
|
||||
|
||||
## 合并入口:`merge(50, 45)`
|
||||
|
||||
* 比较根:50 >= 45 → 保留 50 为新根。
|
||||
* 按算法把 **第二棵树(45)合并到 50 的右子树**:
|
||||
|
||||
递归调用: `merge(50,45)` → 需要计算 `50->right = merge(30, 45)`。
|
||||
|
||||
---
|
||||
|
||||
## 递归 1:`merge(30, 45)`
|
||||
|
||||
* 比较根:30 < 45 → 交换(让数值大的作为子树根),所以在实现上会把参数交换,使 `h1` 指向 45,`h2` 指向 30。
|
||||
* 结果:以 45 为当前子树根,继续将 `h2(30)` 合并到 `45->right`:
|
||||
|
||||
递归调用:`merge(45,30)` → `45->right = merge(25, 30)`(因为 45 的右子树原为 25)。
|
||||
|
||||
当前(在这一层)状态:
|
||||
|
||||
```
|
||||
(暂时) 45
|
||||
/ \
|
||||
35 25
|
||||
(将与 30 合并)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 递归 2:`merge(25, 30)`
|
||||
|
||||
* 比较根:25 < 30 → 交换 → 以 30 为根,25 为待合并树。
|
||||
* 执行 `30->right = merge(30->right, 25)`。但原来 30 没有孩子,所以 `30->right` 是 `nullptr`。
|
||||
* 所以调用变成 `merge(nullptr, 25)` → 直接返回 25。
|
||||
|
||||
所以在这一步,临时构造出:
|
||||
|
||||
```
|
||||
30
|
||||
\
|
||||
25
|
||||
```
|
||||
|
||||
接下来要维护左高性质(保证 s(left) >= s(right)):
|
||||
|
||||
* 计算 s 值:原来 30 的左子为空(left\_s = 0),右子为 25(right\_s = 1)。
|
||||
* 因为 left\_s < right\_s,所以**交换左右子树**,得到:
|
||||
|
||||
```
|
||||
30
|
||||
/
|
||||
25
|
||||
```
|
||||
|
||||
* 更新 s(30) = (right ? right.s : 0) + 1 = (nullptr ? 0 : ) + 1 = 0 + 1 = 1
|
||||
|
||||
所以 `merge(25,30)` 返回的子树为:
|
||||
|
||||
```
|
||||
30(s=1)
|
||||
/
|
||||
25(s=1)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 回到上层(恢复到 45 的层)
|
||||
|
||||
* 把返回的子树作为 `45->right`:现在 45 的左右子树为
|
||||
|
||||
```
|
||||
45
|
||||
/ \
|
||||
35(s=2) 30(s=1)
|
||||
/
|
||||
25(s=1)
|
||||
```
|
||||
|
||||
* 检查并维持左高性质:
|
||||
|
||||
* left\_s = s(35) = 2
|
||||
* right\_s = s(30) = 1
|
||||
* left\_s >= right\_s,**不必交换**。
|
||||
* 更新 s(45) = right.s + 1 = 1 + 1 = 2(保持不变)。
|
||||
|
||||
`merge(30,45)` 到此结束,返回的子树根为 45(其 s=2)。
|
||||
|
||||
---
|
||||
|
||||
## 回到最顶层(恢复到 50 的层)
|
||||
|
||||
* 将上一步得到的子树赋为 `50->right`,现在 50 的结构为:
|
||||
|
||||
```
|
||||
50
|
||||
/ \
|
||||
40(s=2) 45(s=2)
|
||||
/ \ / \
|
||||
20 10 35 30
|
||||
/\ /
|
||||
15 5 25
|
||||
```
|
||||
|
||||
(为清楚起见:35 下保留 15、5;30 下保留 25)
|
||||
|
||||
* 检查并维持左高性质:
|
||||
|
||||
* left\_s = s(40) = 2
|
||||
* right\_s = s(45) = 2
|
||||
* left\_s >= right\_s(等于也符合),所以**不交换**。
|
||||
|
||||
* 更新 s(50) = right.s + 1 = 2 + 1 = 3。
|
||||
|
||||
---
|
||||
|
||||
## 最终合并结果(图与 s 值)
|
||||
|
||||
```
|
||||
50(s=3)
|
||||
/ \
|
||||
40(s=2) 45(s=2)
|
||||
/ \ / \
|
||||
20(1)10(1)35(2) 30(1)
|
||||
/\ /
|
||||
15(1)5(1)25(1)
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
* 合并过程中确实发生了你关心的“原来右子树的位置(30)被另一棵树的节点(45/35)取代”的情况,这很正常:合并算法**优先保证堆序(根值最大)**,于是较大的根会在递归中“上移”到合适位置。
|
||||
* 同时,算法通过「递归合并到右子树」+「必要时交换左右子树」来保持左高性质。最终时间复杂度按树的右路径长度上界(均摊 O(log n))来保证。
|
||||
|
||||
---
|
||||
|
||||
## 伪代码回顾(便于对应每一步)
|
||||
|
||||
```cpp
|
||||
Node* merge(Node* h1, Node* h2) {
|
||||
if (!h1) return h2;
|
||||
if (!h2) return h1;
|
||||
if (h1->key < h2->key) swap(h1, h2); // 确保 h1 的根更大(Max-heap)
|
||||
h1->right = merge(h1->right, h2);
|
||||
// 保持左高:如果左子 s < 右子 s,则交换
|
||||
if (npl(h1->left) < npl(h1->right)) swap(h1->left, h1->right);
|
||||
h1->s = npl(h1->right) + 1;
|
||||
return h1;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
22
BinaryTree/priorityQueue/maxHBLT/binaryTreeNode.h
Normal file
22
BinaryTree/priorityQueue/maxHBLT/binaryTreeNode.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Created by PC on 25-8-6.
|
||||
//
|
||||
|
||||
#ifndef BINARYTREENODE_H
|
||||
#define BINARYTREENODE_H
|
||||
|
||||
template<class T>
|
||||
class binaryTreeNode {
|
||||
public:
|
||||
T element;
|
||||
binaryTreeNode<T>* left;
|
||||
binaryTreeNode<T>* right;
|
||||
public:
|
||||
binaryTreeNode() {left = right = nullptr;}
|
||||
explicit binaryTreeNode(const T& e) : element(e), left(nullptr), right(nullptr) {}
|
||||
binaryTreeNode(const T& e, binaryTreeNode<T>* l, binaryTreeNode<T>* r) : element(e), left(l), right(r) {}
|
||||
~binaryTreeNode() = default;
|
||||
|
||||
};
|
||||
|
||||
#endif //BINARYTREENODE_H
|
34
BinaryTree/priorityQueue/maxHBLT/main.cpp
Normal file
34
BinaryTree/priorityQueue/maxHBLT/main.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "maxHblt.h"
|
||||
|
||||
int main() {
|
||||
/*
|
||||
Tree1
|
||||
50
|
||||
/ \
|
||||
40 30
|
||||
/ \
|
||||
20 10
|
||||
Tree2
|
||||
45
|
||||
/ \
|
||||
35 25
|
||||
/ \
|
||||
15 5
|
||||
*/
|
||||
maxHblt<int> t1,t2;
|
||||
t1.push(50);t1.push(30);t1.push(40);t1.push(20);t1.push(10);
|
||||
t2.push(45);t2.push(25);t2.push(35);t2.push(15);t2.push(5);
|
||||
t1.display();
|
||||
t2.display();
|
||||
//正确的树
|
||||
t1.meld(t2);
|
||||
t1.display();
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
最后输出了
|
||||
{2, 50}, {2, 40}, {1, 30}, {1, 20}, {1, 10},
|
||||
{2, 45}, {2, 35}, {1, 25}, {1, 15}, {1, 5},
|
||||
{3, 50}, {2, 40}, {2, 45}, {1, 20}, {1, 10}, {2, 35}, {1, 30}, {1, 15}, {1, 5}, {1, 25},
|
||||
与预测一致,详情见README.MD
|
||||
*/
|
133
BinaryTree/priorityQueue/maxHBLT/maxHblt.h
Normal file
133
BinaryTree/priorityQueue/maxHBLT/maxHblt.h
Normal file
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// Created by PC on 25-8-9.
|
||||
//
|
||||
|
||||
#ifndef MAXHBLT_H
|
||||
#define MAXHBLT_H
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "binaryTreeNode.h"
|
||||
template<class T>
|
||||
class maxHblt {
|
||||
private:
|
||||
binaryTreeNode<std::pair<int, T>>* root;
|
||||
int bsize;
|
||||
std::vector<binaryTreeNode<std::pair<int, T>>*> data;
|
||||
static void meld(binaryTreeNode<std::pair<int, T>>* &x, binaryTreeNode<std::pair<int, T>>* &y);
|
||||
public:
|
||||
maxHblt() : root(nullptr), bsize(0) {}
|
||||
maxHblt(binaryTreeNode<std::pair<int, T>>* r, int b) : root(r), bsize(b) {}
|
||||
~maxHblt();
|
||||
|
||||
void push(const T& x);
|
||||
void pop();
|
||||
void meld(maxHblt &y);
|
||||
void initialize() {}
|
||||
void display();
|
||||
|
||||
T& top() {return root->element.second;}
|
||||
bool empty() {return bsize == 0;}
|
||||
int size() {return bsize;}
|
||||
void dispose(binaryTreeNode<std::pair<int, T>>* r) {
|
||||
if (r == nullptr) return;
|
||||
if (r->left != nullptr) dispose(r->left);
|
||||
if (r->right != nullptr) dispose(r->right);
|
||||
delete r;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
maxHblt<T>::~maxHblt() {
|
||||
dispose(root);
|
||||
root = nullptr;
|
||||
bsize = 0;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void maxHblt<T>::push(const T& x) {
|
||||
if (root == nullptr) {
|
||||
root = new binaryTreeNode<std::pair<int, T>>(std::make_pair(1, x));
|
||||
return;
|
||||
}
|
||||
auto tmp = new binaryTreeNode<std::pair<int, T>>(std::make_pair(1, x));
|
||||
meld(root, tmp);
|
||||
bsize++;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void maxHblt<T>::pop() {
|
||||
if (root == nullptr) {
|
||||
std::cerr << "Empty Error!" << std::endl;
|
||||
}
|
||||
auto left = root->left;
|
||||
auto right = root->right;
|
||||
delete root;
|
||||
root = nullptr;
|
||||
meld(left, right);
|
||||
bsize--;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void maxHblt<T>::meld(maxHblt &y) {
|
||||
meld(root, y.root);
|
||||
bsize += y.bsize;
|
||||
y.dispose(y.root);
|
||||
y.root = nullptr;
|
||||
y.bsize = 0;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void maxHblt<T>::display() {
|
||||
if (root == nullptr) {
|
||||
std::cerr << "Empty Error!" << std::endl;
|
||||
return;
|
||||
}
|
||||
data.clear();
|
||||
data.push_back(root);
|
||||
int cnt = 0;
|
||||
while (cnt < bsize) {
|
||||
if (data.at(cnt)->left != nullptr) data.push_back(data.at(cnt)->left);
|
||||
if (data.at(cnt)->right != nullptr) data.push_back(data.at(cnt)->right);
|
||||
cnt++;
|
||||
}
|
||||
for (auto x : data) {
|
||||
std::cout << "{" << x->element.first << ", " << x->element.second << "}" << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
//重要 -> 合并两个子树,把y合并进入x
|
||||
//* &x,指针的引用,表示指针可以修改(传入函数)
|
||||
template<class T>
|
||||
void maxHblt<T>::meld(binaryTreeNode<std::pair<int, T>>*& x,
|
||||
binaryTreeNode<std::pair<int, T>>*& y) {
|
||||
if (y == nullptr) return;
|
||||
if (x == nullptr) {
|
||||
x = y;
|
||||
//一定要y = nullptr
|
||||
y = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x->element.second < y->element.second) {
|
||||
std::swap(x, y);
|
||||
}
|
||||
|
||||
meld(x->right, y);
|
||||
|
||||
if (x->left == nullptr) {
|
||||
x->left = x->right;
|
||||
x->right = nullptr;
|
||||
x->element.first = 1;
|
||||
}
|
||||
else {
|
||||
if (x->left->element.first < x->right->element.first) {
|
||||
std::swap(x->left, x->right);
|
||||
}
|
||||
int rightNpl = (x->right ? x->right->element.first : 0);
|
||||
x->element.first = rightNpl + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //MAXHBLT_H
|
Reference in New Issue
Block a user