红黑树及其应用介绍(万字长文)

news2024/11/29 12:55:39

红黑树

定义与性质

红黑树是一种特殊的二叉查找树,它遵循了特定的规则使得其具有了平衡性。红黑树的定义包括以下几个方面:

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点是黑色的。
  3. 每个叶子节点(NIL节点,空节点)是黑色的。
  4. 对于任意一个节点,其子节点中黑色节点的数量不少于 1。
  5. 对于任意一个节点,从该节点到其叶子节点的所有路径上,黑色节点数量相同。

红黑树的性质:

  1. 红黑树是一棵二叉查找树。
  2. 红黑树的平衡是指,对于任意一个节点,其左右子树的高度差不超过 1。
  3. 红黑树的查找、插入、删除等操作的时间复杂度都可以做到 O(logn)。
  4. 红黑树的颜色变换规则使得它在插入、删除等操作后能够快速地恢复平衡。

红黑树在很多领域中都有广泛的应用,例如操作系统、数据库、图形学等。它的平衡性和高效的操作性能使得它成为一种非常优秀的数据结构,其结构图如下。
在这里插入图片描述

查找遍历

红黑树的查找遍历有以下几种常用的方法:

  1. 递归遍历:使用递归的方式遍历红黑树,对于每个节点,递归遍历其左子树和右子树。
  2. 前序遍历:先遍历根节点,然后按照前序遍历的方式遍历左子树,最后按照前序遍历的方式遍历右子树。
  3. 中序遍历:先遍历左子树,然后遍历根节点,最后按照中序遍历的方式遍历右子树。
  4. 后序遍历:先遍历左子树,然后按照后序遍历的方式遍历右子树,最后遍历根节点。
  5. 层序遍历:按照层级的方式遍历红黑树,对于每一层,按照从左到右的顺序遍历该层的所有节点。

递归遍历

红黑树的递归遍历是指使用递归的方式遍历红黑树的所有节点。具体的遍历方式可以是前序遍历、中序遍历、后序遍历等。以下是一个使用递归实现红黑树前序遍历的 Java 代码示例:

public void preorderTraversal(TreeNode root) {
    if (root == null) {
        return;
    }
    System.out.print(root.val + " "); // 先输出根节点的值
    preorderTraversal(root.left); // 递归遍历左子树
    preorderTraversal(root.right); // 递归遍历右子树
}

其中,TreeNode表示红黑树的节点,其代码如下:

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

在递归遍历的过程中,需要注意以下几点:

  1. 对于每个节点,先递归遍历其左子树,再递归遍历其右子树。
  2. 对于叶子节点(leftright都为null),不需要进行递归遍历。
  3. 递归终止条件为节点为null

前中后序遍历

红黑树的前序遍历、中序遍历和后序遍历是指按照不同的顺序遍历红黑树的节点。

前序遍历:先访问根节点,然后按照前序遍历的方式遍历左子树,最后按照前序遍历的方式遍历右子树。

中序遍历:先按照中序遍历的方式遍历左子树,然后访问根节点,最后按照中序遍历的方式遍历右子树。

后序遍历:先按照后序遍历的方式遍历左子树,然后按照后序遍历的方式遍历右子树,最后访问根节点。

以下是一个使用递归实现红黑树前序遍历、中序遍历和后序遍历的 Java 代码示例:

import java.util.ArrayList;
import java.util.List;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

/**
 * 前序遍历
 */
public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    preorderTraversal(root, result);
    return result;
}

private void preorderTraversal(TreeNode root, List<Integer> result) {
    if (root == null) {
        return;
    }
    result.add(root.val); // 先访问根节点
    preorderTraversal(root.left, result); // 递归遍历左子树
    preorderTraversal(root.right, result); // 递归遍历右子树
}

/**
 * 中序遍历
 */

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    inorderTraversal(root, result);
    return result;
}

private void inorderTraversal(TreeNode root, List<Integer> result) {
    if (root == null) {
        return;
    }
    inorderTraversal(root.left, result);
    result.add(root.val);
    inorderTraversal(root.right, result);
}

/**
 * 后序遍历
 */
public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    postorderTraversal(root, result);
    return result;
}

private void postorderTraversal(TreeNode root, List<Integer> result) {
    if (root == null) {
        return;
    }
    postorderTraversal(root.left, result);
    postorderTraversal(root.right, result);
    result.add(root.val);
}

层序遍历

红黑树的层序遍历是指按照从根节点到叶子节点的顺序,依次访问每个节点。具体来说,遍历顺序为从根节点开始,按照深度优先搜索的方式遍历每个节点,对于每个节点,先访问其左子树,再访问其右子树。

在 Java 中,可以使用递归或者迭代的方式实现红黑树的层序遍历。

递归实现:

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class RedBlackTree {
    private final TreeNode root;

    public RedBlackTree(TreeNode root) {
        this.root = root;
    }

    public List<Integer> levelOrderTraversal() {
        List<Integer> result = new ArrayList<>();
        levelOrderTraversal(root, 0, result);
        return result;
    }

    private void levelOrderTraversal(TreeNode node, int level, List<Integer> result) {
        if (node == null) {
            return;
        }
        if (result.size() == level) {
            result.add(node.val);
        } else {
            result.set(level, node.val);
        }
        levelOrderTraversal(node.left, level + 1, result);
        levelOrderTraversal(node.right, level + 1, result);
    }
}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

迭代实现:

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class RedBlackTree {
    private final TreeNode root;

    public RedBlackTree(TreeNode root) {
        this.root = root;
    }

    public List<Integer> levelOrderTraversal() {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode current = root;
        while (current != null || !stack.isEmpty()) {
            while (current != null) {
                result.add(current.val);
                stack.push(current);
                current = current.left;
            }
            current = stack.peek();
            current = current.right;
            stack.pop();
        }
        return result;
    }
}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

插入与删除

红黑树是一种特殊的二叉查找树,它遵循了特定的规则使得其具有了平衡性,因此查询、插入和删除操作的时间复杂度都能够保持在对数级别。

红黑树的插入操作如下:

  1. 如果插入的节点的父节点是黑色的,则直接将该节点插入到树中。
  2. 如果插入的节点的父节点是红色的,则需要进行变色操作。具体来说,如果该节点的祖父节点是黑色的,则将该节点的父节点和祖父节点都变成黑色的。如果该节点的祖父节点是红色的,则需要进行旋转操作,将该节点的父节点和祖父节点的颜色进行交换,并且可能需要继续旋转,直到该节点的祖父节点是黑色的为止。

红黑树的删除操作如下:

  1. 如果要删除的节点没有子节点,则直接将该节点删除,并且将其父节点变成黑色的。
  2. 如果要删除的节点只有一个子节点,则将该节点的子节点替代该节点的位置,并且将该节点的父节点和子节点都变成黑色的。
  3. 如果要删除的节点有两个子节点,则需要找到该节点的后继节点,也就是左子树的最大值节点。然后将该节点的后继节点替代该节点的位置,并且将该节点的父节点和后继节点都变成黑色的。如果该节点的后继节点的父节点是红色的,则需要进行变色操作。

在进行变色操作时,需要遵循以下规则:

  1. 每个节点都必须是红色或者黑色。
  2. 根节点是黑色的。
  3. 如果一个节点是红色的,那么它的子节点必须是黑色的(也就是说不存在连续的红色节点)。
  4. 对于任意一个节点,从该节点到其叶子节点的所有路径上,黑色节点数量相同。

这些规则保证了红黑树的平衡性,使得查询、插入和删除操作的时间复杂度都能够保持在对数级别。

插入

红黑树插入节点的代码实现可以分为以下几个步骤:

  1. 新建一个节点,并将新节点的值设置为待插入的值。
  2. 按照红黑树的性质,将新节点插入到合适的位置。
  3. 如果新节点是红色,需要进行旋转操作来保持红黑树的性质。
  4. 更新父节点的指针,使其指向新节点。
  5. 如果根节点为空,将新节点设置为根节点。

以下是红黑树插入节点的详细代码实现:

public class RedBlackTree<Key extends Comparable<Key>, Value> {
    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private Node root;

    private class Node {
        Key key;
        Value value;
        Node left, right;
        int size;
        boolean color;

        Node(Key key, Value value, int size, boolean color) {
            this.key = key;
            this.value = value;
            this.size = size;
            this.color = color;
        }
    }

    private boolean isRed(Node x) {
        if (x == null) {
            return false;
        }
        return x.color == RED;
    }

    private Node rotateLeft(Node h) {
        Node x = h.right;
        h.right = x.left;
        x.left = h;
        x.color = h.color;
        h.color = RED;
        x.size = h.size;
        h.size = size(h.left) + size(h.right) + 1;
        return x;
    }

    private Node rotateRight(Node h) {
        Node x = h.left;
        h.left = x.right;
        x.right = h;
        x.color = h.color;
        h.color = RED;
        x.size = h.size;
        h.size = size(h.left) + size(h.right) + 1;
        return x;
    }

    private void flipColors(Node h) {
        h.color = RED;
        h.left.color = BLACK;
        h.right.color = BLACK;
    }

    public int size() {
        return size(root);
    }

    private int size(Node x) {
        if (x == null) {
            return 0;
        }
        return x.size;
    }

    public void put(Key key, Value value) {
        root = put(root, key, value);
        root.color = BLACK;
    }

    private Node put(Node h, Key key, Value value) {
        if (h == null) {
            return new Node(key, value, 1, RED);
        }

        int cmp = key.compareTo(h.key);
        if (cmp < 0) {
            h.left = put(h.left, key, value);
        } else if (cmp > 0) {
            h.right = put(h.right, key, value);
        } else {
            h.value = value;
        }

        if (isRed(h.right) && !isRed(h.left)) {
            h = rotateLeft(h);
        }
        if (isRed(h.left) && isRed(h.left.left)) {
            h = rotateRight(h);
        }
        if (isRed(h.left) && isRed(h.right)) {
            flipColors(h);
        }

        h.size = size(h.left) + size(h.right) + 1;
        return h;
    }
}

以上代码实现了红黑树的插入操作。在实际应用中,我们可以根据需要选择合适的遍历方式来访问红黑树中的节点。

删除

红黑树删除节点的代码实现可以分为以下几个步骤:

  1. 找到待删除节点。
  2. 如果待删除节点是叶子节点(NIL节点),直接删除。
  3. 如果待删除节点只有一个子节点,用其子节点替换该节点。
  4. 如果待删除节点有两个子节点,找到其右子树中的最小节点(或左子树中的最大节点),用该节点替换待删除节点。
  5. 更新父节点的指针,使其指向待删除节点的子节点。
  6. 如果根节点为空,将待删除节点设置为根节点。

以下是红黑树删除节点的详细代码实现:

public class RedBlackTree<Key extends Comparable<Key>, Value> {
    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private Node root;

    private class Node {
        Key key;
        Value value;
        Node left, right;
        int size;
        boolean color;

        Node(Key key, Value value, int size, boolean color) {
            this.key = key;
            this.value = value;
            this.size = size;
            this.color = color;
        }
    }

    private Node deleteMin(Node h) {
        if (h.left == null) {
            return null;
        }
        h.left = deleteMin(h.left);
        h.size = size(h.left) + size(h.right) + 1;
        return balance(h);
    }

    private Node deleteMax(Node h) {
        if (h.right == null) {
            return null;
        }
        h.right = deleteMax(h.right);
        h.size = size(h.left) + size(h.right) + 1;
        return balance(h);
    }

    private Node balance(Node h) {
        if (isRed(h.right)) {
            h = rotateLeft(h);
        }
        if (isRed(h.left) && isRed(h.left.left)) {
            h = rotateRight(h);
        }
        if (isRed(h.left) && isRed(h.right)) {
            flipColors(h);
        }
        return h;
    }

    private void flipColors(Node h) {
        h.color = RED;
        h.left.color = BLACK;
        h.right.color = BLACK;
    }

    public void delete(Key key) {
        root = delete(root, key);
    }

    private Node delete(Node h, Key key) {
        if (key.compareTo(h.key) < 0) {
            if (!isRed(h.left) && !isRed(h.left.left)) {
                h = moveRedLeft(h);
            }
            h.left = delete(h.left, key);
        } else {
            if (isRed(h.left)) {
                h = rotateRight(h);
            }
            if (key.compareTo(h.key) == 0 && (h.right == null)) {
                return null;
            }
            if (!isRed(h.right) && !isRed(h.right.left)) {
                h = moveRedRight(h);
            }
            if (key.compareTo(h.key) == 0) {
                Node x = min(h.right);
                h.key = x.key;
                h.value = x.value;
                h.right = deleteMin(h.right);
            } else {
                h.right = delete(h.right, key);
            }
        }
        return balance(h);
    }

    private Node min(Node x) {
        if (x.left == null) {
            return x;
        } else {
            return min(x.left);
        }
    }

    private Node max(Node x) {
        if (x.right == null) {
            return x;
        } else {
            return max(x.right);
        }
    }

    private Node moveRedLeft(Node h) {
        flipColors(h);
        if (isRed(h.right.left)) {
            h.right = rotateRight(h.right);
            h = rotateLeft(h);
            flipColors(h);
        }
        return h;
    }

    private Node moveRedRight(Node h) {
        flipColors(h);
        if (isRed(h.left.left)) {
            h = rotateRight(h);
            flipColors(h);
        }
        return h;
    }
}

复杂度分析

时间复杂度

红黑树的插入和删除操作的时间复杂度分析如下:

插入操作:

插入操作的时间复杂度主要取决于红黑树的高度。在最坏情况下,插入操作需要将新节点插入到树的最顶层,并且需要进行多次旋转操作来维护红黑树的性质。因此,插入操作的时间复杂度为 O(logn)。

删除操作:

删除操作的时间复杂度也主要取决于红黑树的高度。在最坏情况下,删除操作需要将被删除节点的父节点和祖先是红色节点,并且需要进行多次旋转操作来维护红黑树的性质。因此,删除操作的时间复杂度为 O(logn)。

总的来说,红黑树的插入和删除操作的时间复杂度都是 O(logn),这使得红黑树成为了一种非常高效的数据结构,适用于需要频繁进行插入和删除操作的场景。

空间复杂度

红黑树插入和删除操作的空间复杂度主要与树的高度相关。在最坏情况下,插入或删除操作可能需要进行多次旋转操作,导致树的高度增加。但是,由于红黑树的性质,树的高度始终不会超过 O(logn),因此插入和删除操作的空间复杂度均为 O(logn)。

在实际实现中,红黑树通常采用自平衡的方式来维护树的性质,避免了过多的旋转操作。这种实现方式的空间复杂度为 O(1)或 O(logn),具体取决于红黑树的具体实现方式。

总的来说,红黑树插入和删除操作的空间复杂度均为 O(logn)或 O(1),是一种非常高效的数据结构。

应用场景

Mysql 数据库索引

在 MySQL 数据库中,红黑树被广泛应用于 B+树索引结构中。

B+树索引是一种平衡的多路查找树,其结构与红黑树类似,具有以下优点:

  1. 快速查找:B+树索引能够快速地查找数据,因为它具有平衡的结构,可以将查找范围限制在最小的范围内。

  2. 高存储密度:B+树索引可以将大量数据存储在索引节点中,从而减少了存储空间的浪费。

  3. 支持范围查询:B+树索引可以支持范围查询,因为它具有顺序存储的特点,可以快速地定位到某个范围内的数据。

在 MySQL 数据库中,B+树索引通常存储在磁盘上,以提高查询效率。当数据量较大时,B+树索引可以显著提高数据库的查询速度。

具体来说,在 MySQL 数据库中,B+树索引是以文件的形式存储的,每个节点存储在一个磁盘块中。B+树索引的每个节点都包含了指向子节点的指针和指向数据记录的指针。在查询时,MySQL 数据库会根据 B+树索引的结构,快速地定位到所需的数据记录,从而提高查询效率。

红黑树在 B+树索引中的应用,使得 B+树索引具有了平衡的结构和快速的查找能力,从而提高了数据库的查询效率。
在这里插入图片描述

Java HashMap

Java HashMap 是一种常用的集合类,它是基于哈希表实现的。下面是 HashMap 的结构和工作原理的详细介绍:

Java 8 中 HashMap 的类定义

public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, java.io.Serializable {
    // 省略了部分代码
}

在这个类定义中,我们可以看到 HashMap 继承了AbstractMap类,实现了Map接口,同时还实现了CloneableSerializable接口。这意味着 HashMap 是一个可克隆和可序列化的集合。

在 HashMap 中,每个键值对都是一个Entry对象,它存储在一个哈希桶中。哈希桶是一个数组,它的长度是 2 的幂次方,以提高哈希表的性能。

  1. 数据结构:

HashMap 使用一个Entry<K,V>数组来存储数据,其中K是键类型,V是值类型。每个Entry对象包含一个键和一个值,它们通过哈希函数(通常是HashCode方法)关联起来。

  1. 哈希函数:

HashMap 使用哈希函数将键转换为数组下标,以确定数据存储在数组中的位置。哈希函数的作用是将任意的键值转换为固定长度的、分散均匀的整数,从而减少冲突的可能性。HashMap 中使用的哈希函数是HashCode方法,它是 Object 类的一个默认方法,每个类都可以重写它以生成自己的哈希值。

  1. 冲突解决:

如果两个键通过哈希函数得到了相同的哈希值,就会发生冲突。HashMap 中解决冲突的方法是将冲突的键值存储在一个链表中,这个链表存储在哈希表的桶中。每个桶都包含一个头节点,指向链表中的第一个元素。当哈希冲突时,新元素将被添加到链表的末尾。

  1. 查找操作:

HashMap 的查找操作非常快速,它通过哈希函数将键转换为数组下标,然后在数组中查找与该键关联的值。如果找到了,就返回该值;如果没有找到,则返回null

  1. 插入操作:

HashMap 的插入操作首先通过哈希函数将键转换为数组下标,然后在数组中查找与该键关联的值。如果找到了,就更新该值的值;如果没有找到,就创建一个新的Entry对象,将其插入到数组的相应位置,并将其链接到哈希表的相应桶中的链表末尾。

  1. 删除操作:

HashMap 的删除操作首先通过哈希函数将键转换为数组下标,然后在数组中查找与该键关联的值。如果找到了,就从链表中删除该值,并将该位置。

Java 8 中的 HashMap

Java 8 中的 HashMap 实现相比之前的版本做了一些改进和优化,主要包括以下几个方面:

  1. 初始容量:在 Java 8 中,HashMap 默认的初始容量是 16,而不是以前的 10。这是一个小的优化,可以减少哈希冲突的可能性,提高哈希表的性能。

  2. 负载因子:负载因子是哈希表中元素个数与哈希表大小的比值。在 Java 8 中,负载因子的默认值是 0.75,而不是以前的 0.75。这是一个较大的优化,可以减少哈希冲突的可能性,提高哈希表的性能。

  3. 红黑树:在 Java 8 中,当哈希表中的元素数量超过阈值(默认为 8)时,HashMap 会将哈希桶转换为红黑树。这是一个较大的优化,可以提高哈希表的查询性能,特别是在哈希冲突较多的情况下。

  4. 遍历器:在 Java 8 中,HashMap 提供了一个新的遍历器,称为foreach遍历器,它可以方便地遍历哈希表中的元素。与以前的版本相比,它更加简洁和易于使用。

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

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

相关文章

使用c的标准库函数创建线程

#include <stdio.h> #include <threads.h> #include <time.h>int thrd_proc(void * varg){// 打印10次int times 10;struct timespec ts {1,0}; // 1秒, 0纳秒while(times--){printf("%s\n",(char *)varg);// 每隔1秒,打印一次thrd_sleep(&t…

23062网络编程day4

作业&#xff1a; 多进程并发服务器根据流程图重新编写。 #include <myhead.h>#define ERR_MSG(msg) do{\fprintf(stderr,"__%d__",__LINE__);\perror(msg);}while(0) #define PORT 8888 #define IP "192.168.114.104" void handler(int sig) {whi…

ELK安装、部署、调试(四)KAFKA消息队列的安装和部署

1.简介 Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以处理消费者在网站中的所有动作流数据。 这种动作&#xff08;网页浏览&#xff0c;搜索和其他用户的行动&#xff09;是在现代网络上的许多社会功能的一个关键因素。 这些数据通常是由于吞吐量的要求而通…

【docker】运行redis

拉取redis镜像 有多种选择&#xff1a; redis&#xff08;基础版&#xff09;redis/redis-stack&#xff08;包含redis stack server和RedisInsight&#xff09;redis/redis-stack-server&#xff08;仅包含redis stack server&#xff09; docker pull redis docker pull r…

AE2018 安装过程

双击打开安装包&#xff0c;大概等五分钟后。 出现下边安装界面。 安装成功。 可以将图标发送到桌面快捷方式。

用列表实现堆栈和用列表实现队列(实现队列最好用 collections.deque)学习

实现队列的代码测试 from collections import dequedeque1 deque([a, b, c]) deque1.append(1) deque1.append(2) deque1.append(3) for i in range(4,20):deque1.append(i)print(deque1)deque1.popleft()print(deque1)运行结果 E:\Python\Python38\python.exe D:/pythonpro…

零撸大肉,赛博尔Seppol游戏,无限制闯关打碎片,装备,直接变现项目。

2023年7月10日&#xff0c;在上海外滩酒店—— 由来自硅谷、华尔街的技术先锋&#xff0c;与中国科技翘楚阿里、腾讯的骨干团队联手呈现&#xff0c;区块链元宇宙游戏塞波尔 Seppol于上海精彩亮相路演。 1&#xff0c;栖息之地&#xff0c;宠物可放入栖息之地进行挖矿&#xf…

在css中设计好看的阴影

在css中设计好看的阴影 在本文中&#xff0c;我们将学习如何将典型的盒子阴影转换为更好看的的阴影。 统一角度 当我们想要一个元素有阴影时&#xff0c;会添加box-shadow属性并修改其中的数字&#xff0c;直到满意为止。 问题是&#xff0c;通过像这样单独创建每个阴影&…

ctfshow—萌新—杂项1

0x00 前言 CTF 加解密合集CTF Web合集 0x01 题目 0x02 Write Up ed400fbcff269bd9c65292a97488168a 首先这是一个md5&#xff0c;然后在线解密一下&#xff0c;解密站点 https://www.somd5.com/ 解密出来的内容是helloctf 然后去掉ctf就是最终的答案 结果就是flag{hello}…

浅谈红队资产信息收集经验

文章目录 子公司资产收集备案号|官网收集子域名|ip收集fofa灯塔ARLX情报社区 资产确认目录扫描Google Hacking绕过CDNnmap端口扫描参数技巧其他常用工具 子公司资产收集 红蓝对抗中往往只会给你目标企业的名称&#xff0c;以及对应的靶标系统地址&#xff0c;而很少有直接从靶标…

error:0308010C:digital envelope routines::unsupported

我打算再学一下这个vue2移动端开发项目&#xff0c;发现我报这个错 error:0308010C:digital envelope routines::unsupported 想了一下 &#xff0c;自己创建vue框架流程没有问题,怎么会报这个错&#xff1b;一想是node原因 注意的地方&#xff1a;无论自己创建的vue项目还是从…

启迪未来:学乐多光屏P90引领儿童智能学习革命

在当今数字化时代&#xff0c;教育方式正经历着巨大的变革&#xff0c;智能硬件为教育领域带来了前所未有的机遇和挑战。学乐多光屏学习机作为一款创新的教育智能硬件产品&#xff0c;以其独特的特点和优势&#xff0c;引领着学习机领域的发展潮流。 1. 多功能融合&#xff1a;…

红黑树(AVL树的优化)下(代码实现)

我们先简单的将节点找到要插入的位置&#xff0c;进行前后链接&#xff08;先不进行调整&#xff09; 解决情况一的 &#xff1a; cur为红&#xff0c;parent是红 &#xff0c; grandfater是黑&#xff0c;uncle存在且为红的情况 情况二情况三&#xff1a;

nuxt3+ts+vue3的ssr项目总结

目录 一、什么是SSR、SEO、SPA&#xff0c;它们之间的关系又是怎样的。 二、VUE做SSR的几种方法 1、插件prerender-spa-plugin 2、VUE开启SSR渲染模式 3、使用NUXT框架 三、NUXT3VUE3TS &#xff08;一&#xff09;基本配置 1、文件夹介绍 assets components pages…

新版HBuilderX在uni_modules创建搜索search组件

1、创建自定义组件 my-search 新版HBuilder没有了 component 文件夹&#xff0c;但是有 uni_modules 文件夹&#xff0c;用来创建组件&#xff1a; 右键 uni_modules 文件夹&#xff0c;点击 新建uni_modules创建在弹出框&#xff0c;填写组件名字&#xff0c;例如&#xff1a…

分布式集群——搭建Hadoop环境以及相关的Hadoop介绍

系列文章目录 分布式集群——jdk配置与zookeeper环境搭建 分布式集群——搭建Hadoop环境以及相关的Hadoop介绍 文章目录 前言 一 hadoop的相关概念 1.1 Hadoop概念 补充&#xff1a;块的存储 1.2 HDFS是什么 1.3 三种节点的功能 I、NameNode节点 II、fsimage与edits…

Go的数据结构-hashmap

开放寻址法和拉链法 runtime.hamp bucket的数据结构 bucket的指针指向这里 map初始化&#xff1a;make 和字面量 make初始化 新建一个hamp结尾体&#xff0c;计算大B&#xff0c;创建一个桶数组 字面量初始化 map的并发解决 sync.map

25.CSS自定义形状按钮与悬停效果

效果 源码 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>CSS Custom Shape Button</title><link rel="stylesheet" href="style.css"> </head> <body&…

对战ChatGPT,创邻科技的Graph+AI会更胜一筹吗?

大模型&#xff08;大规模语言模型&#xff0c;即Large Language Model&#xff09;的应用已经成为千行百业发展的必然。特定领域或行业中经过训练和优化的企业级垂直大模型则成为大模型走下神坛、真正深入场景的关键之路。 但是&#xff0c;企业级垂直大模型在正式落地应用前…

C++调用Python Win10 Miniconda虚拟环境配置

目录 前言1. Win10 安装 Miniconda2. 创建虚拟环境3. 配置C调用python环境4. C调用Python带参函数5.遇到的问题6. 总结 前言 本文记录了Win10 系统下Qt 应用程序调用Python时配置Miniconda虚拟环境的过程及遇到的问题&#xff0c;通过配置Python虚拟环境&#xff0c;简化了Qt应…