0
点赞
收藏
分享

微信扫一扫

C++二叉搜索树

洒在心头的阳光 2022-01-24 阅读 25
c++

文章目录

二叉搜索树

二叉搜索树的概念

二叉搜索树又称二叉排序树(因为走中序,数据都是有序的),它或者是一棵空树,或者是具有以下性质的二叉树。

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

即每颗子树中,左子树中值都小于根,右子树中的值都大于根。

image-20220122192802381

如,int a[]={5,3,4,1,7,8,2,6,0,9};

其时间复杂度极端情况下是 O ( n ) O(n) O(n)

只有当树的形状接近完全二叉树或者满二叉树,才能达到 l o g ( n ) log(n) log(n)

所以实际中搜索二叉树极端情况下没办法保证效率,所以需要对搜索二叉树进一步探索特性拓展延伸:AVLTree 红黑树

他们对搜索二叉树左右高度提出了要求,非常接近完全二叉树,他们效率可以达到 O ( n ) O(n) O(n)

上述一般用于内存中查找,当数据在磁盘中时(外查找),对树高度进一步提出了要求,进一步衍生出了B树和B+树,B树系列属于多叉平衡树。

增删查改的时间复杂度 O ( n ) O(n) O(n)

二叉搜索树操作

定义

#include<iostream>
#include<string>
using namespace std;

template <typename K>
struct BSTreeNode
{
  public: 
    BSTreeNode* _left;
    BSTreeNode* _right;

    BSTreeNode(K key)
      :_left(nullptr),
      _right(nullptr),
      _key(key)
      { }
    K _key;
};

template <typename K>
class BSTree
{
  typedef BSTreeNode<K> Node;
  public:
      BSTree()
        :_root(nullptr)
      { }
      //涉及深浅拷贝,需要实现拷贝构造 operator = 等
  private:
  Node* _root;
};

非递归版

插入
template <typename K>
class BSTree
{
  typedef BSTreeNode<K> Node;
  public:
      BSTree()
        :_root(nullptr)
      { }
      //涉及深浅拷贝,需要实现拷贝构造 operator = 等
      bool Insert(const K& key)
      {
           if( _root == nullptr )
           {
              _root = new Node(key);
              return true; 
           }
           Node* parent = nullptr;
           Node* cur = _root;
           while( cur )
           {
              if( cur->_key < key )
              {
                  parent = cur;
                  cur = cur->_right;
              }
              else if( cur ->_key > key )
              {
                  parent = cur;
                  cur = cur->_left;
              }
              else return false;
           }
           cur = new Node(key); 
           if( parent ->_key < key )
           {
               parent ->_right = cur;
           }
           else parent ->_left = cur;
           return true;
      }
  private:
  Node* _root;  
};
中序遍历
template <typename K>
class BSTree
{
  typedef BSTreeNode<K> Node;
  public:
      BSTree()
        :_root(nullptr)
      { }
      //涉及深浅拷贝,需要实现拷贝构造 operator = 等
      bool Insert(const K& key)
      {
           if( _root == nullptr )
           {
              _root = new Node(key);
              return true; 
           }
           Node* parent = nullptr;
           Node* cur = _root;
           while( cur )
           {
              if( cur->_key < key )
              {
                  parent = cur;
                  cur = cur->_right;
              }
              else if( cur ->_key > key )
              {
                  parent = cur;
                  cur = cur->_left;
              }
              else return false;
           }
           cur = new Node(key); 
           if( parent ->_key < key )
           {
               parent ->_right = cur;
           }
           else parent ->_left = cur;
           return true;
      }
      void _InOrder(Node* root){
           if(root == nullptr) return;
          _InOrder(root->_left);
          cout<<root->_key<<endl;
          _InOrder(root->_right);
      } 
    
      void InOrder()
      {
          _InOrder(_root);
      }
  private:
  	Node* _root;  
};
int main()
{
    BSTree<int>t;

    int a[]={5,3,4,1,7,8,2,6,0,9};
    for(auto e :a)
    {
        t.Insert(e);
    }
    t.InOrder();
    return 0;
}
查找
template <typename K>
class BSTree
{
  typedef BSTreeNode<K> Node;
  public:
      BSTree()
        :_root(nullptr)
      { }
      //涉及深浅拷贝,需要实现拷贝构造 operator = 等
      bool Insert(const K& key)
      {
           if( _root == nullptr )
           {
              _root = new Node(key);
              return true; 
           }
           Node* parent = nullptr;
           Node* cur = _root;
           while( cur )
           {
              if( cur->_key < key )
              {
                  parent = cur;
                  cur = cur->_right;
              }
              else if( cur ->_key > key )
              {
                  parent = cur;
                  cur = cur->_left;
              }
              else return false;
           }
           cur = new Node(key); 
           if( parent ->_key < key )
           {
               parent ->_right = cur;
           }
           else parent ->_left = cur;
           return true;
      }
      Node* Find(const K& key)
      {
          Node* cur =_root;
          while(cur){
              if( key >cur -> _key ) cur = cur -> _right;
              else if( key < cur-> _key) cur = cur ->_left;
       		  else return cur;          
          }
          return nullptr;
      }
    
      void _InOrder(Node* root){
           if(root == nullptr) return;
          _InOrder(root->_left);
          cout<<root->_key<<endl;
          _InOrder(root->_right);
      } 
    
      void InOrder()
      {
          _InOrder(_root);
      }
  private:
  	Node* _root;  
};
删除

image-20220122192802381

删除一个值等于key的节点,分情况分析。

  1. 要删除的是6,90…,非常好处理。特征:叶子节点。
    1. 删除当前节点,将父亲对应的儿子节点置为野指针。
  2. 要删除的节点是8,1…也还行。特征:只有一个孩子
    1. 删除当前节点,把孩子交给当前节点的父亲,顶替自己的位置。
  3. 要删除的是57…。特征:有两个孩子。
    1. 解决方案:替换法删除。
    2. 去孩子里面找一个值能替换自己位置的节点,替换自己删除。
      1. 左子树的最大节点——左子树最右节点就是最大的
      2. 右子树的最小节点——右子树最左节点就是最小的
    3. 这两个节点去替换后,都符合特征1或者特征2,可以直接删除。
  4. 再分析一下,特征1的处理可以用特征2的做法来处理。因此只要处理特征2和特征3两种。

对于删除特征2的情况,要考虑下图的细节

image-20220122215753823 image-20220122221457452

非递归版本

注意删除到只有左子树全为空或者右子树全为空的特殊情况,father是空的。

template <typename K>
class BSTree
{
  typedef BSTreeNode<K> Node;
  public:
      BSTree()
        :_root(nullptr)
      { }
      //涉及深浅拷贝,需要实现拷贝构造 operator = 等
      bool Insert(const K& key)
      {
           if( _root == nullptr )
           {
              _root = new Node(key);
              return true; 
           }
           Node* parent = nullptr;
           Node* cur = _root;
           while( cur )
           {
              if( cur->_key < key )
              {
                  parent = cur;
                  cur = cur->_right;
              }
              else if( cur ->_key > key )
              {
                  parent = cur;
                  cur = cur->_left;
              }
              else return false;
           }
           cur = new Node(key); 
           if( parent ->_key < key )
           {
               parent ->_right = cur;
           }
           else parent ->_left = cur;
           return true;
      }
      Node* Find(const K& key)
      {
          Node* cur =_root;
          while(cur){
              if( key >cur -> _key ) cur = cur -> _right;
              else if( key < cur-> _key) cur = cur ->_left;
       		  else return cur;          
          }
          return nullptr;
      }
      
      void _InOrder(Node* root){
           if(root == nullptr) return;
          _InOrder(root->_left);
          cout<<root->_key<<endl;
          _InOrder(root->_right);
      } 
    
      bool Erase(const K& key){
    	   Node* parent = nullptr;
           Node* cur = _root;
           while( cur )
           {
               if( cur -> _key <key)
               {
                   parent = cur;
                   cur =cur ->_right;
               }
               else if(cur -> _key > key)
               {
                   parent =cur ;
                   cur =cur ->_left;
               }
               else{
                   //找到了,准备删除
                   //左为空或者右为空,把另一个孩子交给父亲
                   if(_cur->_left == nullptr)
                   {
                       if( _cur == _root){
                           _root = _cur ->_right;
                       }
                       else{
                           if( parent ->_left ==cur)
                           {
                               parent->_left = cur ->_right;
                           }
                           else{
                               parent ->_right =cur ->_right;
                           }
                       delete _cur;
                   }
                   else if(_cur -->_right == nullptr)
                   {
                        if( _cur == _root){
                            _root = _cur -> _left;
                        }
                       	else{
                            if( parent -> _left == cur)
                            {
                                parent ->_left =cur ->_left;
                            }
                            else{
                                parent ->_right=cur->_left;
                            }
                        }
                    	delete _cur;
                   }
                   else{//左右都不为空,替换法删除
                       //假设规定右子树最小节点去替换
                       Node* minParent = cur;
    				   Node* minRight = cur -> _right;
                       while(minRight -> _left) {minParent = minRight;minRight = minRight ->_left;}
                       cur->_key = minRight ->_key;//保存替换节点的值
                       //删除替换的节点
                       if(minParent -> _left == minRight){
                           minParent ->_left =minRight ->_right;
                       }
                       else minParent ->_right = minRight ->_right;
                       delete minRight;
                   }
                   return true;
               }
           }
           return false;
      }
    	
      void InOrder()
      {
          _InOrder(_root);
      }
  private:
  	Node* _root;  
};

特征3递归版本:

else{//左右都不为空,替换法删除
    //假设规定右子树最小节点去替换
    Node* minRight = cur -> _right;
    while(minRight -> _left) minRight = minRight ->_left;
    
    K mi =minRight ->_key;
    //递归调用自己去删除替换节点,一定会走到左为空的情况处理
    this->Erase(mi);
    cur->_key=mi;
}
#include"BinarySearchTree.h"
int main()
{
    BSTree<int>t;

    int a[]={5,3,4,1,7,8,2,6,0,9};
    for(auto e :a)
    {
        t.Insert(e);
    }
    t.InOrder();
    
    t.Erase(8);
    t.InOrder();
    
    t.Erase(5);
    t.InOrder();
    
    for(auto e: a){
        t.Erase(e);
        t.Inorder();
    }
    
    
    return 0;
}

递归版本

一般递归版本的为了接口的简洁性,再套一层,这样在外部调用就比较符合封装。

如果递归版本细节没想清除就画函数递归展开图。

查找

_FindR的查找:

private:
	Node* _FindR(Node* root , const K& key)
    {
        if(root == nullptr ) return nullptr;
        if(root -> _key < key) return _FindR(root-> _right ,key);
        else if( root-> _key > key)  return _FindR(root -> _left ,key);
        else return root;
    }
public:
	Node* FindR(const K& key){
        return _FindR( _root,key);
    }
插入

有什么办法将插入的结点和父亲链接起来呢?

  1. 一个法子就是在函数参数里面再加一个parent
  2. 或者在到递归边界的时候再使用Find查找父亲
  3. 把参数中的Node* root改成引用(比较好)
private:
	bool _InsertR(Node*& root ,const K& key){
         if( root == nullptr ){//插入
             root = new Node(key);
             return true;
         }
         if( root -> _key < key)
         {
             return _InsertR(root->_right);
         }
         else if( root -> _key > key){
             return _InsertR(root->_left);
         }
         else return false;
    }
public:
	bool InsertR( const K& key){
        return _InsertR(_root,key);
    }

image-20220122233847293

删除

递归版本

对于特征3来说,递归下去就可以找到特征1/2的情况,使用它们的处理方式,然后赋值。

由于传了引用,函数参数中就不是拷贝了,因此是想再原图上直接修改一样。

对于特征3,引用起不了作用。一种做法是使用非递归版中特征3的朴素做法。

private:
	bool _EraseR(Node* &root,const K& key)
    {
        if( root ==nullptr ) return false;
        if( root-> _key  < key){
            return _EraseR( root->_right,key);
        }
        else if( root -> _key > key){
            return _EraseR( root-> _left,key);
        }
        else {
            //特征1和特征2
            if(root ->_left==nullptr )
            {
                 Node* tmp = root;
                 root = root -> _right;
                 delete tmp;
            	 return true;
            }
            else if(root ->_right == nullptr)
            {
                 Node* tmp =root;
                 root = root ->_left;
                 delete tmp;
                 return true;
            }
            //特征3
            else{
                Node* minParent = root;
                Node* minRight = root -> _right;
                while(minRight -> _left) {minParent = minRight;minRight = minRight ->_left;}
                root->_key = minRight ->_key;//保存替换节点的值
                //删除替换的节点
                if(minParent -> _left == minRight){
                    minParent ->_left =minRight ->_right;
                }
                else minParent ->_right = minRight ->_right;
                delete minRight;
            }
            return true;
        }
    }
public:
	bool EraseR(const K&key){
         return _EraseR(root,key);
    }

另一种方式就是使用非递归中的特征3的递归处理。

private:
	bool _EraseR(Node* &root,const K& key)
    {
        if( root ==nullptr ) return false;
        if( root-> _key  < key){
            return _EraseR( root->_right,key);
        }
        else if( root -> _key > key){
            return _EraseR( root-> _left,key);
        }
        else {
            //特征1和特征2
            if(root ->_left==nullptr )
            {
                 Node* tmp = root;
                 root = root -> _right;
                 delete tmp;
            	 return true;
            }
            else if(root ->_right == nullptr)
            {
                 Node* tmp =root;
                 root = root ->_left;
                 delete tmp;
                 return true;
            }
            //特征3
            else{
                Node* MinRight = root ->_right;
                while(MinRight ->_left) MinRight= MinRight->_left;
                K min = MinRight-> _key;
                //转换成在root的右子树删除Min
                _EraseR(root->right,min);
                root-> _key =min;
            }
            return true;
        }
    }
public:
	bool EraseR(const K&key){
         return _EraseR(root,key);
    }

类的默认成员函数

析构

#include<iostream>
#include<string>
using namespace std;

template <typename K>
struct BSTreeNode
{
  public: 
    BSTreeNode* _left;
    BSTreeNode* _right;

    BSTreeNode(K key)
      :_left(nullptr),
      _right(nullptr),
      _key(key)
      { }
    K _key;
};

template <typename K>
class BSTree
{
  typedef BSTreeNode<K> Node;
  public:
      BSTree()
        :_root(nullptr)
      { }
      ~BSTree(){
          _Destroy(_root);
          _root=nullptr;
      }
  private:
      void _Destroy(Node* root){
           if(root == nullptr) return;
           if(root->_left ) _Destroy(root->_left);
           if(root->_right) _Destroy(root->_right);
           delete root;
      }
  private:
  Node* _root;
};

拷贝构造

先拷贝根,再拷贝左子树,再拷贝右子树。

#include<iostream>
#include<string>
using namespace std;

template <typename K>
struct BSTreeNode
{
  public: 
    BSTreeNode* _left;
    BSTreeNode* _right;

    BSTreeNode(K key)
      :_left(nullptr),
      _right(nullptr),
      _key(key)
      { }
    K _key;
};

template <typename K>
class BSTree
{
  typedef BSTreeNode<K> Node;
  public:
      BSTree()
        :_root(nullptr)
      { }
      ~BSTree(){
          _Destroy(_root);
          _root=nullptr;
      }
      BSTree(const BSTree<K>& t){
          _root = _Copy(t._root);
      }
  private:
      void _Destroy(Node* root){
           if(root == nullptr) return;
           if(root->_left ) _Destroy(root->_left);
           if(root->_right) _Destroy(root->_right);
           delete root;
      }
      Node* _Copy(Node* root){
            if(root == nullptr) return nullptr;
            Node* newnode = new Node(root ->_key);
            newnode->_left = _Copy( root ->_left);
            newnode->_right = _Copy(root -> _right);
          	return newnode;
      }
  private:
  Node* _root;
};

operator=赋值

operator=赋值建议采用现代写法,借助拷贝构造去做。

#include<iostream>
#include<string>
using namespace std;

template <typename K>
struct BSTreeNode
{
  public: 
    BSTreeNode* _left;
    BSTreeNode* _right;

    BSTreeNode(K key)
      :_left(nullptr),
      _right(nullptr),
      _key(key)
      { }
    K _key;
};

template <typename K>
class BSTree
{
  typedef BSTreeNode<K> Node;
  public:
      BSTree()
        :_root(nullptr)
      { }
      ~BSTree(){
          _Destroy(_root);
          _root=nullptr;
      }
      BSTree(const BSTree<K>& t){
          _root = _Copy(t._root);
      }
      BSTree<K>& operator=(BSTree<K> t)
      {
          swap(_root,t._root);
          return *this;
      }
  private:
      void _Destroy(Node* root){
           if(root == nullptr) return;
           if(root->_left ) _Destroy(root->_left);
           if(root->_right) _Destroy(root->_right);
           delete root;
      }
      Node* _Copy(Node* root){
            if(root == nullptr) return nullptr;
            Node* newnode = new Node(root ->_key);
            newnode->_left = _Copy( root ->_left);
            newnode->_right = _Copy(root -> _right);
          	return newnode;
      }
  private:
  Node* _root;
};

二叉搜索树的应用

K模型

K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

查找在不在的判断就是K模型

Key搜索场景:宿舍楼门禁,车库的扫描抬杆系统等。

比如宿舍楼门禁,可以将宿舍楼里面同学的学号都存储到BSTree<string>StuNumSet

比如车库的扫描抬杆系统,将小区业主车牌号录入系统,放到BSTree<string>CarNumSet

上述实现的代码就是K模型的。

KV模型

KV——(key-value)

KV搜索场景:

  1. 高铁站,网上买票,刷身份证进站。买的每张票都关联了一个人的身份证号。刷身份证进站,机器读取到的是身份证号码,系统要通过身份证号找到身份证号关联的车票,再看票的日期和车次信息,匹配才开门。
  2. 简单中英互翻字典
  3. 统计一个文本中每个单词出现的次数
#include<iostream>
#include<string>
using namespace std;

namespace KV
{
template <typename K,typename V>
struct BSTreeNode
{
  public: 
    BSTreeNode<K,V>* _left;
    BSTreeNode<K,V>* _right;

    BSTreeNode(const K& key,const V& value)
      :_left(nullptr),
      _right(nullptr),
      _key(key),
      _value(value)
      { }
    K _key;
    V _value;
};

template <typename K,typename V>
class BSTree
{
    typedef BSTreeNode<K,V> Node;
    public:
    BSTree()
        :_root(nullptr)
        { }
    //涉及深浅拷贝,需要实现拷贝构造 operator = 等
    ~BSTree(){
        _Destroy(_root);
        _root=nullptr;
    }
    BSTree(const BSTree<K>& t){
        _root = _Copy(t._root);
    }
    BSTree<K, V>& operator=(BSTree<K, V> t)
    {
        swap(_root, t._root);
        return *this;
    }
    
   	public:
	bool EraseR(const K&key){
         return _EraseR(root,key);
    }
	bool InsertR( const K& key,const V&value){
        return _InsertR(_root,key,value);
    }  
    Node* FindR(const K& key){
        return _FindR( _root,key);
    } 
    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }
  	private:
	bool _InsertR(Node*& root ,const K& key,const V& value){
         if( root == nullptr ){//插入
             root = new Node(key,value);
             return true;
         }
         if( root -> _key < key)
         {
             return _InsertR(root->_right);
         }
         else if( root -> _key > key){
             return _InsertR(root->_left);
         }
         else return false;
    }

	Node* _FindR(Node* root , const K& key)
    {
        if(root == nullptr ) return nullptr;
        if(root -> _key < key) return _FindR(root-> _right ,key);
        else if( root-> _key > key)  return _FindR(root -> _left ,key);
        else return root;
    }

	bool _EraseR(Node* &root,const K& key)
    {
        if( root ==nullptr ) return false;
        if( root-> _key  < key){
            return _EraseR( root->_right,key);
        }
        else if( root -> _key > key){
            return _EraseR( root-> _left,key);
        }
        else {
            //特征1和特征2
            if(root ->_left==nullptr )
            {
                 Node* tmp = root;
                 root = root -> _right;
                 delete tmp;
            	 return true;
            }
            else if(root ->_right == nullptr)
            {
                 Node* tmp =root;
                 root = root ->_left;
                 delete tmp;
                 return true;
            }
            //特征3
            else{
                Node* MinRight = root ->_right;
                while(MinRight ->_left) MinRight= MinRight->_left;
                K min = MinRight-> _key;
                V value = MinRight->_value;
                //转换成在root的右子树删除Min
                _EraseR(root->right,min);
                root-> _key =min;
                root->_value = value;
            }
            return true;
        }
    }
    void _Destory(Node* root)
	{
			if (root == NULL)
			{
				return;
			}

			_Destory(root->_left);
			_Destory(root->_right);
			delete root;
	}
	Node* _Copy(Node* root)
    {
        if (root == nullptr)
        {
            return nullptr;
        }

        Node* copyNode = new Node(root->_key, root->_value);
        copyNode->_left = _Copy(root->_left);
        copyNode->_right = _Copy(root->_right);

        return copyNode;
    }
    void _InOrder(Node* root)
    {
        if (root == nullptr)
            return;

        _InOrder(root->_left);
        cout << root->_key << ":"<<root->_value<<endl;
        _InOrder(root->_right);
    }
  	private:
  	Node* _root;
};
}

简单地测试一下简单字典的实例

void TestBSTree()
{
    KV::BSTree<string,string> dict;
    dict.InsertR("apple","苹果");
    dict.InsertR("banana","香蕉");
    dict.InsertR("candy","糖果");
    dict.InsertR("duck","鸭子");
    
    string str;
    while(cin>>str){
        KV::BSTreeNode<string,string> *ret = dict.FindR(str);
        if(ret == nullptr) {
            cout<<"单词拼写错误"<<endl;
        }
        else{
            cout<<string<<" 中文翻译为 "<<ret->_value<<endl;
        }
    }
}

测试一下统计单词出现次数的实例

void TestBSTree()
{
    string arr[]={"aaa","bb","cc","cc","bb","aaa","aaa","aaa","d"};
    KV::BSTree<string,int> countTree;
    for(const auto& str:arr)
    {
        //先查找在不在搜索树
        //不在说明要插入
        auto ret = countTree.FindR(str);
        if(ret == nullptr){
            coutTree.InsertR(str,1);
        }
        else{
            ret->value ++;
        }
    }
    
}
举报

相关推荐

0 条评论