二叉排序树(二叉查找树)

1.相关概念
节点的度:一个节点拥有子树的数目。
树的高度:也称为树的深度,树中节点的最大层次。
二叉树或者为空集,或者由一个根节点和两棵互不相交的、分别称为左子树和右子树的二叉树组成。从定义可以看出一棵二叉树:
二叉树是有序树,区分左子树与右子树,不可以随意交换子树位置。
二叉树共有5种形态
所有节点都只有左子树的二叉树叫做左斜树,所有节点都只有右子树的二叉树叫做右斜树。左斜树和右子树统称为斜树。
斜树已经退化成线性结构,二叉树在查找上表现出来优异性能在斜树得不到体现。

完全二叉树
在一棵二叉树中,只有最下两层的度可以小于2,并且最下一层的叶子节点集中出现在靠左的若干位置上。
或者这样定义:对一棵具有n个节点的二叉树按层序从左到右编序,二叉树树某个节点的编序与同样位置的满二叉树节点的编序相同如果所有节点都满足这个条件,则二叉树为完全二叉树。

满二叉树
满二叉树要满足两个条件:
所有的节点都同时具有左子树和右子树。
所有的叶子节点都在同一层上。
在同样深度的二叉树中,满二叉树的节点数目是最多的,叶子数也是最多的。

二叉查找树
二叉查找树也称为二叉搜索树或二叉排序树。二叉排序树的节点包含键值key。二叉排序树或者是一棵空树,否则要求:
若它的左子树不为空,那么左子树上所有节点的key都小于根节点的key
若它的右子树不为空,那么右子树上所有节点的key都大于根节点的key
它的左右子树也分别为二叉排序树

根据定义,二叉查找树中没有重复key的节点。

二叉树的性质
性质一:在二叉树的第i层上至多有2^(i-1)个节点(i>=1)
性质二:深度为k的二叉树至多有2^k-1个节点
性质三:对任何一棵二叉树T,如果终端节点数为n0,度为2的节点数为n2 ,那么 n0 = n2 +1
证明:二叉树节点度数最大为2,则 : n = n0 + n1 + n2 (等式一)
从孩子个数角度出发: 度为0的节点没有孩子, 度为1的节点没有1个孩子,度为2的节点有2个孩子,孩子总数为 n00 + n11 +n2 2 = n1+2n2;
树的所有节点中,只有根不是任何节点的孩 子,因此有 n -1 = n1 + 2* n2 ,即 n = n1 + 2* n2 + 1. (等式二)
由等式一等式而可以推出 n0 = n2 +1

性质四: 具有n个节点的完全二叉树的高度为至少为log2(n+1)
证明:高度为h的二叉树最多有2{h}–1个结点。反之,对于包含n个节点的二叉树的高度至少为log2(n+1)。

性质五:如果对一棵有n个节点的完全二叉树的节点按层序编号(从第一层开始到最下一层,每一层从左到右编号),
对任一节点i有:
如果i=1 ,则节点为根节点,没有双亲。
如果2 * i > n ,则节点i没有左孩子 ;否则其左孩子节点为2*i . (n为节点总数)
如果2 * i+1>n ,则节点i没有右孩子;否则其右孩子节点为2*1+1

2.代码实现
BSNode.h


#ifndef DATASTRUCTURE_BSNODE_H
#define DATASTRUCTURE_BSNODE_H

//二叉查找树的节点结构
template <typename T>
class BSNode {
public:
    BSNode(T t): value(t), lchild(nullptr), rchild(nullptr){}


    BSNode() = default;

    T value;

    BSNode<T>* lchild;
    BSNode<T>* rchild;
    BSNode<T>* parent;
};

//        value:节点的值,也即是上文的key,类型由模板参数决定
//        lchild :指向节点的左孩子
//        rchild:指向节点的右孩子
//        parent: 指向节点的双亲
#endif //DATASTRUCTURE_BSNODE_H

BSTree.h

#ifndef DATASTRUCTURE_BSTREE_H
#define DATASTRUCTURE_BSTREE_H

#include <iostream>
#include "BSNode.h"
using namespace std;

//这里我们定义了二叉排序树的类型BSTree。它包含了:
//
//BSTree的根节点root,这是唯一的数据成员
//        操作的外部接口与内部实现接口。
//例如 preOrder()为提供给用户使用的接口,接口声明为public;
//而preOrder(AVLTreeNode* pnode)是类内部为了递归操作所使用的接口,接口声明为private。
//提供的其他接口都有相应的备注说明。

//二叉查找树
template <typename  T>
class BSTree {
public:
    BSTree();
    ~BSTree();

    void preOrder();                 //前序遍历
    void inOrder();                //中序遍历
    void postOrder();            //后序遍历
    void layerOrder();          //层次遍历

    BSNode<T>* search_recursion(T key);//递归地进行查找
    BSNode<T>* search_Iterator(T key);        //迭代地进行查找

    T search_minimum();//查找最小元素
    T search_maximum();查找最大元素

    BSNode<T> * successor (BSNode<T> * x);     //查找指定节点的后继节点
    BSNode<T> * predecessor(BSNode<T> * x);    //查找指定节点的前驱节点

    void insert(T key);    //插入指定值节点
    void remove(T key);    //删除指定值节点
    void destory();        //销毁二叉树
    void print();        //打印二叉树


    BSNode <T> * root;//根节点
    BSNode<T>* search(BSNode<T>* & p, T key);
    void remove(BSNode<T>*  p, T key);
    void preOrder(BSNode<T>* p);
    void inOrder(BSNode<T>* p);
    void postOrder(BSNode<T>* p);
    T search_minimun(BSNode<T>* p);
    T search_maximum(BSNode<T>* p);

    void destory(BSNode<T>* &p);


};

/*默认构造函数*/
template <typename T>
BSTree<T>::BSTree() :root(nullptr){}

template <class T>
BSTree<T>::~BSTree() {
    destory(root);
}

template <class  T>
void BSTree<T>::insert(T key) {
    BSNode<T> * pparent= nullptr;//记录合适的插入位置
    BSNode<T> * pnode=root;寻找合适的插入位置

    while (pnode!=nullptr){//寻找
        //nullptr是c++中空指针类型的关键字。
        pparent=pnode;
        if(key> pnode->value){
            pnode=pnode->rchild;
        }else if(key<pnode->value){
            pnode=pnode->rchild;
        } else
            break;
    }

    pnode=new BSNode<T> (key);//以元素的值构建新节点


    //插入
    if(pparent== nullptr){ //如果是空树
        root=pnode;  //则新节点为根
    } else{
        if(key >pparent->value){
            pparent->rchild=pnode;//否则新节点为其父节点的左孩
        } else
            pparent->lchild=pnode;//或右孩
    }

    pnode->parent=pparent; //指明新节点的父节点
}




//前序遍历
//若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树。(简记为:VLR)


//preOrder(AVLTreeNode* pnode)是类内部为了递归操作所使用的接口,接口声明为private。
template <typename  T>
void BSTree<T>::preOrder() {
    preOrder(root);
}

template <class  T>
void BSTree<T>::preOrder(BSNode<T> *p) {
    if(p!= nullptr)//不为空节点
    {
        cout<<p->value<<endl;
        preOrder(p->lchild);
        preOrder(p->rchild);
    }
}


//rchild中序遍历
//若二叉树为空,则空操作返回,否则从根节点开始,中序遍历根节点的左子树,然后访问根节点,最后中序遍历右子树。(简记为:LVR)
template<typename  T>
void BSTree<T>::inOrder() {
    inOrder(root);
}

template <class T>
void BSTree<T>::inOrder(BSNode<T> *p) {
    if(p!= nullptr){

        inOrder(p->lchild);
        cout<<p->value<<endl;
        inOrder(p->rchild);
    }
}

//
//后序遍历
//若树为空,则返回空操作,否则从左到右先叶子后节点的方式遍历访问左右子树,左右子树都访问结束,才访问根节点。(简称LRV)
template <class T>
void BSTree<T>::postOrder() {
    postOrder(root);
}

template<class T>
void BSTree<T>::postOrder(BSNode<T> *p) {
    if(p!= nullptr){
        postOrder(p->lchild);
        postOrder(p->rchild);
        cout<<p->value<<endl;
    }
}

//对于一棵二叉排序树,中序遍历时刚好可以输出一个非递减的序列。
//一个节点的前驱节点有3种情况:
//它有左子树,则其前驱节点为其右子树的最右节点
//它没有左子树,且它本身为右子树,则其父节点为其前驱节点
//它没有左子树,且它本身为左子树,则它的前驱节点为“拥有右子树的最近父节点”

/*寻找其前驱节点*/
template <class T>
BSNode<T> * BSTree<T>::predecessor(BSNode<T> *pnode) {
    if(pnode->lchild!= nullptr)//它有左子树
    {
        pnode=pnode->lchild;
        while(pnode->rchild!= nullptr){
            pnode=pnode->rchild;
        }
        return pnode;
    }
    //以下均为无左子树
    BSNode<T>* pparent=pnode->parent;
    while(pparent!= nullptr&&pparent->lchild==pnode){
        //如果进入循环,则是第三种情况:它本身为左子树,
        // 否则为第二种情况
        //拥有右子树的最近父节点
        pnode=pparent;
        pparent=pparent->parent;
    }
//、、、、、、、、、、、、、、、、、、、、、、、
    return pparent;
}
//一个节点的后继节点也有三种情况:
//它有右子树;则其后继节点为其右子树的最左节点
//同样的,
// 它没有右子树,但它本身是一个左孩子,则后继节点为它的双亲
//它没有右子树,但它本身是一个右孩子,则其后继节点为“具有左孩子的最近父节点”

//寻找其后继节点
template <class  T>
BSNode<T> * BSTree<T>::successor(BSNode<T> *x) {
    if(x->rchild!= nullptr){//右右子树
        x=x->rchild;
        while(x!= nullptr)
            x=x->lchild;
        return x;
    }
    //没有右子树
    BSNode<T> * xparent=x->parent;
    while(xparent!= nullptr&&xparent->rchild==x)
    {
//具有左孩子的最近父节点
        x=xparent;
        xparent=xparent->parent;

//        if(xparent->lchild!= nullptr)
//            return xparent;
    }
    return xparent;
}

/*删除二叉排序树的某个节点有三种情况:
被删除节点同时有左子树与右子树。
被删除节点只有左子树或只有右子树。
被删除节点没有子树。

对于第一种情况,我们的处理方式是将前驱节点的值保存在当前结点,继而删除前驱节点。
对于第二种情况,我们直接用子树替换被删节点。
对于第三种情况,我们可以直接删除节点。
 */

/*删除指定节点*/
template <class  T>
void BSTree<T>::remove(T key) {
    remove(root,key);
}
/*删除指定节点*/
/*内部使用函数*/
template <class T>
void BSTree<T>::remove(BSNode<T> *p, T key) {
    if(p!= nullptr){
        if(p->value==key){
            BSNode<T> * pdel= nullptr;//记录要删除的节点
            if(p->lchild== nullptr||p->rchild== nullptr)
                //情况二、三:被删节点只有左子树或右子树,或没有孩子
                pdel=p;
            else
                pdel=predecessor(p); //情况一:被删节点同时有左右子树,将前驱节点的值保存在当前结点,继而删除前驱节点。

            //此时,被删节点只有一个孩子(或没有孩子),保存该孩子指针
            BSNode<T> *pchild= nullptr;
            if(pdel->lchild!= nullptr)
                pchild=pdel->lchild;
            else
                pchild=pdel->rchild;

            //让孩子指向被删节点的父节点
            if(pchild!= nullptr)
                pchild->parent=pdel->parent;


            //让被删节点的父节点指向被删节点的孩子节点
            //如果要删除的节点是头结点,注意更改4root值
            if(pdel->parent== nullptr)
                root=pchild;
                //如果要删除的节点不是头节点,要注意更改它的双亲节点指向新的孩子节点
            else if(pdel->parent->lchild==p)
                pdel->parent->lchild=pchild;
            else
                pdel->parent->rchild=pchild;


            if(p->value!=pdel->value)
                p->value=pdel->value;
            delete pdel;
        }
            //进行递归删除
        else if(key>p->value)
            remove(p->rchild,key);
        else
            remove(p->lchild,key);
    }
}

/*查找指定元素的节点(非递归)*/
template <class T>
BSNode<T>* BSTree<T>::search_Iterator(T key) {
    BSNode<T> * pnode=root;
    while(pnode!= nullptr){
        if(key==pnode->value)//找到
            return pnode;
        if(key>pnode->value)
            pnode=pnode->rchild; //关键字比节点值大,在节点右子树查找
        else
            pnode=pnode->lchild;//关键字比节点值小,在节点左子树查找
    }
    return pnode;
}
/*查找指定元素的节点(递归)*/
template <class T>
BSNode<T> * BSTree<T>::search_recursion(T key) {
    return search(root,key);
}

/*private:search()*/
/*递归查找的类内部实现*/
template <typename T>
BSNode<T>* BSTree<T>::search(BSNode<T> *&p, T key) {
    if(p== nullptr)
        return nullptr;
    if(p->value==key)
        return p;
    if(key>p->value)
        return search(p->rchild,key);
    return search(p->lchild,key);
}

/*寻找最小元素*/
template <typename T>
T BSTree<T>::search_minimum() {
    return search_minimun(root);
}
template<class T>
T BSTree<T>::search_minimun(BSNode<T> *p) {
    if(p->lchild!= nullptr)
        return search_minimun(p->lchild);
    return p->value;
}

/*寻找最大元素*/
template <typename T>
T BSTree<T>::search_maximum()
{
    return search_maximum(root);
}
template<class T>
T BSTree<T>::search_maximum(BSNode<T> *p) {
    if(p->rchild!= nullptr)
        return search_maximum(p->rchild);
    return p->value;
}

/*销毁二叉树*/
//使用后序遍历递归销毁二叉树
template<class T>
void BSTree<T>::destory() {
    destory(root);
}

template <class T>
void BSTree<T>::destory(BSNode<T> *&p) {
    if(p!= nullptr){
        if(p->lchild!= nullptr)
            destory( p->lchild);
        if(p->rchild!= nullptr)
            destory(p->rchild);
        delete p;
        p=nullptr;
    }
}

#endif //DATASTRUCTURE_BSTREE_H

测试:main.cpp

#include <iostream>
#include "BSTree/BSTree.h"

using namespace std;
int main() {
    BSTree <int> t;
    t.insert(62);
    t.insert(58);
    t.insert(47);
    t.insert(51);
    t.insert(35);
    t.insert(37);
    t.insert(88);
    t.insert(73);
    t.insert(99);
    t.insert(93);
    t.insert(95);

    cout<<"中序遍历:"<<endl;
    t.inOrder();

    cout<<"最大元素:"<<t.search_maximum()<<endl;
    cout<<"最小元素:"<<t.search_minimum()<<endl;

    cout<<"删除元素99"<<endl;
    t.remove(99);
    cout << "最大元素:" << t.search_maximum() << endl;
    t.insert(111);
    cout<<"插入111"<<endl;
    cout << "最大元素:" << t.search_maximum() << endl;
    t.destory();

    return 0;
}

输出:
中序遍历:
37
62
73
88
95
99
最大元素:99
最小元素:37
删除元素99
最大元素:95
插入111
最大元素:111

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值