C++数据结构之树

目录

1.介绍

2.常见类型

(1)二叉树

(2)二叉搜索树(BST)

(3)平衡二叉树(AVL)

(4)红黑树


1.介绍

        在C++中,树(Tree)是一种常见的数据结构,用于表示层次关系和分层数据。树由节点(Node)组成,每个节点包含数据和指向其子节点的指针。树的基本概念包括:

        1.根节点(Root):树的顶层节点,没有父节点。

        2.子节点(Child):一个节点的直接下级节点。

        3.父节点(Parent):一个节点的直接上级节点。

        4.叶节点(Leaf):没有子节点的节点。

        5.深度(Depth):从根节点到当前节点的路径长度。

        6.高度(Height):从当前节点到最远叶节点的路径长度。

2.常见类型

(1)二叉树

        二叉树是一种特殊的树,每个节点最多有两个子节点,分别成为左子节点和右子节点。

        1.二叉树的节点定义

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

        2.二叉树的遍历

        二叉树的遍历主要有三种:

        前序遍历:根 -> 左 -> 右

        中序遍历:左 -> 根 -> 右

        后续遍历:左 -> 右 -> 根

// 前序遍历
void preorder(TreeNode* root) {
    if (root == nullptr) return;
    std::cout << root->val << " ";
    preorder(root->left);
    preorder(root->right);
}

// 中序遍历
void inorder(TreeNode* root) {
    if (root == nullptr) return;
    inorder(root->left);
    std::cout << root->val << " ";
    inorder(root->right);
}

// 后序遍历
void postorder(TreeNode* root) {
    if (root == nullptr) return;
    postorder(root->left);
    postorder(root->right);
    std::cout << root->val << " ";
}
(2)二叉搜索树(BST)

        二叉搜索树是一种特殊的二叉树,其中每个节点的左子树包含的值都小于该节点的值,右子树包含的值都大于该节点的值。 二叉搜索树支持高效的查找、插入和删除操作,时间复杂度为O(h),其中h是树的高度。在平衡的情况下(AVL或红黑树),h = log₂n,其中n为节点数。

        1.插入节点

        插入节点的过程是从根节点开始,递归找到合适的位置插入新节点。

TreeNode* insert(TreeNode* root, int val) {
    if (root == nullptr) {
        return new TreeNode(val); // 如果树为空,创建新节点
    }
    if (val < root->val) {
        root->left = insert(root->left, val); // 递归插入左子树
    } else if (val > root->val) {
        root->right = insert(root->right, val); // 递归插入右子树
    }
    return root; // 返回当前节点
}

        2.查找节点

        查找节点的过程是从根节点开始,递归地比较节点的值。

TreeNode* search(TreeNode* root, int val) {
    if (root == nullptr || root->val == val) {
        return root; // 找到节点或树为空
    }
    if (val < root->val) {
        return search(root->left, val); // 递归查找左子树
    } else {
        return search(root->right, val); // 递归查找右子树
    }
}

        3.删除节点

        删除节点是二叉搜索树种最复杂的操作,分为三种情况:

        1.叶子结点:直接删除

        2.节点有一个子节点:用子节点替换当前节点。

        3.节点有两个子节点:找到右子树的最小节点(或左子树的最大节点),用其值替换当前节点的值,然后删除该最小节点。

TreeNode* deleteNode(TreeNode* root, int val) {
    if (root == nullptr) {
        return root; // 树为空,直接返回
    }
    if (val < root->val) {
        root->left = deleteNode(root->left, val); // 递归删除左子树
    } else if (val > root->val) {
        root->right = deleteNode(root->right, val); // 递归删除右子树
    } else {
        // 找到要删除的节点
        if (root->left == nullptr) {
            TreeNode* temp = root->right; // 只有一个右子节点
            delete root;
            return temp;
        } else if (root->right == nullptr) {
            TreeNode* temp = root->left; // 只有一个左子节点
            delete root;
            return temp;
        } else {
            // 有两个子节点,找到右子树的最小节点
            TreeNode* temp = root->right;
            while (temp->left != nullptr) {
                temp = temp->left;
            }
            root->val = temp->val; // 用最小节点的值替换当前节点的值
            root->right = deleteNode(root->right, temp->val); // 删除最小节点
        }
    }
    return root;
}

        二叉搜索树中序遍历二叉搜索树会得到一个升序排列的序列。(中序遍历实现代码见二叉树遍历)

(3)平衡二叉树(AVL)

        二叉平衡树是一种自平衡的二叉搜索树,确保树的高度差不超过1,以保持高效的查找、插入和删除操作。

        平衡因子:平衡因子是节点的左子树高度减去右子树高度。平衡因子的绝对值不超过1.

        旋转操作:为了保持平衡,AVL树在插入或删除节点时可能需要进行旋转操作。包括:

左旋、右旋、左右旋和右左旋。

        1.节点定义

struct AVLNode {
    int val;
    int height; // 节点的高度
    AVLNode* left;
    AVLNode* right;
    AVLNode(int x) : val(x), height(1), left(nullptr), right(nullptr) {}
};

        2.计算节点高度

int getHeight(AVLNode* node) {
    if (node == nullptr) return 0;
    return node->height;
}

          3.计算平衡因子

int getBalanceFactor(AVLNode* node) {
    if (node == nullptr) return 0;
    return getHeight(node->left) - getHeight(node->right);
}

        4.更新节点高度

void updateHeight(AVLNode* node) {
    if (node == nullptr) return;
    node->height = std::max(getHeight(node->left), getHeight(node->right)) + 1;
}

        5.旋转

        (1)右旋(根节点往左子树节点移)

AVLNode* rightRotate(AVLNode* y) {
    AVLNode* x = y->left;
    AVLNode* T2 = x->right;

    // 旋转
    x->right = y;
    y->left = T2;

    // 更新高度
    updateHeight(y);
    updateHeight(x);

    return x;
}

        (2)左旋(根节点往右子树节点移)

AVLNode* leftRotate(AVLNode* x) {
    AVLNode* y = x->right;
    AVLNode* T2 = y->left;

    // 旋转
    y->left = x;
    x->right = T2;

    // 更新高度
    updateHeight(x);
    updateHeight(y);

    return y;
}

        6.插入节点

        插入节点,检查平衡因子并进行旋转操作以恢复平衡。

AVLNode* insert(AVLNode* root, int val) {
    // 1. 插入节点
    if (root == nullptr) {
        return new AVLNode(val);
    }
    if (val < root->val) {
        root->left = insert(root->left, val);
    } else if (val > root->val) {
        root->right = insert(root->right, val);
    } else {
        return root; // 不允许重复节点
    }

    // 2. 更新高度
    updateHeight(root);

    // 3. 计算平衡因子
    int balanceFactor = getBalanceFactor(root);

    // 4. 平衡调整
    // 左子树不平衡
    if (balanceFactor > 1) {
        if (val < root->left->val) {
            // 左左情况,右旋
            return rightRotate(root);
        } else {
            // 左右情况,先左旋再右旋
            root->left = leftRotate(root->left);
            return rightRotate(root);
        }
    }
    // 右子树不平衡
    if (balanceFactor < -1) {
        if (val > root->right->val) {
            // 右右情况,左旋
            return leftRotate(root);
        } else {
            // 右左情况,先右旋再左旋
            root->right = rightRotate(root->right);
            return leftRotate(root);
        }
    }

    return root;
}

        7.删除节点

        删除节点后,检查平衡因子并进行旋转操作以恢复平衡。

AVLNode* deleteNode(AVLNode* root, int val) {
    // 1. 删除节点
    if (root == nullptr) {
        return root;
    }
    if (val < root->val) {
        root->left = deleteNode(root->left, val);
    } else if (val > root->val) {
        root->right = deleteNode(root->right, val);
    } else {
        // 找到要删除的节点
        if (root->left == nullptr || root->right == nullptr) {
            AVLNode* temp = root->left ? root->left : root->right;
            if (temp == nullptr) {
                temp = root;
                root = nullptr;
            } else {
                *root = *temp;
            }
            delete temp;
        } else {
            // 有两个子节点,找到右子树的最小节点
            AVLNode* temp = root->right;
            while (temp->left != nullptr) {
                temp = temp->left;
            }
            root->val = temp->val;
            root->right = deleteNode(root->right, temp->val);
        }
    }

    if (root == nullptr) {
        return root;
    }

    // 2. 更新高度
    updateHeight(root);

    // 3. 计算平衡因子
    int balanceFactor = getBalanceFactor(root);

    // 4. 平衡调整
    // 左子树不平衡
    if (balanceFactor > 1) {
        if (getBalanceFactor(root->left) >= 0) {
            // 左左情况,右旋
            return rightRotate(root);
        } else {
            // 左右情况,先左旋再右旋
            root->left = leftRotate(root->left);
            return rightRotate(root);
        }
    }
    // 右子树不平衡
    if (balanceFactor < -1) {
        if (getBalanceFactor(root->right) <= 0) {
            // 右右情况,左旋
            return leftRotate(root);
        } else {
            // 右左情况,先右旋再左旋
            root->right = rightRotate(root->right);
            return leftRotate(root);
        }
    }

    return root;
}
(4)红黑树

        红黑树是一种自平衡的二叉搜索树,它通过额外的颜色标记和旋转操作来保持树的平衡。红黑树确保了最坏情况下的查找、插入和删除时间复杂度为O(logn)。 

        红黑树必须满足一下性质:

        1.节点是红色或黑色。

        2.根节点是黑色。

        3.所有叶子结点是黑色。

        4.如果一个节点是红色,则它的两个子节点都是黑色。(没有两个连续的红色节点)。

        5.从任意节点到其每个叶子的所有路径都包含相同数量的黑色节点(黑色高度相同)。

红黑树基本操作

        1.节点定义

enum Color { RED, BLACK };

struct RBNode {
    int val;
    Color color;
    RBNode* left;
    RBNode* right;
    RBNode* parent;
    RBNode(int x) : val(x), color(RED), left(nullptr), right(nullptr), parent(nullptr) {}
};

        2.左旋

void leftRotate(RBNode*& root, RBNode* x) {
    RBNode* y = x->right;
    x->right = y->left;
    if (y->left != nullptr) {
        y->left->parent = x;
    }
    y->parent = x->parent;
    if (x->parent == nullptr) {
        root = y;
    } else if (x == x->parent->left) {
        x->parent->left = y;
    } else {
        x->parent->right = y;
    }
    y->left = x;
    x->parent = y;
}

        3.右旋

void rightRotate(RBNode*& root, RBNode* y) {
    RBNode* x = y->left;
    y->left = x->right;
    if (x->right != nullptr) {
        x->right->parent = y;
    }
    x->parent = y->parent;
    if (y->parent == nullptr) {
        root = x;
    } else if (y == y->parent->right) {
        y->parent->right = x;
    } else {
        y->parent->left = x;
    }
    x->right = y;
    y->parent = x;
}

        4.插入节点

        插入节点后,通过颜色调整和旋转操作来恢复红黑树的性质。

void insertFixup(RBNode*& root, RBNode* z) {
    while (z->parent != nullptr && z->parent->color == RED) {
        if (z->parent == z->parent->parent->left) {
            RBNode* y = z->parent->parent->right;
            if (y != nullptr && y->color == RED) {
                // Case 1: 叔叔节点是红色
                z->parent->color = BLACK;
                y->color = BLACK;
                z->parent->parent->color = RED;
                z = z->parent->parent;
            } else {
                if (z == z->parent->right) {
                    // Case 2: 叔叔节点是黑色,且当前节点是右孩子
                    z = z->parent;
                    leftRotate(root, z);
                }
                // Case 3: 叔叔节点是黑色,且当前节点是左孩子
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                rightRotate(root, z->parent->parent);
            }
        } else {
            RBNode* y = z->parent->parent->left;
            if (y != nullptr && y->color == RED) {
                // Case 1: 叔叔节点是红色
                z->parent->color = BLACK;
                y->color = BLACK;
                z->parent->parent->color = RED;
                z = z->parent->parent;
            } else {
                if (z == z->parent->left) {
                    // Case 2: 叔叔节点是黑色,且当前节点是左孩子
                    z = z->parent;
                    rightRotate(root, z);
                }
                // Case 3: 叔叔节点是黑色,且当前节点是右孩子
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                leftRotate(root, z->parent->parent);
            }
        }
    }
    root->color = BLACK; // 根节点必须是黑色
}

void insert(RBNode*& root, int val) {
    RBNode* z = new RBNode(val);
    RBNode* y = nullptr;
    RBNode* x = root;

    // 找到插入位置
    while (x != nullptr) {
        y = x;
        if (z->val < x->val) {
            x = x->left;
        } else {
            x = x->right;
        }
    }

    z->parent = y;
    if (y == nullptr) {
        root = z;
    } else if (z->val < y->val) {
        y->left = z;
    } else {
        y->right = z;
    }

    z->left = nullptr;
    z->right = nullptr;
    z->color = RED;

    // 修复红黑树性质
    insertFixup(root, z);
}

        5.删除节点

        删除节点后,通过颜色调整和旋转操作来恢复红黑树的性质。

void deleteFixup(RBNode*& root, RBNode* x) {
    while (x != root && (x == nullptr || x->color == BLACK)) {
        if (x == x->parent->left) {
            RBNode* w = x->parent->right;
            if (w->color == RED) {
                // Case 1: 兄弟节点是红色
                w->color = BLACK;
                x->parent->color = RED;
                leftRotate(root, x->parent);
                w = x->parent->right;
            }
            if ((w->left == nullptr || w->left->color == BLACK) &&
                (w->right == nullptr || w->right->color == BLACK)) {
                // Case 2: 兄弟节点是黑色,且兄弟节点的两个孩子都是黑色
                w->color = RED;
                x = x->parent;
            } else {
                if (w->right == nullptr || w->right->color == BLACK) {
                    // Case 3: 兄弟节点是黑色,且兄弟节点的左孩子是红色,右孩子是黑色
                    w->left->color = BLACK;
                    w->color = RED;
                    rightRotate(root, w);
                    w = x->parent->right;
                }
                // Case 4: 兄弟节点是黑色,且兄弟节点的右孩子是红色
                w->color = x->parent->color;
                x->parent->color = BLACK;
                w->right->color = BLACK;
                leftRotate(root, x->parent);
                x = root;
            }
        } else {
            RBNode* w = x->parent->left;
            if (w->color == RED) {
                // Case 1: 兄弟节点是红色
                w->color = BLACK;
                x->parent->color = RED;
                rightRotate(root, x->parent);
                w = x->parent->left;
            }
            if ((w->right == nullptr || w->right->color == BLACK) &&
                (w->left == nullptr || w->left->color == BLACK)) {
                // Case 2: 兄弟节点是黑色,且兄弟节点的两个孩子都是黑色
                w->color = RED;
                x = x->parent;
            } else {
                if (w->left == nullptr || w->left->color == BLACK) {
                    // Case 3: 兄弟节点是黑色,且兄弟节点的右孩子是红色,左孩子是黑色
                    w->right->color = BLACK;
                    w->color = RED;
                    leftRotate(root, w);
                    w = x->parent->left;
                }
                // Case 4: 兄弟节点是黑色,且兄弟节点的左孩子是红色
                w->color = x->parent->color;
                x->parent->color = BLACK;
                w->left->color = BLACK;
                rightRotate(root, x->parent);
                x = root;
            }
        }
    }
    if (x != nullptr) {
        x->color = BLACK;
    }
}

void deleteNode(RBNode*& root, RBNode* z) {
    RBNode* y = z;
    RBNode* x;
    Color yOriginalColor = y->color;

    if (z->left == nullptr) {
        x = z->right;
        transplant(root, z, z->right);
    } else if (z->right == nullptr) {
        x = z->left;
        transplant(root, z, z->left);
    } else {
        y = minimum(z->right);
        yOriginalColor = y->color;
        x = y->right;
        if (y->parent == z) {
            if (x != nullptr) {
                x->parent = y;
            }
        } else {
            transplant(root, y, y->right);
            y->right = z->right;
            y->right->parent = y;
        }
        transplant(root, z, y);
        y->left = z->left;
        y->left->parent = y;
        y->color = z->color;
    }

    if (yOriginalColor == BLACK) {
        deleteFixup(root, x);
    }

    delete z;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值