动态查找树

前言:

这三种是基础的树,要清晰的知道原理,并且知道代码实现

但是网络上的代码参差不齐,这是我罗列的,并且修改过的、完整的、实现度高、逻辑清晰的代码。

一、BST(二叉查找树)

功能实现:

插入某一元素,查找元素,删除指定元素(可以复用删除最小或者最大元素,这要直接敲代码才知道)
寻找和删除最小元素和最大元素(直接复用删除指定元素即可)

要点:

1、接口的设计

2、删除节点:依旧要维持BST树,默认用要删除节点的右子树的最小元素(即最左边元素)要替代删除节点的位置

也可以用左子树的最大元素

代码实现:

#include<iostream>
#include<queue>
using namespace std;
//struct TreeNode;
struct TreeNode {
    int val;
    TreeNode* left;//疑问,为什么不可以直接用TreeNode
    TreeNode* right;
    TreeNode(int x) : val(x),left(NULL),right(NULL) {}
};

//BST树(简单的,不需要平衡)
//功能实现如下:
//插入某一元素,查找元素,删除指定元素(可以复用删除最小或者最大元素,这要直接敲代码才知道)
//寻找和删除最小元素和最大元素(直接复用删除指定元素即可)
class BST {
public:
    BST():size(0),root(NULL) {  }
    void insert(int x);
    bool contain(int x);//查找元素
    void erase(int x);//细节的,我们删除元素必须要调整树结构
    int findMin();//返回查找到的最小值,本身上下层没有什么交互,所以可以直接用迭代法,更简单
    int findMax();//返回查找到的最大值
    void print();//层序遍历

    int size;
private:
    TreeNode* insert(TreeNode* node,int x);
    bool contain(TreeNode* node, int x);
    TreeNode* erase(TreeNode* node, int x);
    TreeNode* findMin(TreeNode* node);
    TreeNode* findMax(TreeNode* node);
    TreeNode* root;
};
void BST::insert(int x) {//利用递归,比较简单,由于递归三部曲,由于接口固定,要再定义一个函数
    root= insert(root, x);//有可能根节点初始值便为空
}
TreeNode* BST::insert(TreeNode* root, int x) {//该树不支持相同元素的插入,相同元素不会树扩大
    if (root == NULL) {
        size++;
        return new TreeNode(x);
    }
    if (x < root->val)root->left=insert(root->left, x);//此递归的代码的框架,可以记住,用本层指针接住下层要回传的,并且本层也向上层回传
    if (x > root->val)root->right=insert(root->right, x);//这个框架适合走某一条路径
    return root;
}
bool BST::contain(int x) {
   return contain(root, x);
}
bool BST::contain(TreeNode* node, int x) {//前序遍历
    if (node == NULL)return false;//边界
    if (node->val == x)return true;  
    else if (x < node->val)return contain(node->left, x);
    else return contain(node->right, x);
}
void BST::erase(int x) {
    if (contain(x))root=erase(root, x);//可能根节点会被更改
    else cout << "没有此元素"<<endl;
    return;
}
//删除节点的函数写错了

TreeNode* BST::erase(TreeNode* node, int x) {//处理三种情况下的删除节点,并且最后要返回根节点,因为有可能根节点也被操作了
    if (node == NULL)return NULL;//由于提前判断必须包含,所以这条语句是不会运行到的
    if (x ==node->val) {//一旦相等,就不需要再递归了,再向下走了
        size--;
        //处理三种情况下的删除节点
        if (!node->left && !node->right) return NULL;//左右节点都为空
        else if (node->left && !node->right)return node->left;//左节点不为空,右节点为空
        else if (!node->left && node->right)return node->right;
        else {//最复杂的情况,左右子节点都不空,需要多几步操作
            //找到右子树的最小节点
            TreeNode* min= findMin(node->right);
            erase(root,min->val);//迅速重构了一棵树
            min->left = node->left;
            min->right = node->right;
            //   min->right =  erase(node->right,min->val);;
            return min;
        }
    }
    else if (x < node->val) node->left = erase(node->left, x);
    else                    node->right = erase(node->right, x);
    return node;
}

int BST::findMin() {
   return findMin(root)->val;
}
int BST::findMax() {
    return findMax(root)->val;
}
TreeNode* BST::findMin(TreeNode* node) {
    while (node->left) {
        node = node->left;
    }
    return node;
}
TreeNode* BST::findMax(TreeNode* node) {
    while (node->right) {
        node = node->right;
    }
    return node;
}
void BST::print(){//层序遍历
    queue<TreeNode*> que;
    que.push(root);
    while (!que.empty()) {
        TreeNode* node = que.front();
        que.pop();
        cout << node->val << " ";
        if (node->left)que.push(node->left);
        if (node->right)que.push(node->right);
    }
    cout << endl;
}


//测试代码
int main() {
    BST a;
    a.insert(4);
    a.insert(2);
    a.insert(1);
    a.insert(3);
    a.insert(6);
    a.insert(5);
    a.insert(15);
    a.insert(7);
    a.insert(16);
    a.insert(14);
    a.print();   
    a.erase(12);
    a.print();
    cout << a.findMin();  
    cout << a.size;

}

二、AVL树

2、1单旋与右旋

思路:

1、判断出问题的节点(不平衡的节点)

2、从该节点出发,走两条边,判断如下

两条边
单旋左—左;右—右
右旋左—右;右—左

单旋:

                                 插入14之前的AVL树

 插入14,并且完成第一步,找到问题节点,即不平衡的节点,默认叶子节点的高度为0,即该节点左右子节点高度差大于1

 完成第二步,从问题节点出发,向插入节点的方向走两步,然后就可以确定是单旋,并且旋转的对象是问题节点和走两步经过的节点(图中是4和6)

                                         结果如上(话说,这样写笔记是真的累,哎)

右旋:

                                                         未插入8的AVL树

                                                 第一步,第二步,确定为双旋

                                                         结果如图

2、2实现

左旋与右旋的代码理解:

可参考:详细图文——AVL树_带翅膀的猫的博客-优快云博客_avl树

功能实现:

插入与删除操作,并且要求同步调整树结构使其达到平衡(要实现右旋与左旋(可复用性强)),并且要求同步调整高度

//AVL树
//功能实现:
//插入与删除操作,并且要求同步调整树结构使其达到平衡(要实现右旋与左旋(可复用性强)),并且要求同步调整高度
#include<iostream>
#include<queue>
using namespace std;
struct TreeNode
{
    int val;
    TreeNode* left;//疑问,为什么不可以直接用TreeNode
    TreeNode* right;
    int height;//叶子节点为0;空节点是不会调用.height的,height(-1)错误
    TreeNode(int x) : val(x), left(NULL), right(NULL),height(0) {}
};
class AVLTree {
public:
    AVLTree():root(NULL){}
    void insert(int x);
    void erase(int x);
    void print();
    TreeNode* root;
private:
   
    TreeNode* leftRotate(TreeNode* node);
    TreeNode* rightRotate(TreeNode* node);
    TreeNode* insert(TreeNode* node,int x);
    TreeNode* erase(TreeNode* node, int x);
    int getHeight(TreeNode* node);
    int getBalanceFactor(TreeNode* node);
};

TreeNode* AVLTree::leftRotate(TreeNode* node) {//注意调整完结构后要更新树的高度
    TreeNode* temp = node->right;
    node->right = temp->left;
    temp->left = node;//简洁的美,观察结构:被承接之后,就可以毫无顾虑的被更改
    node->height = max(getHeight(node->left), getHeight(node->right)) + 1;
    temp->height = max(getHeight(temp->left), getHeight(temp->right)) + 1;
    return temp;
}
TreeNode* AVLTree::rightRotate(TreeNode* node) {
    TreeNode* temp = node->left;
    node->left = temp->right;
    temp->right = node;//简洁的美,观察结构:被承接之后,就可以毫无顾虑的被更改
    node->height = max(getHeight(node->left), getHeight(node->right)) + 1;
    temp->height = max(getHeight(temp->left), getHeight(temp->right)) + 1;
    return temp;
}
void AVLTree::insert(int x) {
    root= insert(root, x);//可能根节点被更新了
}
TreeNode* AVLTree::insert(TreeNode* node, int x) {//默认得不包含重复的节点
    if (node == NULL) {//找到空位了,截至条件
        return new TreeNode(x);
    }
    if (x < node->val)node->left = insert(node->left, x);
    else if(x > node->val)node->right= insert(node->right, x);
    //回溯到这一层之后要更新height
    node->height = 1 + max(getHeight(node->left), getHeight(node->right));
    //验证是否失去平衡,并且调整
    int balanceFactor = getBalanceFactor(node);
    //左边更深,并且是插入到左节点的左节点的位置
    if (balanceFactor > 1 && getBalanceFactor(node->left) > 0) {
        //右旋LL
        return rightRotate(node);
    }
    if (balanceFactor < -1 && getBalanceFactor(node->right) < 0) {
        //左旋RR
        return leftRotate(node);
    }
    //LR:并且从下往上旋,底是右旋,所以要靠左旋;即LR对应于R-左旋;L-右旋
    if (balanceFactor > 1 && getBalanceFactor(node->left) < 0) {     
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }
    //RL
    if (balanceFactor < -1 && getBalanceFactor(node->right) > 0) {
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }
    return node;
}
int AVLTree::getHeight(TreeNode* node) {
    if (node == NULL)return -1;
    else return node->height;
}
int AVLTree::getBalanceFactor(TreeNode* node) {
    if (node == NULL)return 0;
    else return getHeight(node->left) - getHeight(node->right);
}
//删除节点
void AVLTree::erase(int x) {
   root=erase(root, x);
}
TreeNode* AVLTree::erase(TreeNode* node,int x) {
    if (node == NULL) {
        cout << "要删除的节点不存在" << endl;
        return NULL;
    }
    TreeNode* balancenode=node;
    if (x == node->val) {
        //处理三种情况下的删除节点
        if (!node->left && !node->right) return NULL;//左右节点都为空,实际上不需要参与调整
        else if (node->left && !node->right)balancenode= node->left;//左节点不为空,右节点为空
        else if (!node->left && node->right)balancenode= node->right;
        else {//最复杂的情况,左右子节点都不空,返回右子树的最小节点,即最左节点
            //找到右子树的最小节点
            TreeNode* min = node->right;
            while (min->left) {
                min = min->left;
            }
         //不会陷入循环,因为erase不会走进这个if语句
            //min->left = node->left;细节的不可以写在删除操作前面,会提前更改min的左右子节点,对接下来的erase操作有影响
            min->right = erase(node->right,min->val);//可能会重构,节点将不是node->right
            min->left = node->left;
            balancenode= min;                        
            node->left = node->right = NULL;
              
           
        }
    }
    else if (x < node->val) node->left = erase(node->left, x);
    else                    node->right = erase(node->right, x);
    //维护平衡
    //更新height
    balancenode->height = 1 + max(getHeight(balancenode->left), getHeight(balancenode->right));
    //计算平衡因子
    int balanceFactor = getBalanceFactor(balancenode);
    int leftbalanceFactor = getBalanceFactor(balancenode->left);
    int rightbalanceFactor = getBalanceFactor(balancenode->right);
 
    if (balanceFactor > 1 && leftbalanceFactor >= 0) {
        //右旋LL
       
        return rightRotate(balancenode);
    }
    if (balanceFactor < -1 && getBalanceFactor(balancenode->right) <= 0) {
        //左旋RR
     
        return leftRotate(balancenode);
    }
    //LR
    if (balanceFactor > 1 && leftbalanceFactor < 0) {
        balancenode->left = leftRotate(balancenode->left);
        //cout << balancenode->left->val;
        return rightRotate(balancenode);
    }
    //RL
    if (balanceFactor < -1 && getBalanceFactor(balancenode->right) > 0) {
        node->right = rightRotate(balancenode->right);
        return leftRotate(balancenode);
    }
    return balancenode;
}
void AVLTree::print() {//层序遍历
    queue<TreeNode*> que;
    que.push(root);
    while (!que.empty()) {
        TreeNode* node = que.front();
        que.pop();
        cout << node->val << " ";
        if (node->left)que.push(node->left);
        if (node->right)que.push(node->right);
    }
    cout << endl;
}

 代码测试情况:

//测试代码
int main() {//
    AVLTree a;
    a.insert(6);
    a.insert(15);
    a.insert(4);
    a.insert(2);
    a.insert(5);
    a.insert(7);
    a.insert(16);
    a.insert(1);
    a.insert(3);
    a.insert(14);
    a.print();
   a.erase(15);
    a.print();
}

 三、splay树

3、1旋转

“一“字旋转

”之“字旋转

(我是真画不下,xdm,自己看书吧)

3、2代码实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值