C++一步一步实现二叉搜索树

本文通过《STL源码剖析》关联容器章节,介绍了如何一步步实现二叉搜索树,包括节点插入、建立、中序遍历、查找、删除及销毁等功能。文章详细解释了每个操作的逻辑,并提供了完整的C++代码实现。此外,还通过一道题目验证了二叉搜索树的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习《STL源码剖析》关联容器的章节,map,set等关联容器的底层实现都是红黑树,本着循序渐进的思想,先实现二叉搜索树。读懂了二叉搜索树的定义后,实现了二叉搜索树的基本功能。文章属原创,代码全手打,欢迎指正讨论。首先给出类的定义,如下:

#include<iostream>
#include<string>
#include<cstring>
#include<sstream>
//#include<>
using namespace std;
class BST
{
private:
    //嵌套类,仅在BST中有效
    class Node
    {
    public:
        int val;
        Node *left;
        Node *right;
        Node *parent; //增加父节点,在删除节点时父节点很有用

        Node()
        {
            val = 0;
            left = right = parent = NULL;
        }
    };
    Node *m_root;      //存储根节点
    Node *m_cur;       //存储查找的结果  
    int   m_size;      //存储树的大小

    void SetNode(Node* cur,Node* newNode);           //设置节点的父子关系
    bool CompareTree(const Node *r1,Node* r2) const;
public:
    bool InsertNode(const int val);                  //插入节点
    bool FindNode(const int val);                    //找到返回true,val所在的位置就是pos
    bool DelNode(const int val);                     //删除节点
    void Inorder(Node *p, string &str) const;        //中序遍历输出参数可自定义
    void DestroyTree(Node *p);                       //销毁整棵二叉树,模仿后序遍历  
    bool operator==(BST &t) const;                   //用于判断两个BST是否相等
    void CreateBST(const string &str);

    Node* GetRootNode() const
    {
        return m_root;
    }
    BST() :m_root(NULL), m_cur(NULL),m_size(0){}
    ~BST()
    {
        DestroyTree(m_root);
    }
};

一、实现节点插入功能,此处是构建二叉搜索树的基本操作。插入节点时需满足的条件有二:
1、左子树小于父节点。
2、右子树不小于父节点。
以下是详细实现代码,以下代码虽不是最简短的写法,但是但是能想到的思路最清晰的写法,适合用于学习理解二叉搜索树。

bool BST::InsertNode(const int val)
{
    Node *p = new Node();
    p->val = val;
    m_size++;
    if (m_root == NULL)
    {
        m_root = p;
        return true;
    }
    Node * cur = m_root;
    Node * last = m_root;
    bool lflag = false;  //为true说明是左子树
    while (cur != NULL)
    {
        last = cur;
        if (val >= cur->val)
        {
            cur = cur->right;
            lflag = false;
        }
        else
        {
            cur = cur->left;
            lflag = true;
        }
    }

    //建立父节点与子树的关系
    cur = p;
    cur->parent = last;
    lflag == true ? last->left = cur : last->right = cur;

    return true;
}

二、建立二叉搜索树。此处只需要循环调用上面实现的InsertNode方法即可。

void BST::CreateBST(const string &str)
{
    stringstream ss;
    int val;
    for (int i = 0; i < str.size(); i++)
    {
        ss << str[i];
        ss >> val;
        ss.clear();//多次转换需要清空
        InsertNode(val);
    }
}

三、中序遍历二叉搜索树。二叉搜索树中序遍历的结果可得到递增的有序序列,从此处可以理解为BST查找的平均复杂度是O(logn),因为其查找过程类似于二分查找。为了方便输出查看,将输出结果放在string中。中序遍历实现方法有两种,其一为采用递归的方式;其二为使用栈来辅助实现。此处给出递归版本,今后再补充给出非递归版本。

void BST::Inorder(Node *p, string &st) const
{
    if (p == NULL)
    {
        return;
    }
    Inorder(p->left,st);
    stringstream ss;
    string t;
    ss << p->val;
    ss >> t;
    st += t;
    Inorder(p->right, st);
}

四、在二叉搜索树中查找节点。二叉排序树的查找操作,平均复杂度为O(logn),最差为O(n)。而平衡二叉搜索树能很好的处理最坏情况。常见的平衡二叉搜索树:红黑树等

bool BST::FindNode(const int val)
{
    if (m_root == NULL)
    {
        return false;
    }
    bool ret = false;
    Node * cur = m_root;
    while (cur != NULL)
    {
        if (cur->val == val)
        {
            m_cur = cur;
            ret = true;
            break;
        }
        else if (val > cur->val)
        {
            cur = cur->right;
        }
        else
        {
            cur = cur->left;
        }
    }
    return ret;
}

五、二叉搜索树删除节点。二叉搜索树不能只是简单的删除某个节点,应该保证BST删除元素后仍然符合二叉搜索树的定义。删除分为三种情况:
1、左子树和右子树均不为空,那么就从右子树中找到一个最小值来替换。
2、仅有一个单个子树,那就将当前元素删除即可
3、没有子树
在编码时一定要注意根节点是特殊情况,根节点没有父节点。
此处代码分为两个步骤,首先找到要替代节点(倘若有),然后替换掉原来的节点。

bool BST::DelNode(const int val)
{
    if (m_root == NULL)
    {
        return false;
    }
    m_size--;
    if (!FindNode(val))
    {
        return false;
    }
    //难点:找到叶子节点后如何处理父节点,防止父节点中出现野指针
    //为方便操作,而返回去增加定义父指针
    if (m_cur->left != NULL && m_cur->right != NULL)//有两个子树
    {
        Node * min = m_cur->right;  
        while (min->left != NULL)
        {
            min = min->left;
        }
        m_cur->right == min ? min->parent->right = NULL : min->parent->left = NULL;
        SetNode(m_cur,min);
        delete m_cur;
        m_cur = NULL;
    }
    else if (m_cur->left != NULL || m_cur->right != NULL)//仅有一个子树
    {
        if (m_cur->left != NULL)
        {
            SetNode(m_cur, m_cur->left);
            delete m_cur;
            m_cur = NULL;
        }
        else
        {
            SetNode(m_cur, m_cur->right);
            delete m_cur;
            m_cur = NULL;
        }
    }
    else //叶子节点、无子树的根节点直接删除
    {
        SetNode(m_cur, NULL);
        delete m_cur;
        m_cur = NULL;
    }
    return false;
}

DelNode方法中调用的SetNode代码实现如下,主要作用是重新设定原来节点周围的父子关系。
总共要设定五条关系
1、当前节点与父节点之间的关系(单边1条)
2、与左子树之间的关系(双边2条)
3、与右子树之间的关系(双边2条)

void BST::SetNode(Node* cur, Node* newNode)
{
    //与父节点
    if (cur->parent != NULL)
    {
        if (cur->parent->left == cur)
        {
            cur->parent->left = newNode;
        }
        else
        {
            cur->parent->right = newNode;
        }
    }
    else
    {
        m_root = newNode;  //父节点为空,说明为根节点
    }
    //与左子树
    if (cur->left != NULL)
    {
        //子->父
        cur->left->parent = newNode;
        //父->子
        if (newNode != cur->left)
        {
            newNode->left = cur->left;
        }
        else
        {
            //不操作
        }

    }
    //右子树
    if (cur->right != NULL)
    {
        //子->父
        cur->right->parent = newNode;
        if (newNode != cur->right)
        {
            newNode->right = cur->right;
        }
        else
        {
            //不操作
        }
    }
}
六、二叉树拓扑结构比较。为了比较两个二叉树是否相同,可采用递归的方式实现,代码如下:
bool BST::CompareTree(const Node *r1,Node* r2) const
{
    if (r1 == NULL && r2 == NULL)
    {
        return true;
    }
    if (r1 == NULL || r2 == NULL)
    {
        return false;
    }
    if (r1->val == r2->val)
    {
        if (CompareTree(r1->left, r2->left) && CompareTree(r1->right, r2->right))
        {
            return true;
        }
    }
    return false;
}
bool BST::operator==(BST &t) const
{
    return CompareTree(GetRootNode(),t.GetRootNode());
}

七、二叉搜索树的销毁。所有节点均使用的new获取的堆空间,因此必须手动释放,否则会造成内存泄漏。释放的方法模仿后序遍历实现。

void BST::DestroyTree(Node *p)
{
    if (p == NULL)
    {
        return;
    }
    DestroyTree(p->left);
    DestroyTree(p->right);
    delete p;
}

现在二叉搜索树已经实现,可以通过一道题目来练习。
题目描述
判断两序列是否为同一二叉搜索树序列
输入描述:
开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束。
接下去一行是一个序列,序列长度小于10,包含(0~9)的数字,没有重复数字,根据这个序列可以构造出一颗二叉搜索树。
接下去的n行有n个序列,每个序列格式跟第一个序列一样,请判断这两个序列是否能组成同一颗二叉搜索树。

输出描述:
如果序列相同则输出YES,否则输出NO

输入例子:
2
567432
543267
576342
0

输出例子:
YES
NO

整个工程的全部代码如下:

#include<iostream>
#include<stack>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<cctype>
#include<string>
#include<cstring>
#include<sstream>
//#include<>
using namespace std;

class BST
{
private:
    //嵌套类,仅在BST中有效
    class Node
    {
    public:
        int val;
        Node *left;
        Node *right;
        Node *parent; //增加父节点,在删除节点时父节点很有用

        Node()
        {
            val = 0;
            left = right = parent = NULL;
        }
    };
    Node *m_root; //存储根节点
    Node *m_cur;    //存储查找的结果  
    int m_size;   //存储树的大小

    void SetNode(Node* cur,Node* newNode); //设置节点的父子关系
    bool CompareTree(const Node *r1,Node* r2) const;
public:
    bool InsertNode(const int val);                  //插入节点
    bool FindNode(const int val);   //找到返回true,val所在的位置就是pos
    bool DelNode(const int val);                     //删除节点
    void Inorder(Node *p, string &str) const;        //中序遍历输出参数可自定义
    void DestroyTree(Node *p);                       //销毁整棵二叉树,模仿后序遍历  
    bool operator==(BST &t) const;                    //用于判断两个BST是否相等
    void CreateBST(const string &str);

    Node* GetRootNode() const
    {
        return m_root;
    }
    BST() :m_root(NULL), m_cur(NULL),m_size(0){}
    ~BST()
    {
        DestroyTree(m_root);
    }
};
//二叉排序树的查找操作,平均复杂度为O(logn),最差为O(n)。而平衡二叉搜索树能很好的
//处理最坏情况。常见的平衡二叉搜索树:红黑树等
bool BST::FindNode(const int val)
{
    if (m_root == NULL)
    {
        return false;
    }
    bool ret = false;
    Node * cur = m_root;
    while (cur != NULL)
    {
        if (cur->val == val)
        {
            m_cur = cur;
            ret = true;
            break;
        }
        else if (val > cur->val)
        {
            cur = cur->right;
        }
        else
        {
            cur = cur->left;
        }
    }
    return ret;
}

bool BST::InsertNode(const int val)
{
    Node *p = new Node();
    p->val = val;
    m_size++;
    if (m_root == NULL)
    {
        m_root = p;
        return true;
    }

    Node * cur = m_root;
    Node * last = m_root;
    bool lflag = false;
    while (cur != NULL)
    {
        last = cur;
        if (val >= cur->val)
        {
            cur = cur->right;
            lflag = false;
        }
        else
        {
            cur = cur->left;
            lflag = true;
        }
    }
    cur = p;
    cur->parent = last;
    lflag == true ? last->left = cur : last->right = cur;
    return true;
}

void BST::CreateBST(const string &str)
{
    stringstream ss;
    int val;
    for (int i = 0; i < str.size(); i++)
    {
        ss << str[i];
        ss >> val;
        ss.clear();//多次转换需要清空
        InsertNode(val);
    }
}

/*
总共要设定五条关系
1、当前节点与父节点之间的关系(单边1条)
2、与左子树之间的关系(双边2条)
3、与右子树之间的关系(双边2条)
*/

void BST::SetNode(Node* cur, Node* newNode)
{
    //与父节点
    if (cur->parent != NULL)
    {
        if (cur->parent->left == cur)
        {
            cur->parent->left = newNode;
        }
        else
        {
            cur->parent->right = newNode;
        }
    }
    else
    {
        m_root = newNode;  //父节点为空,说明为根节点
    }

    //与左子树
    if (cur->left != NULL)
    {
        //子->父
        cur->left->parent = newNode;

        //父->子
        if (newNode != cur->left)
        {
            newNode->left = cur->left;
        }
        else
        {
            //不操作
        }

    }

    if (cur->right != NULL)
    {
        //子->父
        cur->right->parent = newNode;
        if (newNode != cur->right)
        {
            newNode->right = cur->right;
        }
        else
        {
            //不操作
        }
    }

}
//二叉搜索树不能只是简单的删除某个节点,应该保证删除元素后
//仍然符合二叉搜索树的定义
/*
1、左子树和右子树均不为空,那么就从右子树中找到一个最小值来替换。
2、仅有一个单个子树或者没有子树,那就将当前元素删除即可
*/
bool BST::DelNode(const int val)
{
    if (m_root == NULL)
    {
        return false;
    }
    m_size--;
    if (!FindNode(val))
    {
        return false;
    }
    //难点:找到叶子节点后如何处理父节点,防止父节点中出现野指针
    //为方便操作,而返回去增加定义父指针
    if (m_cur->left != NULL && m_cur->right != NULL)
    {
        Node * min = m_cur->right;  
        while (min->left != NULL)
        {
            min = min->left;
        }
        m_cur->right == min ? min->parent->right = NULL : min->parent->left = NULL;
        SetNode(m_cur,min);
        delete m_cur;
        m_cur = NULL;
    }
    else if (m_cur->left != NULL || m_cur->right != NULL)
    {
        if (m_cur->left != NULL)
        {
            SetNode(m_cur, m_cur->left);
            delete m_cur;
            m_cur = NULL;
        }
        else
        {
            SetNode(m_cur, m_cur->right);
            delete m_cur;
            m_cur = NULL;
        }
    }
    else //叶子节点、无子树的根节点直接删除
    {
        SetNode(m_cur, NULL);
        delete m_cur;
        m_cur = NULL;
    }
    return false;
}

bool BST::CompareTree(const Node *r1,Node* r2) const
{
    if (r1 == NULL && r2 == NULL)
    {
        return true;
    }
    if (r1 == NULL || r2 == NULL)
    {
        return false;
    }
    if (r1->val == r2->val)
    {
        if (CompareTree(r1->left, r2->left) && CompareTree(r1->right, r2->right))
        {
            return true;
        }
    }
    return false;
}
bool BST::operator==(BST &t) const
{
    return CompareTree(GetRootNode(),t.GetRootNode());
}
void BST::Inorder(Node *p, string &st) const
{
    if (p == NULL)
    {
        return;
    }
    Inorder(p->left,st);
    stringstream ss;
    string t;
    ss << p->val;
    ss >> t;
    st += t;
    Inorder(p->right, st);
}

void BST::DestroyTree(Node *p)
{
    if (p == NULL)
    {
        return;
    }
    DestroyTree(p->left);
    DestroyTree(p->right);
    delete p;
}
int main()
{
    //注释掉的为原始测试代码
    //BST tree;
    //BST tree1,tree2;
    //string str = "567432";// "567043289";
    //string str1 = "543267";
    //string str2 = "576342";
    //tree.CreateBST(str);
    //tree1.CreateBST(str1);
    //tree2.CreateBST(str2);
    //if (tree2 == tree)
    //{
    //  cout << "same tree!!" << endl;
    //}
    //str.clear();
    //tree.Inorder(tree.GetRootNode(),str);
    //cout << str << endl;
    //if(tree.FindNode(4))
    //{
    //  cout << "I find it !!" << endl;
    //}
    //else
    //{
    //  cout << "I find nothing!" << endl;
    //}

    //tree.DelNode(4);

    //if (tree.FindNode(4))
    //{
    //  cout << "I find it !!" << endl;
    //}
    //else
    //{
    //  cout << "I find nothing!" << endl;
    //}
    //str.clear();
    //tree.Inorder(tree.GetRootNode(), str);
    //cout << str << endl;

    int n = 0;
    string str;
    while (cin >> n >> str)
    {
        BST tree;
        tree.CreateBST(str);
        while (n--)
        {
            string match;
            cin >> match;
            BST treeMatch;
            treeMatch.CreateBST(match);
            if (tree == treeMatch)
            {
                cout << "YES" << endl;
            }
            else
            {
                cout << "NO"<< endl;
            }
        }
    }
    return 0;
}

写在最后:完整的实现了二叉搜索树后回过来看,在实现删除功能时可以不增加父指针,增加的指针带来了替换操作的复杂度上升。直接导致这部分代码需要更多的时间调试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值