c# 数据结构 树篇 入门树与二叉树的一切

news2025/5/18 18:30:53

        事先声明,本文不适合对数据结构完全不懂的小白 请至少学会链表再阅读

        c# 数据结构 链表篇 有关单链表的一切_c# 链表-CSDN博客

数据结构理论先导:《数据结构(C 语言描述)》也许是全站最良心最通俗易懂最好看的数据结构课(最迟每周五更新~~)_哔哩哔哩_bilibili

代码随想录:

关于二叉树,你该了解这些!| 二叉树理论基础一网打尽,二叉树的种类、二叉树的存储方式、二叉树节点定义、二叉树的遍历顺序_哔哩哔哩_bilibili

        

目录

0.树基础概念

1.二叉树基础概念 BT B:Binary 

​编辑

2.普通二叉树存储/遍历/缺点

 存储方式

顺序存储:

 链式存储:

遍历方式

顺序存储遍历(递归)

链式存储遍历(递归)

插入方式及其缺点

        链式存储插入

        缺点

3.二叉搜索树 BST S:Search

        链式构建及其增删查改

        测试用例以及全部代码

4.平衡二叉树(AVL) BBT:   B:Balanced

        构建和插入

        删除

5.二叉树的线索化     

5.1设计线索化结构

5.2 线索化构建树 

5.3 线索化后中序遍历方法举例

   关于红黑树和b+树 下一篇数据结构 树篇 将会做出彻底详解 


0.树基础概念

        0和1是基础概念和一些性质 可以看个大概 知道有这么回事就行了 后面用到再回来想想是不是这么回事即可 

 叶子节点也算是子树 

树的性质

 解释:

结点的度 = 该节点下最大子树个数,也就是看有几个孩子

比如:A结点 有三个子树 即为三个孩子 ,B有两个孩子,K无孩子

解释: 

还是这棵树 i =1开始 

树的度 = 最大结点的度,也就是有最多孩子结点的度

还是上面这棵树 最多孩子的度为A||D 所以 m = 3

第一层就是3的0次方 =1

第二层就是3的1次方 =3

第三层就是3的二次方 =9 (最大值)

1.二叉树基础概念 BT B:Binary 

 解释:

对于这个性质的解释:


 

2.普通二叉树存储/遍历/缺点

 存储方式

顺序存储:

        顺序存储需要注意的是让索引 i 从0

        

        特点是:依据其索引的特点 通过线性结构存储起来

        我用的是List(i=0开始)

public class Tree<T> : MonoBehaviour
{
    private List<T> bitTree = new List<T>();

    public void AddTree(T[] values)
    {
       for(int i = 0; i < values.Length; i++)
       {
            bitTree.Add(values[i]);
      }
    }
}
     Tree<char> tree = new Tree<char>();
        char[] values = { 'A', 'B', 'C', 'D' };
        tree.AddTree(values);

 链式存储:

特点为 =左右孩子指针 + 数据存储位

//节点暂时定义为char
public class TreeNode {
        public char Date;
        public TreeNode LeftNode;
        public TreeNode RightNode;

        public TreeNode(char Date, TreeNode LeftNode=null, TreeNode RightNode=null) {
            this.Date = Date;
        this.LeftNode =LeftNode;
        this.RightNode = RightNode;
        }

遍历方式

有三种分为前中后序

 前序特点:

就是先当前节点(如果从根节点开始),再左节点再右节点

中序特点:

先左,再当前,最后右

后序特点:

先左,再右,最后当前

顺序存储遍历(递归)

以前序为例 先构造树

再做递归

    //顺序遍历二叉树
    //前序
    public void Preorder(int index)
        {
        if (index >= bitTree.Count)
            return;

        Debug.Log(bitTree[index]);
         int LeftNode = 2*index+1;
        int  RightNode = 2*index+2;
        Preorder(LeftNode);
        Preorder(RightNode);
    }
    //中序
    public void MiddleOrder(int index)
        {
        if (index >= bitTree.Count)
            return;
        int LeftNode = 2 * index + 1;
        int RightNode = 2 * index + 2;
        MiddleOrder(LeftNode);
        Debug.Log(bitTree[index]);
        MiddleOrder(RightNode);
    }
    //后序
    public void AfterOrder(int index) {
        if (index >= bitTree.Count)
            return;
        int LeftNode = 2 * index + 1;
        int RightNode = 2 * index + 2;
        AfterOrder(LeftNode);
        AfterOrder(RightNode);
        Debug.Log(bitTree[index]);
    }

链式存储遍历(递归)

还是以前序遍历为例:

TreeNode rootNode = new TreeNode('A',
    new TreeNode('B',new TreeNode('D'), new TreeNode('E')),
    new TreeNode('C', new TreeNode('F')));

rootNode.FirstRoot(rootNode);
Console.WriteLine();

rootNode.MiddleRoot(rootNode);
Console.WriteLine();

rootNode.AfterRoot(rootNode);
//节点暂时定义为char
public class TreeNode {
        public char Date;
        public TreeNode LeftNode;
        public TreeNode RightNode;

        public TreeNode(char Date, TreeNode LeftNode=null, TreeNode RightNode=null) {
            this.Date = Date;
        this.LeftNode =LeftNode;
        this.RightNode = RightNode;
        }

    //遍历方法 前
    public void FirstRoot(TreeNode treeNode)
    {
        if (treeNode == null) return;
        Console.Write(treeNode.Date);
        FirstRoot(treeNode.LeftNode);
        FirstRoot(treeNode.RightNode);
    }
    //遍历方法 中
    public void MiddleRoot(TreeNode treeNode)
    {
        if (treeNode == null) return;
        Console.Write(treeNode.Date);
        MiddleRoot(treeNode.LeftNode);

        MiddleRoot(treeNode.RightNode);
    }  //遍历方法 后
    public void AfterRoot(TreeNode treeNode)
    {
        if (treeNode == null) return;
        AfterRoot(treeNode.LeftNode);
        AfterRoot(treeNode.RightNode);
        Console.Write(treeNode.Date);
    }
}

插入方式及其缺点

        链式存储插入

        我想给原来的二叉树的 2 6之间插入一个4


TreeNode root = new TreeNode(1);
root.LeftNode = new TreeNode(2);
root.RightNode = new TreeNode(3);
root.LeftNode.LeftNode = new TreeNode(6);
root.LeftNode.RightNode = new TreeNode(7);


//节点暂时定义为char
public class TreeNode
{
    public int Date;
    public TreeNode LeftNode;
    public TreeNode RightNode;

    public TreeNode(int Date, TreeNode LeftNode = null, TreeNode RightNode = null)
    {
        this.Date = Date;
        this.LeftNode = LeftNode;
        this.RightNode = RightNode;
    } 

我需要做的

// 创建新节点
TreeNode newNode = new TreeNode(4);

// 在节点 2 的左子节点位置插入新节点 4
BinaryTree.InsertNode(root.LeftNode, true, newNode);
 public class BinaryTree
 {
     /// <summary>
     /// 值插入
     /// </summary>
     /// <param name="parent"></param>
     /// <param name="isLeft">是否是左子节点</param>
     /// <param name="newNode"></param>
     public static void InsertNode(TreeNode parent, bool isLeft, TreeNode newNode)
     {
         if (isLeft)
         {
             newNode.LeftNode = parent.LeftNode;
             parent.LeftNode = newNode;
         }
         else
         {
             newNode.RightNode = parent.RightNode;
             parent.RightNode = newNode;
         }
     }
 }

        缺点

        发现了没有和他娘的链表似的: 

        无法利用 “值的顺序” 进行高效操作  

        比如搜索某个值(如查找 4)时,只能从根节点开始遍历整个树(如广度优先或深度优先搜索),时间复杂度为 O(n),和链表无异

         插入逻辑不通用:插入节点 4 到节点 2 的左子节点,依赖于手动指定父子关系,没有统一的规则,如果树结构复杂,插入位置的选择会非常随意,导致树的高度不平衡(如退化成链表)

        于是有了下面这个玩意 二叉搜索树

3.二叉搜索树 BST S:Search

        数据结构合集 - 二叉搜索树(二叉排序树)(二叉查找树)_哔哩哔哩_bilibili

        特点 左子树Data小于根Data   右子树Data 大于根Data

操作最好时间复杂度最坏时间复杂度
查找(Search)O(log n)O(n)
插入(Insert)O(log n)O(n)
删除(Delete)O(log n)O(n)
空间复杂度O(n){O (n)(存储所有节点,与树的形态无关)}

        存储方式还是有链式和顺序 我倾向于链式 因为比较简单和清晰 所以拿此举例

        链式构建及其增删查改

            构建

public class TreeNode { 
    public int value; 
    public TreeNode Left; 
    public TreeNode Right;

    public TreeNode(int value, TreeNode left = null, TreeNode right = null) {
        this.value = value;
        this.Left = left;
        this.Right = right;
    }
}

public class BinarySearchTree
{
    public TreeNode root;
    public BinarySearchTree()
    {
        root = null;
    }

        增加 

    //增加节点
    public void Insert(int value) { 
        InsertTrueMethod(root, value);
    }

    /// <summary>
    /// 递归查找插入位置
    /// </summary>
    /// <param name="node">插入的节点</param>
    /// <param name="val">插入的值</param>

    private TreeNode InsertTrueMethod(TreeNode node, int value)
    {
        if (node == null)
        {
            return new TreeNode(value);
        }
        //如果插入值 小于 节点值,则在左子树中插入
        if (value < node.value)
        {
            node.Left = InsertTrueMethod(node.Left, value);
        }
        //如果插入值 大于 节点值,则在右子树中插入
        else if (value > node.value)
        {
            node.Right = InsertTrueMethod(node.Right, value);
        }
        //如果找到了相同的值,则不插入
        return node;
    }

        删除

    private TreeNode DeleteTrueMethod(TreeNode node, int value)
    {
        //如果树为空,则直接返回空
        if (node == null)
        {
            return null;
        }
        //如果删除值 小于 节点值,则在左子树找
        if (value < node.value)
        {
            node.Left = DeleteTrueMethod(node.Left, value);
        }
        //如果删除值 大于 节点值,则在右子树
        else if (value > node.value)
        {
            node.Right = DeleteTrueMethod(node.Right, value);
        }
        //如果找到了相同的值,则删除该节点
        else
        {
            //如果该节点没有子节点,则直接删除该节点
            if (node.Left == null && node.Right == null)
            {
                node = null;
            }
            //如果该节点只有一个子节点,则直接用该节点的子节点替换该节点
            else if (node.Left == null)
            {
                node = node.Right;
            }
            else if (node.Right == null)
            {
                node = node.Left;
            }
            // 如果该节点有两个子节点,此时有两种常见的替换策略:
            // 可以选择该节点右子树中的最小节点,也可以选择左子树中的最大节点。
            // 这里我们采用选择右子树中最小节点的策略。选择右子树最小节点(或者左子树最大节点)的目的是
            // 为了保证替换后仍然满足二叉搜索树的性质:左子树所有节点值小于根节点值,右子树所有节点值大于根节点值。
            // 步骤为:先找到右子树的最小节点,用其值替换当前要删除节点的值,
            // 然后再递归地从右子树中删除这个最小节点。
            else
            {
                TreeNode minNode = GetMinNode(node.Right);
                node.value = minNode.value;
                node.Right = DeleteTrueMethod(node.Right, minNode.value);
            }
        }
        return node;
    }
    //获取节点方法
    private TreeNode GetMinNode(TreeNode node)
    {
        while (node.Left != null)
        {
            node = node.Left;
        }
        return node;
    }

        测试用例以及全部代码

        注释进行了简单的修改

using System;
BinarySearchTree bst = new BinarySearchTree();

// 测试用例 1:删除叶子节点
int[] values1 = { 50, 30, 20, 40, 70, 60, 80 };
foreach (var val in values1) bst.Insert(val);
//Console.WriteLine("删除前(用例1):");
//bst.InOrderTraversal(bst.root); // 输出:20 30 40 50 60 70 80 
//bst.Delete(20);
//Console.WriteLine("删除后(用例1-叶子节点):");
//bst.InOrderTraversal(bst.root); // 预期:30 40 50 60 70 80 

 测试用例 2:删除单子节点
//BinarySearchTree bst2 = new BinarySearchTree();
//int[] values2 = { 50, 30, 40 };
//foreach (var val in values2) bst2.Insert(val);
//bst2.Delete(30);
//Console.WriteLine("删除后(用例2-单子节点):");
//bst2.InOrderTraversal(bst2.root); // 预期:40 50 

 测试用例 3:删除双子节点
BinarySearchTree bst3 = new BinarySearchTree();
int[] values3 = { 50, 30, 20, 40, 70, 60, 80 };
foreach (var val in values3) bst3.Insert(val);
bst3.Delete(50);
Console.WriteLine("删除后(用例3-双子节点):");
bst3.InOrderTraversal(bst3.root); // 预期:20 30 40 60 70 80 
public class TreeNode
{
    public int value;
    public TreeNode Left;
    public TreeNode Right;

    public TreeNode(int value, TreeNode left = null, TreeNode right = null)
    {
        this.value = value;
        this.Left = left;
        this.Right = right;
    }
}

public class BinarySearchTree
{
    public TreeNode root;
    public BinarySearchTree()
    {
        root = null;
    }

    // 增加节点
    public void Insert(int value)
    {
        root = InsertTrueMethod(root, value); 
    }

    private TreeNode InsertTrueMethod(TreeNode node, int value)
    {
        if (node == null)
            return new TreeNode(value);
        if (value < node.value)
            node.Left = InsertTrueMethod(node.Left, value);
        else if (value > node.value)
            node.Right = InsertTrueMethod(node.Right, value);
        return node;
    }

    public void Delete(int value)
    {
        root = DeleteTrueMethod(root, value);
    }

    private TreeNode DeleteTrueMethod(TreeNode node, int value)
    {
        //先找到待删除节点
        if (node == null) return null;
        if (value < node.value)
            node.Left = DeleteTrueMethod(node.Left, value);
        else if (value > node.value)
            node.Right = DeleteTrueMethod(node.Right, value);
        else 
        
        {
            //找到以后分为三种情况
            //1. 叶子节点
            if (node.Left == null && node.Right == null)
                node = null;
            //2. 单子节点
            else if (node.Left == null)
                node = node.Right;
            else if (node.Right == null)
                node = node.Left;
            else
            {
                //3. 双子节点 找到右子树最小节点(或者左子树最大节点 替换待删除节点 然后递归删除右子树最小节点, 或者左子树最大节点)
                TreeNode minNode = GetRightMinNode(node.Right);
                node.value = minNode.value;
                node.Right = DeleteTrueMethod(node.Right, minNode.value);
            }
        }
        return node;
    }

    private TreeNode GetRightMinNode(TreeNode node)
    {
        while (node.Left != null) node = node.Left;
        return node;
    }

    public void InOrderTraversal(TreeNode node)
    {
        if (node == null) return;
        InOrderTraversal(node.Left);
        Console.Write(node.value + " "); 
        InOrderTraversal(node.Right);
        if (node == root) Console.WriteLine(); 
    }
}

        但是还是有一个问题 如果数据本来就有序 那构建二叉搜索树会成为一条线

         

        所以为了解决这个问题 有了下面这个平衡二叉树

4.平衡二叉树(AVL) BBT:   B:Balanced

        建议直接看视频 平衡二叉树(AVL树)_哔哩哔哩_bilibili

        这个东西是比较抽象的 而且情况也比较多 下图来自 b站 @帕拉迪克

        平衡因子:Balanced Factor

        上代码

        构建和插入

using System;

//测试结果
AVLTree tree = new AVLTree();
int[] values = { 5,3,4};

foreach (int value in values) {
    tree.Insert(value);
}

tree.InOrderTraversal(tree.root);
// 定义 AVL 树节点类
public class TreeNode
{
    public int Value;       
    public TreeNode Left;     
    public TreeNode Right;    
    public int Height;        

    public TreeNode(int value)
    {
        Value = value;
        Height = 1; // 新节点初始高度为1
    }
}

public class AVLTree
{
    public TreeNode root;

    //获取传入节点的高度 
    private int GetHight(TreeNode node) { 
        return node == null? 0 : node.Height;
    }

    //计算平衡因子 让传入节点的左子树 - 右子树高度 
    private int ComputeBalanceFactor(TreeNode node) { 
      if(node == null ) return 0;
        int BalanceFactor = GetHight(node.Left) - GetHight(node.Right);
        return BalanceFactor;
    }


    //右旋 右旋父节点,然后将冲突的右放在旋转后的父节点的左子树
    private TreeNode RightRotate(TreeNode father) { 
        //找到左子树节点和新插入节点
        TreeNode leftChild = father.Left;
        TreeNode conflict = leftChild.Right;

        //旋转
        leftChild.Right =  father; //原父节点弄到左子树的右边
        father.Left = conflict; //冲突的弄到原父节点的左边

        //更新高度 先更新一下原来的父节点的高度 再更新一下现在的父节点的高度
        father.Height = Math.Max(GetHight(father.Left), GetHight(father.Right)) + 1;
        leftChild.Height = Math.Max(GetHight(leftChild.Left), GetHight(leftChild.Right)) + 1;

        //返回旋转后的父节点
        return leftChild;
    }

    //左旋 左旋父节点,然后将冲突的左放在旋转后的父节点的右子树
    private TreeNode LeftRotate(TreeNode father)
    {
        //找到右子树节点和新插入节点
        TreeNode rightChild = father.Right;
        TreeNode conflict = rightChild.Left;

        //旋转
        rightChild.Left = father; //原父节点弄到右子树的左边
        father.Right = conflict; //冲突的弄到原父节点的右边

        //更新高度 先更新一下原来的父节点的高度 再更新一下现在的父节点的高度
        father.Height = Math.Max(GetHight(father.Left), GetHight(father.Right)) + 1;
        rightChild.Height = Math.Max(GetHight(rightChild.Left), GetHight(rightChild.Right)) + 1;

        //返回旋转后的父节点
        return rightChild;
    }

    public void Insert(int value)
    {
        root = InsertTrueMethod(root, value);
    }

    private TreeNode InsertTrueMethod(TreeNode root, int value)
    {
        //BST插入 空创建 左右递归插入 重复值不插入
        if (root == null)
            return new TreeNode(value);
        if (value < root.Value)
            root.Left = InsertTrueMethod(root.Left, value); //递归+更新+平衡
        else if (value > root.Value)
            root.Right = InsertTrueMethod(root.Right, value);//递归+更新+平衡
        else 
            return root;

        //更新高度
        root.Height = Math.Max(GetHight(root.Left), GetHight(root.Right)) + 1;

        //计算平衡因子
        int balanceFactor = ComputeBalanceFactor(root);

        //根据平衡因子判断怎么转
        //LL型 右旋  value < root.Left.Value:验证新节点确实插入在左子树的左侧
        if (balanceFactor > 1 && value < root.Left.Value)
            return RightRotate(root);

        //RR型 左旋
        if (balanceFactor < -1 && value > root.Right.Value)
            return LeftRotate(root);

        //LR型 先左旋再右旋
        if (balanceFactor > 1 && value > root.Left.Value)
        {
            root.Left = LeftRotate(root.Left);
            return RightRotate(root);
        }

        //RL型 先右旋再左旋
        if (balanceFactor < -1 && value < root.Right.Value)
        {
            root.Right = RightRotate(root.Right);
            return LeftRotate(root);
        }

        //不用旋返回根节点
        return root;
    }

    //中序遍历
    public void InOrderTraversal(TreeNode root)
    {
        if (root == null)
            return;
        InOrderTraversal(root.Left);
        Console.WriteLine(root.Value);
        InOrderTraversal(root.Right);
    }
}

        删除

    // 删除指定值的节点
    public void Delete(int value)
    {
        root = DeleteNode(root, value);
    }

    private TreeNode DeleteNode(TreeNode root, int value)
    {
        // 如果根节点为空,直接返回 null
        if (root == null)
            return root;

        // 如果要删除的值小于当前节点的值,递归删除左子树中的节点
        if (value < root.Value)
            root.Left = DeleteNode(root.Left, value);
        // 如果要删除的值大于当前节点的值,递归删除右子树中的节点
        else if (value > root.Value)
            root.Right = DeleteNode(root.Right, value);
        // 找到要删除的节点
        else
        {
            // 情况 1: 节点没有子节点或只有一个子节点
            if (root.Left == null || root.Right == null)
            {
                TreeNode temp = root.Left ?? root.Right;
                // 如果没有子节点,直接删除该节点
                if (temp == null)
                {
                    root = null;
                }
                else
                {
                    // 用子节点替换当前节点
                    root = temp;
                }
            }
            // 情况 2: 节点有两个子节点
            else
            {
                // 找到右子树中的最小节点
                TreeNode temp = MinValueNode(root.Right);
                // 用最小节点的值替换当前节点的值
                root.Value = temp.Value;
                // 递归删除右子树中的最小节点
                root.Right = DeleteNode(root.Right, temp.Value);
            }
        }

        // 如果删除后树为空,直接返回 null
        if (root == null)
            return root;

        // 更新节点高度
        root.Height = Math.Max(GetHight(root.Left), GetHight(root.Right)) + 1;

        // 计算平衡因子
        int balanceFactor = ComputeBalanceFactor(root);

        // LL 型失衡,进行右旋操作
        if (balanceFactor > 1 && ComputeBalanceFactor(root.Left) >= 0)
            return RightRotate(root);

        // LR 型失衡,先对左子树进行左旋,再对根节点进行右旋
        if (balanceFactor > 1 && ComputeBalanceFactor(root.Left) < 0)
        {
            root.Left = LeftRotate(root.Left);
            return RightRotate(root);
        }

        // RR 型失衡,进行左旋操作
        if (balanceFactor < -1 && ComputeBalanceFactor(root.Right) <= 0)
            return LeftRotate(root);

        // RL 型失衡,先对右子树进行右旋,再对根节点进行左旋
        if (balanceFactor < -1 && ComputeBalanceFactor(root.Right) > 0)
        {
            root.Right = RightRotate(root.Right);
            return LeftRotate(root);
        }

        return root;
    }

    // 找到以给定节点为根的子树中的最小节点
    private TreeNode MinValueNode(TreeNode node)
    {
        TreeNode current = node;
        // 不断向左遍历,直到找到最左边的节点
        while (current.Left != null)
            current = current.Left;
        return current;
    }

        看起来有点难度实际上熟悉以后你会发现其编写都是套路 多读几遍就好了

        递归递归递归,你会发现二叉树一直在做这个动作 那么有没有一种方式脱离它呢?

        有的兄弟 有的 :线索化二叉树

5.二叉树的线索化     

        线索化二叉树的设计思想是:

        将空的指针(Left 或 Right)利用起来,不再指向子树(因为子树为空),而是指向该节点在中序遍历中的前驱或后继节点

        这样做可以在后续遍历中,不依赖递归或栈,直接通过线索找到前后节点

        画图,逻辑思想

        下为代码解释

5.1设计线索化结构

        其中LeftIsThread 和 RightIsThread 是指当前Left和Right是否为空

public class ThreadedNode<T>
{
    public T Data { get; set; }
    public ThreadedNode<T> Left { get; set; }
    public ThreadedNode<T> Right { get; set; }
    // 标志位:false表示指向子节点,true表示线索
    public bool LeftIsThread { get; set; }
    public bool RightIsThread { get; set; }

    public ThreadedNode(T data)
    {
        this.Data = data;
        this.Left = null;
        this.Right = null;
        this.LeftIsThread = false;
        this.RightIsThread = false;
    }
}

5.2 线索化构建树 

public class ThreadedBinaryTree<T>
{
    private ThreadedNode<T> _root;
    private ThreadedNode<T> _pre; // 记录前驱节点

    // 线索化核心逻辑(中序)
    private void ThreadNodes(ThreadedNode<T> node)
    {
        if (node == null) return;
        // 递归线索化左子树
        ThreadNodes(node.Left);
        // 处理当前节点的前驱
        if (node.Left == null)
        {
            node.Left = _pre;
            node.LeftIsThread = true; // 标记为线索
        }
        // 处理前驱节点的后继
        if (_pre != null && _pre.Right == null)
        {
            _pre.Right = node;
            _pre.RightIsThread = true;
        }
        _pre = node; // 更新前驱
        // 递归线索化右子树
        ThreadNodes(node.Right);
    }

    public void BuildThreadedTree(ThreadedNode<T> root)
    {
        _root = root;
        _pre = null;
        ThreadNodes(root);
    }

}

5.3 线索化后中序遍历方法举例

// 中序遍历(利用线索)
    public void InOrderTraversal()
    {
        ThreadedNode<T> current = _root;
        while (current != null)
        {
            // 找到最左节点
            while (!current.LeftIsThread)
            {
                current = current.Left;
            }
            Console.Write(current.Data + " ");
            // 根据后继线索遍历
            while (current.RightIsThread)
            {
                current = current.Right;
                Console.Write(current.Data + " ");
            }
            current = current.Right;
        }

   关于红黑树和b+树 下一篇数据结构 树篇 将会做出彻底详解 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python Bug 修复案例分析:asyncio 事件循环异常引发的程序崩溃 两种修复方法

在 Python 异步编程的工作中&#xff0c;asyncio库为我们提供了高效处理并发任务的强大工具。然而&#xff0c;asyncio在使用过程中也可能因为一些细节处理不当而引发 Bug。下面&#xff0c;我们就来深入分析一个因asyncio事件循环异常导致程序崩溃的典型案例。兴趣的友友可以借…

题单:递归求和

宣布一个重要的事情&#xff0c;我的洛谷有个号叫 题目描述 给一个数组 a:a[0],a[1],...,a[n−1]a:a[0],a[1],...,a[n−1] 请用递归的方式出数组的所有数之和。 提示&#xff1a;递推方程 f(x)f(x−1)a[x]f(x)f(x−1)a[x]; 输入格式 第一行一个正整数 n (n≤100)n (n≤100)…

怎么在excel单元格1-5行中在原来内容前面加上固定一个字?

环境&#xff1a; WPS 2024 问题描述&#xff1a; 怎么在excel单元格1-5行中在原来内容前面加上固定一个字&#xff1f; 解决方案&#xff1a; 1.在Excel中&#xff0c;如果您想在单元格的内容前面添加一个固定的字&#xff0c;可以通过以下几种方法实现&#xff1a; 方法…

OpenHarmony 开源鸿蒙南向开发——linux下使用make交叉编译第三方库——mqtt库

准备工作 请依照这篇文章搭建环境 OpenHarmony 开源鸿蒙南向开发——linux下使用make交叉编译第三方库——环境配置_openharmony交叉编译-CSDN博客 下载 wget ftp://ftp.gnutls.org/gcrypt/gnutls/v3.5/gnutls-3.5.9.tar.xz 解压 tar -xf mkdir ./out cd ./out Cmake命…

数据结构 -- 顺序查找和折半查找

查找的基本概念 基本概念 查找&#xff1a;在数据集合中寻找满足某种条件的数据元素的过程 查找表&#xff08;查找结构&#xff09;&#xff1a;用于查找的数据集合称为查找表&#xff0c;它由同一类型的数据结构元素&#xff08;或记录&#xff09;组成 关键字&#xff1…

信息收集+初步漏洞打点

目标&#xff1a;理解信息收集在渗透测试中的意义&#xff0c;熟悉常用工具用法&#xff0c;完成基本打点测试 一.理论学习&#xff1a; 模块内容说明信息收集分类主动信息收集 vs 被动信息收集目标发现子域名、IP、端口、子站点、目录、接口技术指纹识别Web框架&#xff08;如…

JavaScript【5】DOM模型

1.概述&#xff1a; DOM (Document Object Model)&#xff1a;当页面被加载时&#xff0c;浏览器会创建页面的文档对象模型&#xff0c;即dom对象&#xff1b;dom对象会被结构化为对象树&#xff0c;如一个HTML文档会被分为head&#xff0c;body等部分&#xff0c;而每个部分又…

Cloudflare防火墙拦截谷歌爬虫|导致收录失败怎么解决?

许多站长发现网站突然从谷歌搜索结果中“消失”&#xff0c;背后很可能是Cloudflare防火墙误拦截了谷歌爬虫&#xff08;Googlebot&#xff09;&#xff0c;导致搜索引擎无法正常抓取页面。 由于Cloudflare默认的防护规则较为严格&#xff0c;尤其是针对高频访问的爬虫IP&…

如何在 Windows 11 或 10 的 CMD 中检查固件

检查 Windows 11 或 10 中现有设备的硬件固件版本,可以帮助用户安装和更新准确的驱动程序,进行故障排除活动,确保兼容性以及维护系统性能。因此,在本教程中,我们将讨论如何在命令提示符(CMD)中使用一些命令查找 Windows 服务器或桌面中硬件固件版本的方法。由于本教程将…

进阶-数据结构部分:3、常用查找算法

飞书文档https://x509p6c8to.feishu.cn/wiki/LRdnwfhNgihKeXka7DfcGuRPnZt 顺序查找 查找算法是指&#xff1a;从一些数据之中&#xff0c;找到一个特殊的数据的实现方法。查找算法与遍历有极高的相似性&#xff0c;唯一的不同就是查找算法可能并不一定会将每一个数据都进行访…

基于QT和FFmpeg实现自己的视频播放器FFMediaPlayer(一)——项目总览

在音视频开发的学习过程中&#xff0c;开发一款视频播放器是FFmpeg进阶的最好实战方法。本文将基于 QT 和 FFmpeg 着手实现自定义视频播放器 FFMediaPlayer&#xff0c;作为系列文章的开篇&#xff0c;我们先来整体了解项目的设计思路、架构与配置。 一、软件设计五大原则​ …

【HCIA】浮动路由

前言 我们通常会在出口路由器配置静态路由去规定流量进入互联网默认应该去往哪里。那么&#xff0c;如果有两个运营商的路由器都能为我们提供上网服务&#xff0c;我们应该如何配置默认路由呢&#xff1f;浮动路由又是怎么一回事呢&#xff1f; 文章目录 前言1. 网络拓扑图2. …

使用instance着色

本节我们学习使用instance着色器进行着色 //拾取var handler new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);handler.setInputAction(function(movement){console.log(movement);var pickedObject viewer.scene.pick(movement.position);if(Cesium.defined(picke…

【NLP 72、Prompt、Agent、MCP、function calling】

命运把我们带到哪里&#xff0c;就是哪里 —— 25.5.13 一、Prompt 1.User Prompt 用户提示词 当我们与大模型进行对话时&#xff0c;我们向大模型发送的消息&#xff0c;称作User Prompt&#xff0c;也就是用户提示词&#xff0c;一般就是我们提出的问题或者想说的话 但是我们…

Mysql数据库之集群进阶

一、日志管理 5.7版本自定义路径时的文件需要自己提前创建好文件&#xff0c;不会自动创建&#xff0c;否则启动mysql会报错 错误日志 rpm包(yum) /var/log/mysql.log 默认错误日志 ###查询日志路径 [rootdb01 ~]# mysqladmin -uroot -pEgon123 variables | grep -w log_e…

临床决策支持系统的提示工程优化路径深度解析

引言 随着人工智能技术在医疗领域的迅猛发展,临床决策支持系统(CDSS)正经历从传统规则引擎向智能提示工程的范式转变。在这一背景下,如何构建既符合循证医学原则又能适应个体化医疗需求的CDSS成为医学人工智能领域的核心挑战。本报告深入剖析了临床决策支持系统中提示工程的…

苹果新一代车载系统CarPlay Ultra来袭,全屏接管+ChatGPT助力,智能驾驶要“起飞”

AITOP100获悉&#xff0c;苹果又搞出大动作啦&#xff01;正式推出了新一代车载系统——CarPlay Ultra。这次&#xff0c;苹果可是下了狠功夫&#xff0c;把iPhone和汽车的所有显示屏深度整合到了一起&#xff0c;还首次把ChatGPT引入到了驾驶体验当中。这系统可不简单&#xf…

无线信道的噪声与干扰

目录 1. 无线信道(wireless channel)与电磁波 2.1 电磁波的传输(无线信道传输) 2.2 视线(line of sight)传播与天线高度 2. 信道的数学模型 2.1 调制信道模型 2.1.1 加性噪声/加性干扰 2.1.2 乘性噪声/乘性干扰 2.1.3 随参信道/恒参信道 2.2 编码信道模型 2.3 小结 …

MySQL 8.0 OCP 1Z0-908 101-110题

Q101.which two queries are examples of successful SQL injection attacks? A.SELECT id, name FROM backup_before WHERE name‘; DROP TABLE injection; --’; B. SELECT id, name FROM user WHERE id23 oR id32 OR 11; C. SELECT id, name FROM user WHERE user.id (SEL…

BBR 的 buffer 动力学观感

这周很忙&#xff0c;今天还加了一天班&#xff0c;但还是抽空实现了五一在安徽泾县山区喝着一壶酒写的 BBR ProbeRTT 的想法&#xff0c;没多少行代码&#xff0c;它真就消除了带宽锯齿&#xff0c;皮了个鞋&#x1f45e;&#xff0c;昨天我还在群里说了今天再说说 BBR 的&…