二叉查找树(Binary Search Tree)

是一种树形的存储数据的结构

如图所示,它具有的特点是:

1、具有一个根节点

2、每个节点可能有0、1、2个分支

3、对于某个节点,他的左分支小于自身,自身小于右分支

接下来我们用c++来实现BST的封装

首先我们编写每个节点的类结构,分析可以知道我们每一个节点需要存储一个数据(data),左分支(left指向一个节点),右分支(right指向另一个节点)

因此我们建立

bstNode.h

#ifndef TEST1_BSTNODE_H
#define TEST1_BSTNODE_H
template <typename T> //这里使用模板类,以放入多种类型的数据,值得一提的是模板类不能讲声明和实现放在两个文件中
class bstNode{
public:
T data;
bstNode* left;
bstNode* right;
bstNode(){ //默认构造函数
data = ;
left = nullptr;
right = nullptr;
}
bstNode(T val){ //赋值构造函数
data = val;
left = nullptr;
right = nullptr;
}
};
#endif //TEST1_BSTNODE_H

接下来我们创建封装了各种方法的树形结构类:

myBST.h

这个头文件的设计思路如下:

1、先包含bstNode* root作为根节点,在通过根节点的左右指针延伸出整棵树;

2、封装了一些会用到的方法:搜索指定值(Search)、找出一颗子树中的最小值(treeMin)、插入指定值(Insert)、删除指定值(Delete)、判断是否是叶子结点(isLeaf)、判断是否有两个孩子(isNodeWithTwoChild)、

三种遍历方式(前序PreorderTraversal、中序InorderTraversal、后序Postodertraversal)、删除所有节点(DeleteAllNodes)、广度搜索进行周游(BFTraversal)、横着画图(Graph)、返回根节点(getRoot)、判断树空(isEmpty)

默认构造函数、vector为参数的构造函数、数组和长度为参数的构造函数、析构函数。

注意在这里为了防止公有方法直接调用私有数据,采用了创建以"__"开头的私有方法,让公有方法先来调用该私有方法,再让私有方法来调用私有数据,以确保其安全性。

#ifndef TEST1_MYBST_H
#define TEST1_MYBST_H #include <iomanip>
#include "bstNode.h"
#include <vector>
#include <deque>
#include <iostream>
using namespace std; template <typename T>
class myBST{
private:
bstNode<T> * root = nullptr;
bstNode<T> * __search(bstNode<T> * root , const T &key){
if (nullptr == root)
return nullptr;
if (key == root->data)
return root;
else if (key < root->data)
return __search(root->left, key);
else
return __search(root->right, key);
} //查找关键字是否存在
bstNode<T> * __treeMin(bstNode<T> * root , bstNode<T> * &parent){
bstNode<T> * curr = root;
while(curr->left!= nullptr){
parent = curr;
curr = curr->left;
}
return curr;
} //返回最小节点(一路向左)
bool __Insert(const T &key){
bstNode<T> * temp = new bstNode<T>(key);
bstNode<T> * parent = nullptr;
if(isEmpty()){
root=temp;
return true;
}
else{
bstNode<T> * curr;
curr = root;
while(curr){
parent = curr;
if(temp->data>curr->data) curr=curr->right;
else curr = curr->left;
}
if(temp->data<parent->data){
parent->left=temp;
return true;
}
else {
parent->right = temp;
return true;
}
}
return false;
} //插入指定值
bool __Delete(const T &key){
bool found = false;//存储有没有找到key的变量
if(isEmpty()){
cerr<<"BST为空"<<endl;
return false;
}
bstNode<T> * curr = root;
bstNode<T> * parrent = nullptr;
while(curr!= nullptr) {
if (key == curr->data) {
found = true;
break;
} else {
parrent = curr;
if (key < curr->data) curr = curr->left;
else curr = curr->right;
}
}
if(!found){
cerr<<"未找到key!"<<endl;
return false;
}
if (parrent == nullptr){//删除根节点
root = nullptr;
delete(curr);
return true;
}
/*
删除的节点有三种可能:
1、叶子结点
2、一个孩子的节点
3、两个孩子的节点
*/
if (__isLeaf(curr)){ //删除的点是叶子结点
if(parrent->left==curr) parrent->left= nullptr;
else parrent->right= nullptr;
delete(curr);
return true;
}
else if(__isNodeWithTwoChild(curr)){ //是两个孩子的节点
//以当前右子树中的最小值取代他
bstNode<T> * parrent = curr;
bstNode<T> * tmp = __treeMin(curr->right,parrent);
curr->data = tmp->data;
if(parrent->right==tmp)
parrent->right== nullptr;
else parrent->left== nullptr;
delete(tmp);
return true;
}
else{ //只有一个孩子的节点
if(curr->left!= nullptr){
if(curr->left == curr){
parrent->left=curr->left;
delete(curr);
return true;
}
else{
parrent->right=curr->right;
delete(curr);
return true;
}
}
if(curr->right!= nullptr){
if(curr->left == curr){
parrent->left=curr->left;
delete(curr);
return true;
}
else{
parrent->right=curr->right;
delete(curr);
return true;
}
}
}
return false;
} //删除指定值
bool __isLeaf(bstNode<T> * const & root){
if(root->left== nullptr && root->right== nullptr) return true;
else return false;
}//判断是否是叶子节点
bool __isNodeWithTwoChild(bstNode<T> * const & root){
if(root->left!= nullptr && root->right!= nullptr) return true;
else return false;
}//判断是否有两个孩子
void __InorderTraversal(bstNode<T> *root,std::vector<int>&result){
if(nullptr == root) return;
__InorderTraversal(root->left,result);
cout<<root->data<<" ";
result.push_back(root->data);
__InorderTraversal(root->right,result);
}//中序遍历
void __PreorderTraversal(bstNode<T> *root,std::vector<int>&result){
if(nullptr == root) return;
cout<<root->data<<" ";
result.push_back(root->data);
__InorderTraversal(root->left,result);
__InorderTraversal(root->right,result);
}//前序遍历
void __PostorderTraversal(bstNode<T> *root,std::vector<int>&result){
if(nullptr == root) return;
__InorderTraversal(root->left,result);
__InorderTraversal(root->right,result);
cout<<root->data<<" ";
result.push_back(root->data);
}//后序遍历
void __DeleteAllNodes(bstNode<T> *root){
if (root == nullptr) return;
__DeleteAllNodes(root->left);
__DeleteAllNodes(root->right);
__Delete(root->data);
}//删除所有节点
void __BFTraversal(vector<T>&result) {
deque<bstNode<T> *> TQueue;
bstNode<T> *pointer = root;
if (pointer != nullptr) {
TQueue.push_back(pointer);
}
while (!TQueue.empty()) {
pointer = TQueue.front();
TQueue.pop_front();
cout << pointer->data << " ";
result.push_back(pointer->data);
if (pointer->left != nullptr) TQueue.push_back(pointer->left);
if (pointer->right != nullptr) TQueue.push_back(pointer->right);
}
} //广度搜索来进行周游
void __Graph(int indent,bstNode<T>* root){
if(root != ){
__Graph(indent + , root->right);
cout<<setw(indent)<<" "<<root->data<<endl;
__Graph(indent + , root->left);
}
} //横着画图的内部接口
bstNode<T> * __GetRoot(){
return root;
} //返回根节点的内部接口
public:
myBST(){
root = nullptr;
} //默认构造
myBST(vector<T> arr){
root = nullptr;
for(int i =;i<(int)arr.size();i++){
__Insert(arr[i]);
}
}
myBST(T * arr,int len){
root = nullptr;
for(int i =;i<len;i++){
__Insert(*(arr+i));
}
}
~myBST(){
bstNode<T> * curr = root;
__DeleteAllNodes(curr);
}//析构
bool isEmpty() const{
return root == nullptr;
}//判断树空
bool search(const T &key){
bstNode<T> * temp = __search(root, key);
return (temp == nullptr) ? false : true;
}//查找关键字是否存在的对外接口
bool Insert(const T &key){
return __Insert(key);
}//插入节点的外部接口
bool Delete(const T &key){
return __Delete(key);
}//删除节点的外部接口
void InorderTraversal(vector<T>&result){
__InorderTraversal(root, result);
}//中序遍历的外部接口
void PreorderTraversal(vector<T>&result){
__PreorderTraversal(root, result);
}//前序遍历的外部接口
void PostorderTraversal(vector<T>&result){
__PostorderTraversal(root, result);
}//后序遍历的外部接口
void BFTraversal(vector<T>&result){
return __BFTraversal(result);
} //广度搜索外部接口
void Graph(int indent,bstNode<T>* root){
return __Graph(indent,root);
} //横着画图的外部接口
bstNode<T> * GetRoot(){
return __GetRoot();
} //返回根节点的外部接口
}; #endif //TEST1_MYBST_H

最后来进行测试:

main.cpp

#include <iostream>
#include <vector>
#include "myBST.h"
#include "bstNode.h"
using namespace std;
int main() {
vector<int> in = {,,,,,,,,,};
myBST<int> bst(in);
bst.Delete();
bst.Insert();
bool found = bst.search();
if(!found)
cout<<"not found!"<<endl;
else
cout<<"found!"<<endl;
vector<int> result;
cout<<"InorderTravelsal: ";
bst.InorderTraversal(result);
cout<<endl<<"PreorderTravelsal: ";
bst.PreorderTraversal(result);
cout<<endl<<"PostorderTraversal: ";
bst.PostorderTraversal(result);
cout<<endl<<"BFTraversal: ";
bst.BFTraversal(result);
cout<<endl<<"Graph:"<<endl;
bstNode<int>* pointer = bst.GetRoot();
bst.Graph(,pointer);
return ;
}

得到图示结果:

参考:https://blog.csdn.net/zhangxiao93/article/details/51444972

数据结构学习-BST二叉查找树 : 插入、删除、中序遍历、前序遍历、后序遍历、广度遍历、绘图的更多相关文章

  1. 已知前序(后序)遍历序列和中序遍历序列构建二叉树(Leetcode相关题目)

    1.文字描述: 已知一颗二叉树的前序(后序)遍历序列和中序遍历序列,如何构建这棵二叉树? 以前序为例子: 前序遍历序列:ABCDEF 中序遍历序列:CBDAEF 前序遍历先访问根节点,因此前序遍历序列 ...

  2. TZOJ 3209 后序遍历(已知中序前序求后序)

    描述 在数据结构中,遍历是二叉树最重要的操作之一.所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问. 这里给出三种遍历算法. 1.中序遍历的递归算法定义:  ...

  3. PAT甲题题解-1119. Pre- and Post-order Traversals (30)-(根据前序、后序求中序)

    (先说一句,题目还不错,很值得动手思考并且去实现.) 题意:根据前序遍历和后序遍历建树,输出中序遍历序列,序列可能不唯一,输出其中一个即可. 已知前序遍历和后序遍历序列,是无法确定一棵二叉树的,原因在 ...

  4. PTA L2-006 树的遍历-二叉树的后序遍历+中序遍历,输出层序遍历 团体程序设计天梯赛-练习集

    L2-006 树的遍历(25 分)   给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列.这里假设键值都是互不相等的正整数. 输入格式: 输入第一行给出一个正整数N(≤),是二叉树中结点的 ...

  5. 剑指offer面试题:输入某二叉树的前序遍历和中序遍历,输出后序遍历

    二叉树的先序,中序,后序如何遍历,不在此多说了.直接看题目描述吧(题目摘自九度oj剑指offer面试题6): 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结 ...

  6. 笔试算法题(36):寻找一棵二叉树中最远节点的距离 & 根据二叉树的前序和后序遍历重建二叉树

    出题:求二叉树中距离最远的两个节点之间的距离,此处的距离定义为节点之间相隔的边数: 分析: 最远距离maxDis可能并不经过树的root节点,而树中的每一个节点都可能成为最远距离经过的子树的根节点:所 ...

  7. [Swift]LeetCode889. 根据前序和后序遍历构造二叉树 | Construct Binary Tree from Preorder and Postorder Traversal

    Return any binary tree that matches the given preorder and postorder traversals. Values in the trave ...

  8. [二叉树建树]1119. Pre- and Post-order Traversals (30) (前序和后序遍历建立二叉树)

    1119. Pre- and Post-order Traversals (30) Suppose that all the keys in a binary tree are distinct po ...

  9. 数据结构学习:二叉查找树的概念和C语言实现

    什么是二叉查找树? 二叉查找树又叫二叉排序树,缩写为BST,全称Binary Sort Tree或者Binary Search Tree. 以下定义来自百度百科: 二叉排序树或者是一棵空树,或者是具有 ...

随机推荐

  1. Java中的阻塞队列-LinkedBlockingQueue(二)

    原文地址:http://benjaminwhx.com/2018/05/11/%E3%80%90%E7%BB%86%E8%B0%88Java%E5%B9%B6%E5%8F%91%E3%80%91%E8 ...

  2. nopCommerce 4.10 发布了

    我们的开发工作主要集中在将nopCommerce转移到.NET Core 2.1,性能和架构改进,进一步增强和修复错误. NopChommerce 中文社区:http://www.nopcn.com ...

  3. vue组件双向绑定.sync修饰符的一个坑

    我们知道组件是单项的,但是有时候需要双向,这时候我们可以使用.sync修饰符,但今天遇到一个坑,一直不成功,花了半小时试出来的.... 在编程的时候我们很习惯冒号后面跟着空格.而.sync双向绑定需要 ...

  4. 黑客伦理(hacker ethic)--《黑客与画家》

    使用计算机以及所有有助于了解这个世界本质的事物都不应受到任何限制.任何事情都应该亲手尝试. Access to computers--and anything that might teach you ...

  5. jquery 滑块导航菜单

    带滑块的导航菜单,鼠标悬浮时,滑块会移动至鼠标位置,离开时,滑块会回到原来的位置,点击菜单之后滑块会停留在被点击菜单位置,等待下一次的鼠标悬浮事件或者点击事件,效果图: 图片效果不行,直接上代码: & ...

  6. Android自定义控件练手——波浪效果

    这一次要绘制出波浪效果,也是小白的我第一次还望轻喷.首先当然是展示效果图啦: 一.首先来说说实现思路. 想到波浪效果,当然我第一反应是用正余弦波来设计啦(也能通过贝塞尔曲线,这里我不提及这个方法但是在 ...

  7. 轻松解决 Eclipse Indigo 3.7 中文字体偏小,完美 Consolas 微软雅黑混合字体!(转)

    在 Windows 7 下初始后化,发现界面变化不大,但中文字体却面目全非,小得根本看不见,而且也看起来很不爽.其实这是 Eclipse 的默认字体换了,以前的一直是 Courier New ,这次e ...

  8. appium-python-api中文文档

    来自https://wenku.baidu.com/view/533603ce581b6bd97e19eaa1.html mark,同时提供给需要使用python写脚本的童鞋们

  9. 如何更换vim-airline的theme

    仓库位置: 点我直达 (主题以前是和airline在同个仓库的,现在独立出来了) 这些内置的这些主题,可以直接使用,方法是在 “.vimrc”文件中写 let g:airline_theme=&quo ...

  10. 永恒之蓝EternalBlue复现

    0x01 漏洞原理:http://blogs.360.cn/blog/nsa-eternalblue-smb/ 目前已知受影响的 Windows 版本包括但不限于:Windows NT,Windows ...