数据结构--二叉搜索树1【菜鸟学习日记】

今天学习一下非常有用二叉搜索树!!!
首先看看什么是二叉搜索树,需要满足哪行性质

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

说起来,这种树有什么优点呢?
这里我们要谈到以前学习到 有序的数组无序的链表 这两种结构
数组     可高效的查找   
链表     可灵活的插入
但它们都有自身的不足,而二叉搜索树可以将它们俩的优点结合,实现高效的查找和插入删除

初步了解了一下二叉搜索树,那就着手写一颗这样的树
一、二叉树搜索树的创建与插入

这里写图片描述
首先我们明确树的节点里放关键字key,以及左指针右指针
然后我们想一下算法的思路(插入节点)
1、若为空树,则key作为树的根节点
2、若非空,则key与根节点进行比较
如果key等于根节点,则停止插入;
如果key小于根节点,则向左子树走;
如果key大于根节点,则向右子树走;

#include<iostream>
using namespace std;

template<class K>
class BinarySearchTree
{
    typedef TreeNode<K> Node;
public:
    BinarySearchTree()
        :_root(NULL)
    {}

    //非递归版插入
    bool Insert(const K& key)
    {
        //若为空,拿key创建一个新节点作为跟
        if (_root == NULL)
        {
            _root = new Node(key);
            return true;
        }

        Node* parent = NULL;//需要一个父节点指针,记录
        //方便最后走到空时,能找到父节点进行插入
        Node* cur = _root;
        while (cur)
        {
            if (key<cur->_key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else if (key>cur->_key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else
            {
                break;
            }
        }
        //走到空了,进行插入节点
        //判断左插,还是右插
        //要插入的值小于父节点,插入父节点左边
        if (key < parent->_key)
        {
            parent->_left = new Node(key);
        }
        else if (key>parent->_key)
        {
            parent->_right = new Node(key);
        }
        else
        {
            //值相等,插入失败
            return false;
        }
}

private:
    Node* _root;
};

写好了,插入节点试试

void testBinarySearchTree()
{
    BinarySearchTree<int> tree;
    tree.Insert(5);
    tree.Insert(4);
    tree.Insert(3);
}

这里写图片描述
可以看到已经插入成功了

递归版本
    bool _InsertR(Node*& root, const K& key)
    {
        if (root == NULL)
        {
            root = new Node(key);
            return true;
        }

        if (key < root->_key)
        {
            return _InsertR(root->_left, key);
        }
        else if (key>root->_key)
        {
            return _InsertR(root->_right, key);
        }
        else
            return false;
    }

    bool InsertR(const K& key)
    {
        _InsertR(_root, key);
        return true;
    }

二、节点删除

我们要删除的节点分为三大类:1、叶子节点(我们可以把这种类型归为第二种情况,叶子结点看为左为空或者右为 空的情况) 2、删除节点的左为空或有为空 3、删除节点左右都不为空

这里我们图示了两种左为空的情况,右为空同理

这里写图片描述

特殊情况

这里写图片描述
当要删的为根节点时,直接将右节点给根即可
还有我们要注意这种情况下的parent节点


然后我们再来考虑左右都不为空的情况

这种情况我们就要考虑怎么删了,我们发现当左右都不为空时,我们很难删除这个节点,我们删除这个节点就会牵扯到其他节点改变,可以说“牵一发而动全身”,所以我这里用了一种之前学习过的“假删”,替换法,我们不真删这个节点而是用一个其他节点的值将其进行替换,看起来像删除了
问题一:找谁替换?
这里写图片描述
删5,可以找4或者6替换
这里写图片描述
删1,可以用2替换;
删3,可以用2或者4替换

我们可以发现就是要删除节点的左子树的最大值(左子树的最右节点),或者右子树的最小值(右子树的最左节点)

找到了替换节点,那再来分析都有哪些情况?
这里我们以找右子树的最左节点
这里写图片描述
我们既然要找最左节点,那我们就要考虑有没有左节点,其实就是考虑右子树的最小是有两种情况
如图,我们要删cur,那我们就要找以subRight为根子树的最左节点,可是subRight有可能就没有左节点,(subRight的值就是当前右子树的最小值)那我们就可以直接用subRight的值对cur的值进行替换
然后我们就要考虑这两种情况对于替换后,节点的删除
这里写图片描述
其实就是我们分别删除5和删除7的两种情况
图示删7的情况
这里写图片描述

以上我们分析完了所有情况,将其整理,就可以用代码进行实现了


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值