目录:
- 一、性质
- 二、结点定义
- 三、部分操作
- 插入:唯一键值和重复键值
- 删除:普通删除和懒惰删除
- 四种遍历:先、中、后序、层级
- 四、进一步思考
- 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 任意节点的左、右子树也分别为二叉查找树;
- 树中没有键值相等的节点。
struct TreeNode{
int value;
TreeNode *left, *right;
TreeNode(int val): value(val), left(NULL), right(NULL){}
};
三、部分操作 3.1插入
3.1.1 唯一键值结点的插入 递归插入即可。
void SearchTree::Insert(int val){
root = RecursiveInsert(val, root);
}TreeNode* SearchTree::RecursiveInsert(int val, TreeNode* head){
if(!head){return new TreeNode(val);
}
if(val > head->value){
head->right = RecursiveInsert(val, head->right);
}
else if(val < head->value){
head->left = RecursiveInsert(val, head->left);
}
return head;
}
3.1.2 支持重复键值结点的插入 如果键值是包含在简单的数据结构中,只需要额外一个记录频次的变量。
如果键值时包含在庞大的数据结构中,可以将相同键值存储到辅助数据结构中,比如链表、另一个查找树。
3.2 删除
3.2.1 唯一键值结点删除
- 递归找到删除结点
- 将它的值由其右子树最小值或者左子树最大值替换
- 然后递归删除右子树最小值结点/左子树最大值结点
void SearchTree::Delete(int val){
RecursiveDelete(val, root);
}TreeNode* SearchTree::RecursiveDelete(int val, TreeNode* head){
if(!head) return NULL;
if(head->value < val){
head->right = RecursiveDelete(val, head->right);
}
else if(head->value > val){
head->left = RecursiveDelete(val, head->left);
}
else{
if(head->left && head->right){
head->value = https://www.it610.com/article/RecursiveMin(head->right)->value;
head->right = RecursiveDelete(head->value, head->right);
}
else{
TreeNode* tmp = head;
if(!head->left) head = head->right;
else if(!head->right) head = head->left;
delete tmp;
tmp = NULL;
}
}
return head;
}
3.2.2 懒惰删除->重复键值结点删除 如果删除的结点比较少,可以用懒惰删除法。当一个结点要被删除,它仍可以被保留在tree中,只是被标记成删除的状态。而且,即便树有一半的结点被标记为删除状态,时间上的浪费也只是常数。
这个方法很适用于当树允许重复的结点出现时的情况。允许重复结点的树有记录频次的变量,删除一个结点,相当把这个变量减一,也就相当于mark了。
3.3 四种遍历
3.3.1 先序遍历 Preorder 顺序:父结点->左子树->右子树。
void SearchTree::PrintPreorder(){
Preorder(root);
}void SearchTree::Preorder(TreeNode* head){
if(!head) return;
cout << head->value << " ";
Preorder(head->left);
Preorder(head->right);
}
3.3.2 中序遍历 Inorder 顺序:左子树->父结点->右子树。
void SearchTree::PrintInorder(){
Inorder(root);
}void SearchTree::Inorder(TreeNode* head){
if(!head) return;
Inorder(head->left);
cout << head->value << " ";
Inorder(head->right);
}
3.3.3 后序遍历 Postorder 顺序:左子树->右子树->父结点。
void SearchTree::PrintPostorder(){
Postorder(root);
}void SearchTree::Postorder(TreeNode* head){
if(!head) return;
Postorder(head->left);
Postorder(head->right);
cout << head->value << " ";
}
3.3.4 层级遍历 Level Order 按照结点深度遍历,即先打印深度为0也就是结点,然后打印深度为1的结点,依次递推。
不同于前面三个遍历每次打印单个结点,层级遍历每一次需要打印一层结点,而结点之间从结构上,没有连接,主要通过父结点的左右指针访问。
因此,我们需要用到一个容器类变量,保存上一层的父结点。这里我选择用queue<>,遍历操作从队列前端执行,更新push操作从队列后端执行,如下所示。
void SearchTree::PrintLevelorder(){
if(!root) return;
queue nodeQueue;
nodeQueue.push(root);
Levelorder(nodeQueue);
}void SearchTree::Levelorder(queue &nodeQueue){
if(nodeQueue.empty()) return;
int size = nodeQueue.size();
TreeNode* cur = NULL;
while(size--){
cur = nodeQueue.front();
cout << cur->value << " ";
nodeQueue.pop();
if(cur->left) nodeQueue.push(cur->left);
if(cur->right) nodeQueue.push(cur->right);
}
return Levelorder(nodeQueue);
}
3.4 完整C++实现
#include
#include
#include
using namespace std;
struct TreeNode{
int value;
TreeNode *left, *right;
TreeNode(int val): value(val), left(NULL), right(NULL){}
};
class SearchTree {
public:
SearchTree():root(NULL){}
SearchTree(vector &arr);
void PrintPreorder();
void PrintInorder();
void PrintPostorder();
void PrintLevelorder();
void Insert(int val);
void Delete(int val);
TreeNode* Find(int val);
TreeNode* Min();
void Clear();
private:
void Preorder(TreeNode* head);
void Inorder(TreeNode* head);
void Postorder(TreeNode* head);
void Levelorder(queue &nodeQueue);
TreeNode* RecursiveInsert(int val, TreeNode* head);
TreeNode* RecursiveDelete(int val, TreeNode* head);
TreeNode* RecursiveFind(int val, TreeNode* head);
TreeNode* RecursiveMin(TreeNode* head);
TreeNode* RecursiveClear(TreeNode* &head);
TreeNode* root;
};
SearchTree::SearchTree(vector &arr){
for(auto i: arr)
Insert(i);
}void SearchTree::Insert(int val){
root = RecursiveInsert(val, root);
}TreeNode* SearchTree::RecursiveInsert(int val, TreeNode* head){
if(!head){return new TreeNode(val);
}
if(val > head->value){
head->right = RecursiveInsert(val, head->right);
}
else if(val < head->value){
head->left = RecursiveInsert(val, head->left);
}
return head;
}void SearchTree::Delete(int val){
RecursiveDelete(val, root);
}TreeNode* SearchTree::RecursiveDelete(int val, TreeNode* head){
if(!head) return NULL;
if(head->value < val){
head->right = RecursiveDelete(val, head->right);
}
else if(head->value > val){
head->left = RecursiveDelete(val, head->left);
}
else{
if(head->left && head->right){
head->value = https://www.it610.com/article/RecursiveMin(head->right)->value;
head->right = RecursiveDelete(head->value, head->right);
}
else{
TreeNode* tmp = head;
if(!head->left) head = head->right;
else if(!head->right) head = head->left;
delete tmp;
tmp = NULL;
}
}
return head;
}TreeNode* SearchTree::Find(int val){
return RecursiveFind(val, root);
}TreeNode* SearchTree::RecursiveFind(int val, TreeNode* head){
if(!head) return NULL;
if(head->value > val)
return RecursiveFind(val, head->left);
else if(head->value < val)
return RecursiveFind(val, head->right);
else return head;
}TreeNode* SearchTree::Min(){
return RecursiveMin(root);
}TreeNode* SearchTree::RecursiveMin(TreeNode* head){
if(!head) return NULL;
if(!head->left) return head;
return RecursiveMin(head->left);
}void SearchTree::Clear(){
RecursiveClear(root);
}TreeNode* SearchTree::RecursiveClear(TreeNode* &head){
if(head){
head->left = RecursiveClear(head->left);
head->right = RecursiveClear(head->right);
delete head;
head = NULL;
}
return NULL;
}void SearchTree::PrintPreorder(){
Preorder(root);
}void SearchTree::Preorder(TreeNode* head){
if(!head) return;
cout << head->value << " ";
Preorder(head->left);
Preorder(head->right);
}void SearchTree::PrintInorder(){
Inorder(root);
}void SearchTree::Inorder(TreeNode* head){
if(!head) return;
Inorder(head->left);
cout << head->value << " ";
Inorder(head->right);
}void SearchTree::PrintPostorder(){
Postorder(root);
}void SearchTree::Postorder(TreeNode* head){
if(!head) return;
Postorder(head->left);
Postorder(head->right);
cout << head->value << " ";
}void SearchTree::PrintLevelorder(){
if(!root) return;
queue nodeQueue;
nodeQueue.push(root);
Levelorder(nodeQueue);
}void SearchTree::Levelorder(queue &nodeQueue){
if(nodeQueue.empty()) return;
int size = nodeQueue.size();
TreeNode* cur = NULL;
while(size--){
cur = nodeQueue.front();
cout << cur->value << " ";
nodeQueue.pop();
if(cur->left) nodeQueue.push(cur->left);
if(cur->right) nodeQueue.push(cur->right);
}
return Levelorder(nodeQueue);
}int main(){
vector arr = {8, 4, 15, 2, 6, 9, 17};
SearchTree test(arr);
//Insertion Test
cout << "Before deletion: " << endl;
cout << "Preorder: ";
test.PrintPreorder();
cout << endl;
cout << "Inorder: ";
test.PrintInorder();
cout << endl;
cout << "Postorder: ";
test.PrintPostorder();
cout << endl;
cout << "Levelorder: ";
test.PrintLevelorder();
cout << endl;
test.Delete(8);
//Deletion Test
cout << "After deletion: ";
test.PrintPreorder();
cout << endl;
TreeNode* fRet = test.Find(8);
//Find Test
if(fRet) cout << "Find: " << fRet->value << endl;
else cout << "Can't find."<< endl;
test.Clear();
//Clear Test
test.PrintPreorder();
cout << endl;
四、进一步思考 按照以上insert和delete方法,我们发现一定次数后,树容易一个方向倾斜。
- 如果delete的结点由左子树最大值替换,则往右倾斜;
- 如果delete的结点由右子树最小值替换,则往左倾斜。
【数据结构 - 二叉查找树 Binary Search Tree】还有一个方法就是平衡二叉树。
推荐阅读
- 笔记|C语言数据结构——二叉树的顺序存储和二叉树的遍历
- C语言学习(bit)|16.C语言进阶——深度剖析数据在内存中的存储
- 数据结构和算法|LeetCode 的正确使用方式
- 先序遍历 中序遍历 后序遍历 层序遍历
- 数据结构|C++技巧(用class类实现链表)
- 数据结构|贪吃蛇代码--c语言版 visual c++6.0打开
- 算法|算法-二分查找
- 数据结构学习指导|数据结构初阶(线性表)
- leetcode题解|leetcode#106. 从中序与后序遍历序列构造二叉树
- java|ObjectOrientedProgramming - 面向对象的编程(多态、抽象类、接口)- Java - 细节狂魔