二叉搜索树(BST树)

来源:互联网 发布:unity3d中文版下载 编辑:程序博客网 时间:2024/05/29 03:12

一、简介

(1)二叉搜索树,又叫做二叉排序树二叉查找树有序二叉树
是指一棵空树或者具有下列性质的二叉树:

  1. 每个节点都有一个作为搜索依据的关键码(key),所有节点的关键码互不相同。
  2. 左子树上所有节点的关键码(key)都小于根节点的关键码(key)。
  3. 右子树上所有节点的关键码(key)都大于根节点的关键码(key)。
  4. 左右子树都是二叉搜索树。

(2)二叉搜索树中序遍历的结果就是一个升序序列;

(3)空树也是一个二叉搜索树;

既然有以上性质,那么二叉树的查找是相当方便的;当然插入和删除,复杂度也会明显降低。
这里写图片描述

二、代码实现

(1)二叉搜索树的查找

从根结点开始搜索
如果根结点的健值大于要寻找的key,则向根结左点子树寻找;
如果根结点的健值小于要寻找的健值,则向根结点的右子树寻找;
如果相等,则代表找到;
如果一直找到NULL也没有找到,则说明该健值没有在此二叉搜索树里面;

bool _Find(Node* root,const T& x)//查找---非递归    {          //1.如果二叉搜索树为空,则返回错          if (root==NULL)          {              return false;          }          //2.二叉搜索树不为空          Node* cur=root;          while (cur)          {              //2.1如果根结点的健值大于要寻找的key,则向根结点左子树寻找;              if (cur->data>x)              {                  cur=cur->left;              }              //2.2如果根结点的健值小于要寻找的健值,则向根结点的右子树寻找;              else if (cur->data<x)              {                    cur=cur->right;              }              //2.3如果相等,则代表找到;              else               {                  return true;              }          }          //2.4如果一直找到NULL也没有找到,则说明该健值没有在此二叉搜索树里面;          return false;    }

(2)二叉搜索树的插入

首先说明,在二叉搜索树中,不能出现相同key值的两个结点;
二叉搜索树的插入是,先找到合适的空位置,然后进行插入;如果在寻找的过程中,发现了和要插入的值相同的结点;
那么就不用插入了,直接返回;
这里写图片描述

bool _InsertNoR(Node* root,const T& x)//给搜索二叉树插入某个元素--非递归    {        //1.如果二叉搜索树为空,则直接插入节点        if (root==NULL)        {            root=new Node(x);            _root=root;            return true;        }        //2.二叉搜索树不为空        Node* cur=root;//遍历二叉搜索树        Node* father=NULL;//记录正确插入位置的父节点;        //2.1寻找插入的正确位置,在此期间,如果遇到和插入节点相同的结点,则直接返回错误;        while (cur)        {                if (cur->data==x)             {                 return false;             }                else if (cur->data > x)             {                 father=cur;                 cur=cur->left;             }                else             {                 father=cur;                 cur=cur->right;             }        }        //2.2寻找到正确的插入位置,然后进行插入操作;            cur=new Node(x);//要插入的结点        //2.2.1如果父节点的值小于插入的值,则插入到父节点的右边        if ( x> father->data)        {            father->right=cur;        }        //2.2.2如果父节点的值大于插入的值,则插入到父节点的左边        else        {            father->left=cur;        }        return true;    } 

(3)二叉搜索树的删除
二插搜索树的删除比较麻烦,下面一一分析;
1>如果二叉搜索树为空,返回错;
2>如果要删除的结点没有在二叉搜索树中,则直接返回错误;
3>如果通过查找,发现要删除的结点是叶子节点,没有左右孩子,则直接将该节点删除;
4>如果通过查找发现要删除的结点只有一个孩子;

  • 如果该要删除的结点是父节点的左孩子,就将该孩子连接到父节点的左边;

  • 如果该要删除的结点是父节点的右孩子,就将该孩子连接到父节点的右边;

5>如果通过寻找发现该要删除的结点左右孩子都有;
那么首先:找到该节点的右子树的最小结点(或者也可以找到该节点的左子树的最大节点),该最小结点就是右子树的最左结点;

然后判断:该最小结点是否还有右子树,如果还有右子树,则还要判断改最小结点是其父节点的左孩子还是右孩子,如果是左孩子,则将该最小结点的右子树连接到其父节点的左边;如果是右孩子,则将该最小结点的右子树连接到其父节点的右边;

最后:将游离出来的最小节点和要删除的结点交换值,然后将最小节点释放;

注: 出现该最小结点是其父节点的右孩子的情况只有一种,那就是这个最小结点就是要删除的结点的右子树的根结点;
这里写图片描述

ool _RmoveNoR(Node* root,const T& x)    //二叉搜索树的删除---非递归    {        //1.二叉搜索树为空        if (root==NULL)        {            return false;        }        //2.二叉搜索树不为空        Node* cur=root;        Node* father=NULL;        //2.2寻找要删除的结点        while (cur)        {            //2.2.1x小于cur,往左子树寻找            if (cur->data>x)            {                father=cur;                cur=cur->left;            }            //2.2.2 x大于cur,往右子树寻找            else if (cur->data<x)            {                father=cur;                cur=cur->right;            }            //2.2.3找到结点,准备删除            else            {                //2.2.3.1删除的结点为叶子节点                if (cur->left==NULL&&cur->right==NULL)                {                    //1)要删除的结点是父节点的左孩子                    if (father->left==cur)                    {                        delete cur;                        cur=NULL;                        father->left=NULL;                        return true;                    }                    //1)要删除的结点时父节点的右孩子                    if (father->right==cur)                    {                        delete cur;                        cur=NULL;                        father->right=NULL;                        return true;                    }                }                //2.2.3.2删除的结点只右右子树!                if (cur->left==NULL&&cur->right)                {                    //1)要删除的结点是父节点的左孩子                    if (father->left==cur)                    {                        father->left=cur->right;                        delete cur;                        cur=NULL;                        return true;                    }                    //1)要删除的结点是父节点的右孩子                    if(father->right==cur)                    {                        father->right=cur->right;                        delete cur;                        cur=NULL;                        return true;                    }                }                //2.2.3.3删除的结点只有左子树!                if (cur->left&&cur->right==NULL)                {                    //1)要删除的结点时父节点的左孩子                    if (father->left==cur)                    {                        father->left=cur->left;                        delete  cur;                        cur=NULL;                        return true;                    }                    //1)要删除的结点时父节点的右孩子                    if (father->right==cur)                    {                        father->right=cur->left;                        delete cur;                        cur=NULL;                        return true;                    }                }                //2.2.3.4删除的结点左右子树都有                if (cur->left&&cur->right)                {                    //首先先找到以该节点为根的右子树的最小结点,就是右子树中序遍历的第一个结点,就是右子树的最左结点                    //然后将此节点替换要删除的结点;                    Node* min=cur->right;                    Node* father1=cur;                    while (min->left)                    {                        father1=min;                        min=min->left;                    }                     if (min->right&&father1->left==min)                    {                        father1->left=min->right;                    }                    if (min->right&&father1->right==min)                    {                        father1->right=min->right;                    }                    std::swap(cur->data,min->data);                    delete min;                    min=NULL;                    return true;                }            }        }        //2.3该节点没在二叉搜索树里面        return false;    }

三、二叉搜索树的退化及缺陷

二插搜索树的查找,插入和删除在一般情况下的时间复杂度都是O(H)【H为二叉树的高度】;

但是在插入有序数列或者接近有序的数列之后,查找和删除的时间复杂度是O(N);这和链表的顺序表的查找效率一样;删除效率和链表一样;体现不出二叉树每次排除一般的优势;
这里写图片描述
所以就出现了高度平衡的二叉搜索树AVL; 【明天写】

四、完整代码

#include<iostream>using namespace std;template<class T>struct TreeNode{     T data;     TreeNode<T>* left;     TreeNode<T>* right;     TreeNode(const T& x)         :data(x)         ,left(NULL)         ,right(NULL)     {}};template<class T>class  SearchBinaryTree{    typedef TreeNode<T>  Node;public:    SearchBinaryTree()//构造        :_root(NULL)    {}    ~SearchBinaryTree()//析构    {        _Destroy(_root);    }    bool Find(const T& x)//寻找    {        return _Find(_root,x);    }    bool InsertNoR(const T& x)//插入---非递归    {        return _InsertNoR(_root,x);    }    bool RmoveNoR(const T& x)//删除---非递归    {        return _RmoveNoR(_root,x);    }    void  Printf()//前序打印搜索二叉树    {          _Printf(_root);    }protected:    void _Destroy(Node* root)//销毁搜索二叉树    {         if (root==NULL)         {             return ;         }         _Destroy(root->left);         _Destroy(root->right);         delete root;    }    void _Printf(Node* root)    {        if (root==NULL)        {            return ;        }        cout<<root->data<<" ";        _Printf(root->left);        _Printf(root->right);    }    bool _Find(Node* root,const T& x)//查找---非递归    {          //1.如果二叉搜索树为空,则返回错          if (root==NULL)          {              return false;          }          //2.二叉搜索树不为空          Node* cur=root;          while (cur)          {              //2.1如果根结点的健值大于要寻找的key,则向根结点左子树寻找;              if (cur->data>x)              {                  cur=cur->left;              }              //2.2如果根结点的健值小于要寻找的健值,则向根结点的右子树寻找;              else if (cur->data<x)              {                    cur=cur->right;              }              //2.3如果相等,则代表找到;              else               {                  return true;              }          }          //2.4如果一直找到NULL也没有找到,则说明该健值没有在此二叉搜索树里面;          return false;    }    bool _InsertNoR(Node* root,const T& x)//给搜索二叉树插入某个元素--非递归    {        //1.如果二叉搜索树为空,则直接插入节点        if (root==NULL)        {            root=new Node(x);            _root=root;            return true;        }        //2.二叉搜索树不为空        Node* cur=root;//遍历二叉搜索树        Node* father=NULL;//记录正确插入位置的父节点;        //2.1寻找插入的正确位置,在此期间,如果遇到和插入节点相同的结点,则直接返回错误;        while (cur)        {                if (cur->data==x)             {                 return false;             }                else if (cur->data > x)             {                 father=cur;                 cur=cur->left;             }                else             {                 father=cur;                 cur=cur->right;             }        }        //2.2寻找到正确的插入位置,然后进行插入操作;            cur=new Node(x);//要插入的结点        //2.2.1如果父节点的值小于插入的值,则插入到父节点的右边        if ( x> father->data)        {            father->right=cur;        }        //2.2.2如果父节点的值大于插入的值,则插入到父节点的左边        else        {            father->left=cur;        }        return true;    }    bool _RmoveNoR(Node* root,const T& x)    //二叉搜索树的删除---非递归    {        //1.二叉搜索树为空        if (root==NULL)        {            return false;        }        //2.二叉搜索树不为空        Node* cur=root;        Node* father=NULL;        //2.2寻找要删除的结点        while (cur)        {            //2.2.1x小于cur,往左子树寻找            if (cur->data>x)            {                father=cur;                cur=cur->left;            }            //2.2.2 x大于cur,往右子树寻找            else if (cur->data<x)            {                father=cur;                cur=cur->right;            }            //2.2.3找到结点,准备删除            else            {                //2.2.3.1删除的结点为叶子节点                if (cur->left==NULL&&cur->right==NULL)                {                    //1)要删除的结点是父节点的左孩子                    if (father->left==cur)                    {                        delete cur;                        cur=NULL;                        father->left=NULL;                        return true;                    }                    //1)要删除的结点时父节点的右孩子                    if (father->right==cur)                    {                        delete cur;                        cur=NULL;                        father->right=NULL;                        return true;                    }                }                //2.2.3.2删除的结点只右右子树!                if (cur->left==NULL&&cur->right)                {                    //1)要删除的结点是父节点的左孩子                    if (father->left==cur)                    {                        father->left=cur->right;                        delete cur;                        cur=NULL;                        return true;                    }                    //1)要删除的结点是父节点的右孩子                    if(father->right==cur)                    {                        father->right=cur->right;                        delete cur;                        cur=NULL;                        return true;                    }                }                //2.2.3.3删除的结点只有左子树!                if (cur->left&&cur->right==NULL)                {                    //1)要删除的结点时父节点的左孩子                    if (father->left==cur)                    {                        father->left=cur->left;                        delete  cur;                        cur=NULL;                        return true;                    }                    //1)要删除的结点时父节点的右孩子                    if (father->right==cur)                    {                        father->right=cur->left;                        delete cur;                        cur=NULL;                        return true;                    }                }                //2.2.3.4删除的结点左右子树都有                if (cur->left&&cur->right)                {                    //首先先找到以该节点为根的右子树的最小结点,就是右子树中序遍历的第一个结点,就是右子树的最左结点                    //然后将此节点替换要删除的结点;                    Node* min=cur->right;                    Node* father1=cur;                    while (min->left)                    {                        father1=min;                        min=min->left;                    }                     if (min->right&&father1->left==min)                    {                        father1->left=min->right;                    }                    if (min->right&&father1->right==min)                    {                        father1->right=min->right;                    }                    std::swap(cur->data,min->data);                    delete min;                    min=NULL;                    return true;                }            }        }        //2.3该节点没在二叉搜索树里面        return false;    }protected:     Node* _root;};int main(){    SearchBinaryTree<int>  st;    //插入    st.InsertR(7);    st.InsertR(5);    st.InsertR(4);    st.InsertR(3);    st.InsertR(6);    st.InsertR(9);    st.InsertR(15);    st.InsertR(1);    st.InsertR(8);    st.InsertR(10);    st.InsertR(12);    st.Printf();    cout<<endl;    //寻找    cout<<"寻找"<<endl;    cout<<st.Find(7)<<endl;    cout<<st.Find(5)<<endl;    cout<<st.Find(4)<<endl;    cout<<st.Find(3)<<endl;    cout<<st.Find(6)<<endl;    cout<<st.Find(9)<<endl;    cout<<st.Find(15)<<endl;    cout<<st.Find(1)<<endl;    cout<<st.Find(8)<<endl;    cout<<st.Find(10)<<endl;    cout<<st.Find(12)<<endl;    cout<<st.Find(13)<<endl;    cout<<st.Find(14)<<endl;    /*cout<<"删除"<<endl;*/    //删除    st.RmoveNoR(3);    st.RmoveNoR(6);    st.RmoveNoR(9);    st.Printf();    return 0;}

这里写图片描述

END!!!