二叉搜索树笔记分享

二叉搜索树的概念

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

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

性质:它的中序遍历是有序的。

二叉搜索树的模拟实现

1.先创建节点

template<class K>
struct BSTreeNode
{
    typedef BSTreeNode<K> Node;
    Node* left = nullptr;
    Node* right = nullptr;
    K _key;
};
template<class K>
class BSTree
{
public:
    typedef BSTreeNode<K> Node;

2.查找

bool Find(const K& k)
    {
        Node* cur = cur;
        while (cur)
        {
            if (k < cur->_key)
            {
                cur = cur->left;
            }
            else if (k > cur->_key)
            {
                cur = cur->right;
            }
            else
            {
                return true;
            }
        }
        return false;
    }
 
 /递归版本(因为传参问题,把他写进private里,外部封装它)
 bool FindR(const K& k)
{
        return _FindR(_root, k);
}

 bool _FindR(Node* root, const K& k)
{
    if (root == nullptr)
    {
        return false;
    }
    else if (root->_key > k)
    {
        return _FindR(root->left, k);
    }
    else if (root->right < k)
    {
        return _FindR(root->right, k);
    }
    else
    {
        return true;
    }
}

3.插入

bool Insert(const K& k)
{
    Node* cur = _root;
    Node* parent = nullptr;
    if (_root == nullptr)
    {
        _root = new Node(k);
        return true;
    }

    while (cur)
    {
        if (k < cur->_key)
        {
            parent = cur;
            cur = cur->left;
        }
        else if (k > cur->_key)
        {
            parent = cur;
            cur = cur->right;
        }
        else
        {
            return false;
        }
    }
    cur = new Node(k);
    if (parent->_key < k)
    {
        parent->right = cur;
    }
    else if (parent->_key > k)
    {
        parent->left = cur;
    }
    return true;
}

 /递归版本(因为传参问题,把他写进private里,外部封装它)

 bool InsertR(const K& k)
{
    return _InsertR(_root, k);
}    
//这里引用传参很关键
bool _InsertR(Node*& root, const K& k)
    {
        if (root == nullptr)
        {
            root = new Node(k);
            return true;
        }

        else if (root->_key > k)
        {
            return _InsertR(root->left, k);
        }
        else if (root->_key < k)
        {
            return _InsertR(root->right, k);
        }
        else
        {
            return false;
        }
    }

需要注意的是:必须多来个节点能记录当前节点的上一个节点,否则插入的节点无法链接到树里。

4.中序遍历

这样写的话,外面调不到,因为InOrder必须得传参,而根节点是private的。

解决:

  1. 写一个getroot(),再传参
  2. 封装,如下:可以做到在内部把参数传了,在外面调用无参的就行。

void _InOrder(Node* root) { if (root == nullptr) return; _InOrder(root->left); cout << root->_key << " "; _InOrder(root->right); } void InOrder() { _InOrder(_root); cout << endl; }

5.删除

小心,有多种情况在内,图解在pdf文件中。

bool Erase(const K& k)
{
    if (_root == nullptr)
    {
        return false;
    }
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur)
    {
        if (k < cur->_key)
        {
            parent = cur;
            cur = cur->left;
        }
        else if (k > cur->_key)
        {
            parent = cur;
            cur = cur->right;
        }
        else
        {
            //找到了,现在删除它,并保持二叉搜索树的结构
            if (cur->left == nullptr && cur->right == nullptr)
            {
                delete cur;
            }
            else if (cur->left && cur->right == nullptr)
            {
                if (cur == _root)
                {
                    _root = cur->left;
                    delete cur;
                }
                else
                {
                    if (cur == parent->right)
                    {
                        parent->right = cur->left;
                    }
                    else if (cur == parent->left)
                    {
                        parent->left = cur->left;
                    }
                }
                delete cur;
                return true;
            }
            else if (cur->left == nullptr && cur->right)
            {
                if (cur == _root)
                {
                    _root = cur->right;
                    delete cur;
                }
                else
                {
                    if (cur == parent->right)
                    {
                        parent->right = cur->right;
                    }
                    else if (cur == parent->left)
                    {
                        parent->left = cur->right;
                    }
                }
                delete cur;
                return true;
            }
            else
            {
                //cur的左右都不为空,替换法删除
                Node* Rightmin = cur->right;
                Node* Rightminparent = cur;//不能置nullptr,如果cur是根节点呢?
                while (Rightmin->left)
                {
                    Rightminparent = Rightmin;
                    Rightmin = Rightmin->left;
                }
                cur->_key = Rightmin->_key;
                //现在删Rightmin
                if (Rightmin == Rightminparent->left)
                {
                    Rightminparent->left = Rightmin->right;
                }
                else if (Rightmin == Rightminparent->right)
                {
                    Rightminparent->right = Rightmin->right;
                }
                delete Rightmin;
                return true;

            }
        }
    }
}


 /递归版本(因为传参问题,把他写进private里,外部封装它)
 bool EraseR(const K& k)
{
    return _EraseR(_root, k);
}
 //这里引用传参很关键
 bool _EraseR(Node*& root, const K& k)
{
    if (root == nullptr)
    {
        return false;
    }
    else if (root->_key > k)
    {
        return _EraseR(root->left, k);
    }
    else if (root->_key < k)
    {
        return _EraseR(root->right, k);
    }
    else
    {
        //要删了
        Node* del = root;
        if (root->left == nullptr)
        {
            root = root->right;
        }
        else if (root->right == nullptr)
        {
            root = root->left;
        }
        else
        {
            Node* Rightmin = root->right;
            while (Rightmin->left)
            {
                Rightmin = Rightmin->left;
            }
            swap(Rightmin->_key, root->_key);
            return _EraseR(root->right, k);
        }
        delete del;
        return true;
    }
}

6.KV模型(通过一个值查找另一个值)

就不写在笔记里了,有需要自己看。

二叉搜索树的性能

1.最好

二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:O(logn)

2.最坏

二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:O(n)

总的来说就是因为插入次序不同,导致的,有没有什么办法解决这个问题呢?

AVL树和红黑树在后续就可以上场了。

OJ题

1.已知前序和中序,构建二叉树

class Solution {
public:
    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int ibegin, int iend)
    {
        if (ibegin > iend)
        {
            return nullptr;
        }
        TreeNode* root = new TreeNode(preorder[prei++]);
        //分割中序左右区间(关键:找到中序中根的位置rooti,不能用prei的位置)
        int rooti = ibegin;
        while (rooti <= iend)
        {
            if (inorder[rooti] == root->val)
                break;
            else
                rooti++;
        }
        //rooti就是中序中根的下标了。
        //[ibegin,rooti-1] rooti [rooti+1,iend]
        root->left = _buildTree(preorder, inorder, prei, ibegin, rooti - 1);
        root->right = _buildTree(preorder, inorder, prei, rooti + 1, iend);
        return root;

    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
    {
        int i = 0;
        TreeNode* root = _buildTree(preorder, inorder, i, 0, preorder.size() - 1);
        return root;
    }
};

2.已知后序和中序,构建二叉树

class Solution {
public:
    TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int& prei, int ibegin, int iend)
    {
        if (ibegin > iend)
        {
            return nullptr;
        }
        TreeNode* root = new TreeNode(postorder[prei--]);
        //在中序中分左右区间(关键:找到中序中根的位置)
        int rooti = iend;
        while (rooti >= ibegin)
        {
            if (inorder[rooti] == root->val)
                break;
            else
                rooti--;
        }
        //此时的rooti为中序根的位置
        //[ibegin,rooti-1] rooti [rooti+1,iend]
        root->right = _buildTree(inorder, postorder, prei, rooti + 1, iend);
        root->left = _buildTree(inorder, postorder, prei, ibegin, rooti - 1);
        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
    {
        int i = postorder.size() - 1;
        TreeNode* root = _buildTree(inorder, postorder, i, 0, postorder.size() - 1);
        return root;
    }
};

3.二叉树前序遍历(非递归)

class Solution
{
public:
    vector<int> preorderTraversal(TreeNode* root)
    {
        vector<int> v;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        while (cur || !s.empty())
        {
            while (cur)
            {
                v.push_back(cur->val);
                s.push(cur);
                cur = cur->left;
            }
            //走到这里,左路节点操作完,操作左路节点的右子树
            TreeNode* top = s.top();
            s.pop();

            cur = top->right;
        }
        return v;
    }
};

4.二叉树中序遍历(非递归)

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root)
    {
        vector<int> v;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        while (cur || !s.empty())
        {
            while (cur)
            {
                s.push(cur);
                cur = cur->left;
            }
            //走到这里,左子树走完了,就可以入vector了
            TreeNode* top = s.top();
            s.pop();
            v.push_back(top->val);
            cur = top->right;
        }
        return v;

    }
};

5.二叉树后序遍历(非递归)

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root)
    {
        vector<int> v;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        TreeNode* prev = nullptr;
        while (cur || !s.empty())
        {
            while (cur)
            {
                s.push(cur);
                cur = cur->left;
            }
            //走到这里,左路节点入完栈,就该看节点的右子树了
            //1.如果有,操作右子树
            //2.如果没有,把该节点入vector
            TreeNode* top = s.top();

            if (top->right == nullptr || top->right == prev)
            {
                v.push_back(top->val);
                s.pop();
                prev = top;
            }
            else
            {
                //如果有右子树,才会操作
                cur = top->right;
            }

        }
        return v;
    }
};

【有道云笔记】二叉搜索树
https://note.youdao.com/s/sE5mtWh

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值