链表实现式
首先是二叉搜索树的链表模板,作为二叉树的特例,常用的操作有:
1. 节点定义
struct Node {
int key;
Node* left;
Node* right;
Node(int k) : key(k), left(nullptr), right(nullptr) {}
};
2. 节点的插入
Node* insert(Node* root, int key) {
if (root == nullptr) {
return new Node(key);
}
if (key < root->key) {
root->left = insert(root->left, key);
} else {
root->right = insert(root->right, key);
}
return root;
}
3. 节点的搜索
Node* search(Node* root, int key) {
if (root == nullptr || root->key == key) {
return root;
}
if (key < root->key) {
return search(root->left, key);
}
return search(root->right, key);
}
4. 返回最小节点
Node* minValueNode(Node* node) {
Node* current = node;
while (current && current->left != nullptr) {
current = current->left;
}
return current;
}
5. 节点的删除
Node* deleteNode(Node* root, int key) {
if (root == nullptr) {
return root;
}
if (key < root->key) {
root->left = deleteNode(root->left, key);
} else if (key > root->key) {
root->right = deleteNode(root->right, key);
} else {
if (root->left == nullptr) {
Node* temp = root->right;
delete root;
return temp;
} else if (root->right == nullptr) {
Node* temp = root->left;
delete root;
return temp;
}
Node* temp = minValueNode(root->right);
root->key = temp->key;
root->right = deleteNode(root->right, temp->key);
}
return root;
}
这里说明一下:删除的节点若为左儿子为空,就直接把右子树平移覆盖上去;右儿子为空,直接把左子树平移覆盖上去;若两儿子都存在,则复制左子树最大节点或右子树最小节点覆盖 删除节点 位置,再清除原件。这样保证结果还是二叉搜索树。
6. 遍历(以中序遍历示范)
void inorderTraversal(Node* root) {
if (root != nullptr) {
inorderTraversal(root->left);
cout << root->key << " ";
inorderTraversal(root->right);
}
}
整理一下,写成类的形式:
#include <iostream>
using namespace std;
// 二叉树节点定义
struct Node {
int key;
Node* left;
Node* right;
Node(int k) : key(k), left(nullptr), right(nullptr) {}
};
// 二叉搜索树类定义
class BinarySearchTree {
public://事实上,为了程序安全性,应该用private,然后提供接口便于数据向外输出。但用于做题可以不考虑
Node* root;
Node* insert(Node* node, int key) {
if (node == nullptr) {
return new Node(key);
}
if (key < node->key) {
node->left = insert(node->left, key);
} else {
node->right = insert(node->right, key);
}
return node;
}
Node* minValueNode(Node* node) {
Node* current = node;
while (current && current->left != nullptr) {
current = current->left;
}
return current;
}
Node* deleteNode(Node* node, int key) {
if (node == nullptr) {
return node;
}
if (key < node->key) {
node->left = deleteNode(node->left, key);
} else if (key > node->key) {
node->right = deleteNode(node->right, key);
} else {
if (node->left == nullptr) {
Node* temp = node->right;
delete node;
return temp;
} else if (node->right == nullptr) {
Node* temp = node->left;
delete node;
return temp;
}
Node* temp = minValueNode(node->right);
node->key = temp->key;
node->right = deleteNode(node->right, temp->key);
}
return node;
}
void inorderTraversal(Node* node) {
if (node != nullptr) {
inorderTraversal(node->left);
cout << node->key << " ";
inorderTraversal(node->right);
}
}
BinarySearchTree() : root(nullptr) {}
};
其次是二叉树的链表实现,主要框架如下:
#include <iostream>
#include <queue>
using namespace std;
// 二叉树节点定义
struct Node {
int data;
Node* left;
Node* right;
Node(int value) : data(value), left(nullptr), right(nullptr) {}
};
// 二叉树类定义
class BinaryTree {
private:
Node* root;
public:
BinaryTree() : root(nullptr) {}
// 插入节点,录入节点部分常借助队列实现,同时层次遍历也可以借助队列,更为自然
void insert(int value) {
Node* newNode = new Node(value);
if (root == nullptr) {
root = newNode;
return;
}
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* current = q.front();
q.pop();
if (current->left == nullptr) {
current->left = newNode;
return;
} else {
q.push(current->left);
}
if (current->right == nullptr) {
current->right = newNode;
return;
} else {
q.push(current->right);
}
}
}
// 删除节点
void deleteNode(int value) {
if (root == nullptr) {
cout << "Tree is empty. Cannot delete." << endl;
return;
}
// 使用层序遍历找到要删除的节点
queue<Node*> q;
q.push(root);
Node* target = nullptr;
Node* deepest = nullptr;
while (!q.empty()) {
deepest = q.front();
q.pop();
if (deepest->data == value) {
target = deepest;
}
if (deepest->left) {
q.push(deepest->left);
}
if (deepest->right) {
q.push(deepest->right);
}
}
if (target == nullptr) {
cout << "Node not found." << endl;
return;
}
target->data = deepest->data;
deleteDeepestNode(deepest);
}
// 删除最深的节点
void deleteDeepestNode(Node* node) {
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* current = q.front();
q.pop();
if (current == node) {
current = nullptr;
delete node;
return;
}
if (current->right) {
if (current->right == node) {
current->right = nullptr;
delete node;
return;
} else {
q.push(current->right);
}
}
if (current->left) {
if (current->left == node) {
current->left = nullptr;
delete node;
return;
} else {
q.push(current->left);
}
}
}
}
// 查找节点
Node* search(int value) {
if (root == nullptr) {
return nullptr;
}
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* current = q.front();
q.pop();
if (current->data == value) {
return current;
}
if (current->left) {
q.push(current->left);
}
if (current->right) {
q.push(current->right);
}
}
return nullptr;
}
// 层序遍历
void levelOrderTraversal() {
if (root == nullptr) {
return;
}
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* current = q.front();
q.pop();
cout << current->data << " ";
if (current->left) {
q.push(current->left);
}
if (current->right) {
q.push(current->right);
}
}
cout << endl;
}
};
主要说明一下删除部分的原理,使用层次遍历方式可以找到最深的节点,其删除会更加简单。于是再找到需要删除的节点target和最深处节点deepest后,将原deepest覆盖到target处,接着删除原deepest节点即可。
而具体情境中,很多时候二叉树的输入不一定按照满二叉树的方式行进。可以基于模板灵活变化,比如输入节点有三个指标,值,左儿子,右儿子(以输入顺序记录第几号,例如P2).
void insert(int value,int leftserial,int rightserial) {
Node* newNode = new Node(value,leftserial,rightserial);
if (root == nullptr) {
root = newNode;
quantity++;
return;
}
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* current = q.front();
q.pop();
if (current->leftseiral == quantity) {
current->left = newNode;
quantity++;
return;
} else {
if(current->left) q.push(current->left);
}
if (current->rightserial == quantity) {
current->right = newNode;
quantity++;
return;
} else {
if(current->right ) q.push(current->right);
}
}
}
数组实现式
在一个数组表示的二叉树中,如果根节点的索引为 1,那么对于任意一个节点的索引 i,其左子节点的索引为 2i,右子节点的索引为 2i + 1,父节点的索引为 i/2(向下取整)。
对于完全二叉树,叶节点的下标通常从 n/2 + 1 开始,其中 n 是数组中存储节点的数量。举个例子,如果一个数组表示的完全二叉树有 7 个节点,那么叶节点的索引从 4 开始(7/2 + 1 = 4)。
根据以上性质,很容易灵活使用一个数组实现二叉树与二叉搜索树。
P1. 洛谷p4715淘汰赛
采用数组存储这棵二叉树。
#include<iostream>
#include<cmath>
using namespace std;
struct country
{
int serial;
int strength;
};
int main()
{
int n;cin>>n;
int nums=pow(2,n);
country* tree=new country[2*nums+5];
for(int i=(2*nums-1)/2+1;i<=2*nums-1;i++)
{
tree[i].serial=i-(2*nums-1)/2;
cin>>tree[i].strength;
}
//for(int i=1;i<=2*nums-1;i++) cout<<tree[i]<<" ";
for(int i=2*nums-1;i>=1;i=i-2)
{
tree[i/2].strength=tree[i].strength>tree[i-1].strength?tree[i].strength:tree[i-1].strength;
tree[i/2].serial=tree[i].strength>tree[i-1].strength?tree[i].serial:tree[i-1].serial;
}
if(tree[2].strength<tree[3].strength) cout<<tree[2].serial;
else cout<<tree[3].serial;
delete[] tree;
return 0;
}
P2. 洛谷p4913二叉树深度
尝试以链表实现。
#include <iostream>
#include <queue>
using namespace std;
int maxdepth=0;
struct Node {
int depth=0;
Node* left;
Node* right;
int leftserial;
int rightserial;
Node(int a,int b) : leftserial(a), rightserial(b) {left=nullptr;right=nullptr;}
};
class BinaryTree {
private:
Node* root;
int quantity=1;
public:
BinaryTree() : root(nullptr) {}
void insert(int leftserial,int rightserial) {
Node* newNode = new Node(leftserial,rightserial);
if (root == nullptr) {
root = newNode;
quantity++;
root->depth=1;
return;
}
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* current = q.front();
q.pop();
if (current->leftserial == quantity) {
current->left = newNode;
newNode->depth = current->depth+1;
quantity++;
return;
} else {
if(current->left) q.push(current->left);
}
if (current->rightserial == quantity) {
current->right = newNode;
newNode->depth = current->depth+1;
quantity++;
return;
} else {
if(current->right ) q.push(current->right);
}
}
}
void levelOrderTraversal() {
if (root == nullptr) {
return;
}
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* current = q.front();
q.pop();
if(current->depth>maxdepth) maxdepth=current->depth;
//cout<<current->depth<<" ";
if (current->left) {
q.push(current->left);
}
if (current->right) {
q.push(current->right);
}
}
//cout<<endl;
}
};
int main()
{
int n;cin>>n;
BinaryTree a;
for(int i=1;i<=n;i++)
{
int l,r;
cin>>l>>r;
a.insert(l,r);
}
a.levelOrderTraversal();
cout<<maxdepth;
return 0;
}