CSNotesCSNotes
TODO
LeetCode
数据结构
计算机组成原理
操作系统
计算机网络
数据库
Java
SSM
React
实用工具
GitHub
TODO
LeetCode
数据结构
计算机组成原理
操作系统
计算机网络
数据库
Java
SSM
React
实用工具
GitHub
  • 第一章 绪论

    • 1.1 数据结构的基本概念
  • 第二章 线性表

    • 2.1 线性表的定义和基本操作
    • 2.2 线性表的顺序表示
    • 2.3 线性表的链式表示
  • 第三章 栈、队列和数组

    • 3.1 栈
    • 3.2 队列
    • 3.3 栈和队列的应用
    • 3.4 数组和特殊矩阵
  • 第四章 串

    • 4.1 串的定义和实现
    • 4.2 串的模式匹配
  • 第五章 树与二叉树

    • 5.1 树的基本概念
    • 5.2 二叉树的概念
    • 5.3 二叉树的遍历和线索二叉树
    • 5.4 树、森林
    • 5.5 树和二叉树的应用
  • 第六章 图

    • 6.1 图的基本概念
    • 6.2 图的存储及基本操作
    • 6.3 图的遍历
    • 6.4 图的应用
  • 第七章 查找

    • 7.1 查找的基本概念
    • 7.2 顺序查找和折半查找
    • 7.3 树型查找
    • 7.4 B 树和 B+ 树
    • 7.5 散列表 (哈希表)
  • 第八章 排序

    • 8.1 排序的基本概念
    • 8.2 插入排序
    • 8.3 交换排序
    • 8.4 选择排序
    • 8.5 归并排序和基数排序
    • 8.6 外部排序
  • 第九章 贪心算法

    • 9.1 贪心算法
  • 第十章 动态规划

    • 10.1 动态规划

7.3 树型查找

7.3.1 二叉排序树 (BST)

构造二叉排序树目的:提高查找、插入和删除关键字的速度。

1.二叉排序树的定义

二叉排序树或者是一颗空树,或者是具有下列特性的二叉树:

(1)若左子树非空,则左子树上所有结点的值均小于根结点的值。

(2)若右子树非空,则右子树上所有结点的值均大于根结点的值。

(3)左、右子树也分别是一颗二叉排序树。

LeetCode[109] 有序链表转换二叉搜索树

class Solution
{
public:
    ListNode *findMidNode(ListNode *left, ListNode *right)
    {
        ListNode *slow = left;
        ListNode *fast = left;
        while (fast != right && fast->next != right)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
    TreeNode *buildBST(ListNode *left, ListNode *right)
    {
        if (left == right)
        {
            return nullptr;
        }
        ListNode *mid = findMidNode(left, right);
        TreeNode *root = new TreeNode(mid->val);
        root->left = buildBST(left, mid);
        root->right = buildBST(mid->next, right);
        return root;
    }
    TreeNode *sortedListToBST(ListNode *head)
    {
        return buildBST(head, nullptr);
    }
};

7.3.2 平衡二叉树

任意节点左右子树高度差绝对值不超过 1。

构成平衡二叉树最少结点数递推公式 $n_{0}=0,n_{1}=1,n_{2}=2,n_{h}=1+n_{h-1}+n_{h-2}$,

插入

LL(右单旋转)、

RR(左单旋转)、

LR(先左后右双旋转):在 A 的左孩子的右子树上插入新结点

RL(先右后左双旋转):在 A 的右孩子的左子树上插入新结点

平衡因子为 2 和 1 的结点重新排列,再把其他结点接上去。

删除

向上回溯,找到第一个不平衡的节点,进行调整

7.3.3 红黑树

为什么要有红黑树?

因为平衡二叉树的要求太严格,导致每次进行插入/删除结点时,几乎都会破坏平衡二叉树。在频繁插入/删除结点的场景中,性能会大幅度下降。所以有了红黑树。

左右高度不超过两倍

红黑树是二叉排序树

  1. 每个结点是红色或黑色
  2. 根结点是黑色的
  3. 叶结点(NULL 结点)都是黑色的
  4. 红结点的父结点和孩子结点都是黑色的
  5. 每个结点,从该结点到任一叶结点的简单路径上,所含黑结点的数量相同。记为黑高,根结点的黑高记为红黑树的黑高。

“左根右,根叶黑,不红红,黑路同”

插入

新插入红黑树的结点 z 初始为红色。如果 z 是根结点就是黑色。

违反“不红红”的:

黑叔:

  1. LR:左旋 + 右旋,儿换爷 + 染色
  2. LL:右单旋,父换爷 + 染色
  3. RR:左单选,父换爷 + 染色
  4. RL:右旋 + 左旋,儿换爷 + 染色

红叔:

叔父爷染色 + 爷变新

删除

时间复杂度$O\left( \log_{2} n\right)$

#include <iostream>

using namespace std;

enum Color {
    RED, BLACK
};

struct Node {
    int key;
    Color color;
    Node *left;
    Node *right;

    Node(int k) : key(k), color(RED), left(nullptr), right(nullptr) {}
};

class RedBlackTree {
public:
    RedBlackTree() : root(nullptr) {}

    void insert(int key);

    bool find(int key);

private:
    void insert(Node *&node, int key);

    bool find(Node *node, int key);

    void rotate_left(Node *&node);

    void rotate_right(Node *&node);

    void flip_colors(Node *&node);

    Node *root;
};

void RedBlackTree::insert(int key) {
    insert(root, key);
    root->color = BLACK; // 根节点始终为黑色
}

void RedBlackTree::insert(Node *&node, int key) {
    if (node == nullptr) {
        node = new Node(key);
        return;
    }
    if (key < node->key) { insert(node->left, key); }
    else if (key > node->key) { insert(node->right, key); }
    else {
        return; // 已存在该键值,不插入
    }

// 红黑树自平衡
    if (node->right && node->right->color == RED && (!node->left || node->left->color == BLACK)) {
        rotate_left(node);
    }
    if (node->left && node->left->color == RED && node->left->left && node->left->left->color == RED) {
        rotate_right(node);
    }
    if (node->left && node->left->color == RED && node->right && node->right->color == RED) {
        flip_colors(node);
    }
}


bool RedBlackTree::find(int key) { return find(root, key); }

bool RedBlackTree::find(Node *node, int key) {
    if (node == nullptr) { return false; }
    if (key < node->key) { return find(node->left, key); }
    else if (key > node->key) {
        return find(node->right, key);
    } else { return true; }
}

void RedBlackTree::rotate_left(Node *&node) {
    Node *tmp = node->right;
    node->right = tmp->left;
    tmp->left = node;
    tmp->color = node->color;
    node->color = RED;
    node = tmp;
}

void RedBlackTree::rotate_right(Node *&node) {
    Node *tmp = node->left;
    node->left = tmp->right;
    tmp->right = node;
    tmp->color = node->color;
    node->color = RED;
    node = tmp;
}

void RedBlackTree::flip_colors(Node *&node) {
    node->color = RED;
    node->left->color = BLACK;
    node->right->color = BLACK;
}


int main() {
    RedBlackTree tree;
    tree.insert(5);
    tree.insert(2);
    tree.insert(8);
    tree.insert(1);
    tree.insert(4);
    tree.insert(7);
    tree.insert(9);
    tree.insert(3);
    tree.insert(6);

    cout << "Find 5: " << tree.find(5) << endl; // 输出 1
    cout << "Find 10: " << tree.find(10) << endl; // 输出 0

    return 0;
}

编辑此页
上次更新:
Prev
7.2 顺序查找和折半查找
Next
7.4 B 树和 B+ 树