【数据结构】二叉搜索树的插入,删除,查找等基本操作的实现

1、基本概念

二叉搜索树:又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树

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

特点:

  • 二叉搜索树的最左子树存放最小的值,最右子树存放最大的值;
  • 二叉搜索树如果按中序遍历的话,得到的数列是有序的;

2、基本操作分析

线索化二叉树的基本操作

【普通版】

查找:

根据二叉搜索树的特点,我们要查找一个数,可以进行以下操作:

  1. 从根节点开始遍历,如果为空树,返回false;如果不为空树,则令要查找的值与根值进行比较;
  2. 如果等于根值,则返回true;
  3. 如果大于根值,进入该树的右子树;
  4. 如果小于根值,进入该树的左子树;
  5. 重复以上步骤,直到不满足循环条件;

插入:

这个也是依据二叉搜索树的特性来看的,与查找很相似

  • 判断是否为空树,若为空,则直接给该树的根节点赋值,并返回true;
  • 若不为空,则开始查找要插入元素的位置。若该值小于根值,进入该数的左子树;
  • 若该值大于根值,进入该数的右子树;
  • 不满足条件退出循环,表示找到要插入的位置了;
  • 如果要插入的值小于标记的值,就把该值放到标记的左子树里;
  • 如果要插入的值大于标记的值,就把该值放到标记的右子树里;

【注】:标记是在查找循环里用来标记当前节点的上一节点的;因为循环条件的原因,退出循环时,当前节点肯定为空,而标记就是要插入值的根节点了。

删除:

  • 判断该树是否为空,若为空树,则返回错误;
  • 查找将要删除节点所在位置
  • 删除节点

删除节点则要考虑以下方面:

《【数据结构】二叉搜索树的插入,删除,查找等基本操作的实现》

待删除节点是叶子节点&待删除节点只有右孩子:

《【数据结构】二叉搜索树的插入,删除,查找等基本操作的实现》

《【数据结构】二叉搜索树的插入,删除,查找等基本操作的实现》

《【数据结构】二叉搜索树的插入,删除,查找等基本操作的实现》

待删除节点是叶子节点&待删除节点只有左孩子:

与上一情况(待删除节点是叶子节点&待删除节点只有右孩子)差不多,这里不做过多说明;

待删除节点左右孩子均存在:

《【数据结构】二叉搜索树的插入,删除,查找等基本操作的实现》

《【数据结构】二叉搜索树的插入,删除,查找等基本操作的实现》

3、源码

【普通版】

BSTree.h

#pragma once
#ifndef __BSTREE_H__
#define __BSTREE_H__
#include<iostream>
#include<assert.h>

using namespace std;

template<class T>
struct BSTreeNode
{
public:
    BSTreeNode(const T& data)
        :_data(data)
        , _pLeft(NULL)
        , _pRight(NULL)
    {}

    BSTreeNode()
        :_pLeft(NULL)
        , _pRight(NULL)
    {}

public:
    BSTreeNode<T>* _pLeft;
    BSTreeNode<T>* _pRight;
    T _data;
};

template<class T>
class BSTree
{
public:
    typedef BSTreeNode<T> Node;
    typedef Node* pNode;

    BSTree()
        :_pRoot(NULL)
    {}

    BSTree(pNode node)
        :_pRoot(node)
    {}

    //查找
    bool Find(const T& data){
        if (NULL == _pRoot)
            return false;
        pNode pCur = _pRoot;
        while (pCur){
            if (data == pCur->_data)
                return true;
            else if (data > pCur->_data)
                pCur = pCur->_pRight;
            else
                pCur = pCur->_pLeft;
        }
        return false;
    }

    //插入
    bool Insert(const T& data){
        //要插入的为空树
        if (NULL == _pRoot){
            _pRoot = new Node(data);
            return true;
        }
        //查找要插入的位置
        pNode pCur = _pRoot;
        pNode pParent = NULL;
        while (pCur){
            if (data == pCur->_data)//树中已经存在该值
                return false;
            else if (data < pCur->_data){
                pParent = pCur;
                pCur = pCur->_pLeft;
            }
            else{
                pParent = pCur;
                pCur = pCur->_pRight;
            }
        }
        //进行插入操作
        if (data < pParent->_data)
            pParent->_pLeft = new Node(data);
        else
            pParent->_pRight = new Node(data);
        return true;
    }

    //删除操作
    bool Delete(const T& data){

        //判断树是否存在
        if (NULL == _pRoot)
            return false;

        //查找要删除的节点
        pNode pCur = _pRoot;
        pNode pParent = NULL;
        pNode pDel = NULL;
        while (pCur){
            if (data > pCur->_data){
                pParent = pCur;
                pCur = pCur->_pRight;
            }
            else if (data < pCur->_data){
                pParent = pCur;
                pCur = pCur->_pLeft;
            }
            else{
                //删除节点

                //待删除节点是叶子节点 & 当前节点只有右孩子
                if (NULL == pCur->_pLeft){
                    pDel = pCur;

                    if (NULL == pParent){
                        //待删除节点是根节点
                        _pRoot = pDel->_pRight;
                    }
                    else if (pDel == pParent->_pLeft){
                        //待删除节点是上一节点的左子树
                        pParent->_pLeft = pDel->_pRight;//待删除节点的左子树一定为空
                    }
                    else{
                        //待删除节点是上一节点的右子树
                        pParent->_pRight = pDel->_pRight;
                    }
                }
                else if (NULL == pCur->_pRight){
                    //待删除节点是叶子节点 & 当前节点只有左孩子
                    pDel = pCur;
                    if (NULL == pParent){
                        //待删除节点是根节点
                        _pRoot = pDel->_pLeft;
                    }
                    else if (pDel == pParent->_pLeft){
                        //待删除节点是上一节点的左子树
                        pParent->_pLeft = pDel->_pLeft;
                    }
                    else{
                        //待删除节点是上一节点的右子树
                        pParent->_pRight = pDel->_pLeft;
                    }
                }
                else{
                    //待删除节点左右子树均存在

                    pNode subRight = pCur->_pRight;
                    pParent = pCur;
                    //parent 在这里不能为空,否则在特定场景下判断subRight与上一节点的关系时会崩溃

                    //查找待删除节点的右子树的最小值(左子树)节点
                    while (subRight->_pLeft){
                        pParent = subRight;
                        subRight = subRight->_pLeft;
                    }
                    pDel = subRight;//要删除的节点

                    //交换待删除节点和它的右子树的值,再直接删除它的右子树节点
                    pCur->_data = pDel->_data;

                    //还需判断pDel是pParent的左节点还是右节点
                    //parent 在这里不能为空的原因,↓
                    if (pDel == pParent->_pLeft)
                        pParent->_pLeft = pDel->_pLeft;
                    else
                        pParent->_pRight = pDel->_pRight;
                }
                delete pDel;
                return true;
            }
        }

        return false;

    }

    //中序遍历
    void InOrder(){
        _InOrder(_pRoot);
        cout << endl;
    }

    //析构
    ~BSTree(){
        _Destory(_pRoot);
    }

protected:

    void _InOrder(pNode pRoot){
        if (NULL == pRoot)
            return;
        _InOrder(pRoot->_pLeft);
        cout << pRoot->_data << " ";
        _InOrder(pRoot->_pRight);
    }

    void _Destory(pNode pRoot){
        if (NULL == pRoot)
            return;
        _Destory(pRoot->_pLeft);
        _Destory(pRoot->_pRight);
        delete pRoot;
    }

private:
    pNode _pRoot;
};

#endif //__BSTREE_H__

Test.c

#include"BSTree.h"
#include<Windows.h>
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

//测试二叉搜索树
void test2(){
    int arr[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
    BSTree<int> BSTree;
    //创建二叉搜索树
    for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i){
        BSTree.Insert(arr[i]);
    }
    BSTree.InOrder();//正确的搜索二叉树的中序遍历一定是有序的
    //检测查找6,9,2
    if (BSTree.Find(6))
        cout << "找到了6" << endl;
    else
        cout << "没找到6" << endl;

    if (BSTree.Find(9))
        cout << "找到了9" << endl;
    else
        cout << "没找到9" << endl;

    if (BSTree.Find(2))
        cout << "找到了2" << endl;
    else
        cout << "没找到2" << endl;

    //检测删除元素
    BSTree.Delete(4);
    BSTree.InOrder();
    BSTree.Delete(5);
    BSTree.InOrder();
}

int main(){
    test2();
    system("pause");
    return 0;
}

【程序运行结果】

《【数据结构】二叉搜索树的插入,删除,查找等基本操作的实现》

    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/sofia_m/article/details/79528594
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞