TreeMap和TreeSet

news2024/12/25 2:25:39

在这里插入图片描述


前言

在了解TreeSet和TreeMap之前,先让我们介绍一下搜索树的概念。

1. 搜索树

二叉搜索树又称二叉排序树,这颗树要么是一棵空树,要么是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

在这里插入图片描述

那么对于一颗二叉排序树应该具有的基本操作是什么呢?我们知道作为一颗二叉排序树一定满足的条件是左子树上的所有结点都小于根节点,右子树上的所有结点都大于根节点。因此对于一颗二叉搜索树而言,我们应该存在查找、插入、删除的操作。

1.1 查找

二叉排序树根节点的左子树都小于根节点,根节点的右子树都大于右子树,基于这个特点,我们可以按照以下规律查找:

从根节点开始比较,当待查找的结点大于根节点时,我们就到右子树中去找;当待查找的结点小于根节点是,我们就到左子树中去找;当待查找的结点等于根节点时,此时直接返回根节点就可;当查找完整棵树发现该节点不存在时,返回null

//二叉排序树查找操作的实现:
public TreeNode search(int key) {  
    if(isEmpty()) {  
        throw new EmptyException("查找树为空,无法查找");  
    }  
    TreeNode cur = this.root;  
    while(cur != null) {  
        if(cur.val > key) {  
            cur = cur.left;  
        }else if(cur.val < key) {  
            cur = cur.right;  
        }else {  
            return cur;  
        }  
    }  
    return null;  
  
}

1.2 插入

二叉排序树中不能插入相同的元素,当元素已存在时插入失败,也就说二叉排序树中所有数据都是只有一份的。对于插入操作,我们会根据二叉排序树的性质遍历整棵树找到适合的位置进行插入,使得插入结束后仍然是一颗搜索树。

public boolean insert(int key) {  
    TreeNode node = new TreeNode(key);  
    if(isEmpty()) {  
        this.root = node;  
        return true;    }  
    TreeNode cur = this.root;  
    TreeNode parent = null;  
    while(cur != null) {  
        parent = cur;  
        if(cur.val > key) {  
            cur = cur.left;  
        }else if(cur.val < key) {  
            cur = cur.right;  
        }else {  
            System.out.println("插入失败,key值已存在");  
            return false;        }  
    }  
    //找到可插入节点的父节点,判断往结点那边插入  
    if(parent.val > key) {  
        parent.left = node;  
    }else {  
        parent.right = node;  
    }  
    return true;  
}

再插入过程中需要注意保存可插入结点的父节点,同时注意判断往可插入节点的父节点的那边进行插入。

1.3 删除(较难)

设待删除结点为cur,待删除结点的父节点为parent;对于删除结点在树中可能存在以下几种不同的情况:

  1. cur.left == null,待删除结点的左子树为空
  1. cur == root,待删除结点为根结点;则root = cur.right,根结点等于删除结点的右子树结点。
  2. cur != root ; cur = parent.left,待删除结点是父节点的左子树结点;则parent.left = cur.right,待删除结点的父节点的左子树等于删除结点的右子树结点。
  3. cur != root ; cur = parent.right,待删除结点是父节点的右子树结点;则parent.right = cur.right,待删除结点的父节点的右子树等于删除结点的右子树结点。
  1. cur.right == null ,待删除结点的右子树为空
  1. cur == root,待删除结点为根结点;则root = cur.left,根结点等于删除结点的左子树结点。
  2. cur != root ; cur = parent.left,待删除结点是父节点的左子树结点;则parent.left = cur.left,待删除结点的父节点的左子树等于删除结点的左子树结点。
  3. cur != root ; cur = parent.right,待删除结点是父节点的右子树结点;则parent.right = cur.left,待删除结点的父节点的右子树等于删除结点的左子树结点。
  1. cur.left != null && cur.right != null,待删除结点的左右子树都不为空

对于左右子树都不为空的情况下,我们可以使用替换删除法,即寻找一个合适的值来替代掉原来的数,让后将这个“合适的数”原来所在的位置删除掉。

#合适的数: 待删除结点的左子树中的最大值(左子树中的最右边结点)或者待删除结点的右子树中的最小值(右子树中的最左边结点)。

public boolean remove(int key) {  
    if(isEmpty()) {  
        throw new EmptyException("数为空,无法删除");  
    }  
    //找到要删除结点,同时保存他的父节点  
    TreeNode cur = this.root;  
    TreeNode parent = null;  
    while(cur != null) {  
        parent = cur ;  
        if(cur.val > key) {  
            cur = cur.left;  
        }else if(cur.val < key) {  
            cur = cur.right;  
        }else {  
            //找到删除节点了,删除  
            removeNode(parent,cur);  
            return true;        }  
    }  
    System.out.println("删除节点不存在");  
    return false;  
}  
  
//删除结点存在的情况  
//1. 左边为空  
//2. 右边为空  
//3. 左右为空  
//4. 左右不为空  
private void removeNode(TreeNode parent, TreeNode cur) {  
    //删除结点可能是根节点,此时parent结点==null,且记得修改根节点  
    if(cur.left == null) {  
        //同时包括1、3  
        if(cur == root) {  
            root = cur.right;  
        }else if(cur == parent.left) {  
            parent.left = cur.right;  
        }else {  
            parent.right = cur.right;  
        }  
    }else if(cur.right == null) {  
        //处理2  
        if(cur == root) {  
            root = cur.left;  
        }else if(cur == parent.left) {  
            parent.left = cur.left;  
        }else {  
            parent.right = cur.left;  
        }  
    }else {  
        //左右结点都不为空  
        //删除结点:替换删除法:找到替换当前结点的左子树中的最大值(最右结点),或右子树的最小值(最左结点)  
        //将找的值替换到删除结点,然后删去找到替换的值的结点  
        TreeNode targetParent = cur;  
        TreeNode target = cur.right;  
        while(target.left != null) {  
            targetParent = target;  
            target = target.left;  
            //迭代寻找最右子树最左边结点  
        }  
        //替换  
        cur.val = target.val;  
        //右子树中不一定存在左子树,所以最小值可能是右子树的根节点  
        //判断是否存在左分支  
        if(targetParent.left == target) {  
            targetParent.left = target.right;  
        }else {  
            targetParent.right = target.right;  
        }  
    }  
}

这里的替换删除中的最合适的数为右子树中的最小值,targerParent结点保存的是合适结点的父节点,需要特别注意的是:在右子树中寻找最小值时,最小值可能是右子树的根节点,所以我们应该判断最小值是不是根节点。

1.4 搜索树的模拟实现

/**  
 * 二叉搜索树  
 * 时间复杂度:  
 * 好的:O(LogN)  
 * 坏的:O(N),单分支  
 */  
public class BinarySearchTree {  
    static class TreeNode {  
        public int val;  
        public TreeNode left;  
        public TreeNode right;  
  
        public TreeNode(int val) {  
            this.val = val;  
        }  
    }  
    public TreeNode root;  
  
    //插入得保存父节点  
    public boolean insert(int key) {  
        TreeNode node = new TreeNode(key);  
        if(isEmpty()) {  
            this.root = node;  
            return true;        }  
        TreeNode cur = this.root;  
        TreeNode parent = null;  
        while(cur != null) {  
            parent = cur;  
            if(cur.val > key) {  
                cur = cur.left;  
            }else if(cur.val < key) {  
                cur = cur.right;  
            }else {  
                System.out.println("插入失败,key值已存在");  
                return false;            }  
        }  
        //找到可插入节点的父节点,判断往结点那边插入  
        if(parent.val > key) {  
            parent.left = node;  
        }else {  
            parent.right = node;  
        }  
        return true;  
    }  
  
    /**  
     * 搜索元素  
     * 时间复杂度:好的情况下:O(LogN)  
     * 坏的情况下:O(N)  
     * @param key  
     * @return  
     */  
  
    public TreeNode search(int key) {  
        if(isEmpty()) {  
            throw new EmptyException("查找树为空,无法查找");  
        }  
        TreeNode cur = this.root;  
        while(cur != null) {  
            if(cur.val > key) {  
                cur = cur.left;  
            }else if(cur.val < key) {  
                cur = cur.right;  
            }else {  
                return cur;  
            }  
        }  
        return null;  
  
    }  
    public boolean isEmpty() {  
        return this.root == null;  
    }  
  
    public boolean remove(int key) {  
        if(isEmpty()) {  
            throw new EmptyException("数为空,无法删除");  
        }  
        //找到要删除结点,同时保存他的父节点  
        TreeNode cur = this.root;  
        TreeNode parent = null;  
        while(cur != null) {  
            parent = cur ;  
            if(cur.val > key) {  
                cur = cur.left;  
            }else if(cur.val < key) {  
                cur = cur.right;  
            }else {  
                //找到删除节点了,删除  
                removeNode(parent,cur);  
                return true;            }  
        }  
        System.out.println("删除节点不存在");  
        return false;  
    }  
  
    //删除结点存在的情况  
    //1. 左边为空  
    //2. 右边为空  
    //3. 左右为空  
    //4. 左右不为空  
    private void removeNode(TreeNode parent, TreeNode cur) {  
        //删除结点可能是根节点,此时parent结点==null,且记得修改根节点  
        if(cur.left == null) {  
            //同时包括1、3  
            if(cur == root) {  
                root = cur.right;  
            }else if(cur == parent.left) {  
                parent.left = cur.right;  
            }else {  
                parent.right = cur.right;  
            }  
        }else if(cur.right == null) {  
            //处理2  
            if(cur == root) {  
                root = cur.left;  
            }else if(cur == parent.left) {  
                parent.left = cur.left;  
            }else {  
                parent.right = cur.left;  
            }  
        }else {  
            //左右结点都不为空  
            //删除结点:替换删除法:找到替换当前结点的左子树中的最大值(最右结点),或右子树的最小值(最左结点)  
            //将找的值替换到删除结点,然后删去找到替换的值的结点  
            TreeNode targetParent = cur;  
            TreeNode target = cur.right;  
            while(target.left != null) {  
                targetParent = target;  
                target = target.left;  
                //迭代寻找最右子树最左边结点  
            }  
            //替换  
            cur.val = target.val;  
            //右子树中不一定存在左子树,所以最小值可能是右子树的根节点  
            //判断是否存在左分支  
            if(targetParent.left == target) {  
                targetParent.left = target.right;  
            }else {  
                targetParent.right = target.right;  
            }  
        }  
    }  
  
}

我们知道在数据的插入和删除过程中,二叉搜索树最优的情况下能够化为一颗完全二叉树,其平局比较次数为LogN;在最坏的情况下二叉搜索树会退化为一颗单分支的数,其平均比较此事为N/2
在这里插入图片描述

2. TreeSetTreeMap

TreeMap 和 TreeSet 中利用搜索树实现的 Map 和 Set;实际上它们底层中用的数据结构是红黑树,而红黑树是一棵近似平衡的二叉搜索树,即在二叉搜索树的基础之上 + 颜色以及红黑树性质验证。

#模型:一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以模型通常会有两种
模型会有两种:

  1. 纯Key模型,如:
  • 快速查找一个单词是否在词典中
  • 快速查找某个名字在不在通讯录中
  1. Key-Value模型,如:
  • 统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数>
  • 梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号

Map中存储的就是key-value的键值对,Set中只存储了Key。

对于TreeMap和TreeSet,我们将从以下方面进行切入介绍:相同点、不同点、还有常用方法。

2.1 异同点

相同点

  1. TreeMap和TreeSet底层使用了红黑树,也就是说它们都是有序的集合,他们存储的值都是拍好序的。
  2. 运行速度都要比Hash集合慢,他们内部对元素的操作时间复杂度为O(logN),而HashMap/HashSet则为O(1)
  3. 由于 TreeMap和TreeSet底层使用了红黑树,所以当我们实现元素插入的时候,插入的一定是个能够比较的大小的数据(实现Comparable接口或者提供比较器)
    不同点:
  4. 最主要的区别就是TreeSet和TreeMap非别实现Set和Map接口
  5. TreeSet只存储一个对象(Key),而TreeMap存储两个对象(Key和Value),对于TreeMap中仅仅Key对象有序
  6. TreeSet中不存在重复集合(天然去重的集合),即Key的值不会重复;而TreeMap中却拥有可以重复的Value值,存放键值对的Key是唯一的,value是可以重复的

2.2 TreeMap的常见方法

方法介绍
V get(Object key)返回 key 对应的 value
V put(K key, V value)将Key和Value作为键值对,添加到Map中
boolean containsKey(Object key)如果Map中存在key则返回true
V getOrDefault(Object key, V defaultValue)如果Map中有关于key对应的value
则返回Map中的value, 否则返回默认设置的value
Set<Map.Entry<K,V>> entrySet()返回一个set集合,里面的元素是Map.Entry<K, V>

#Map.Entry<K,V>:Map.Entry<K, V> 是Map内部实现的用来存放<key, value>键值对映射关系的内部类,该内部类中主要提供了<key, value>的获取,value的设置以及Key的比较方式。

方法介绍
K getKey()获得entry 中的 key
V getValue()获得entry 中的 value
import java.util.Map;  
import java.util.Set;  
import java.util.TreeMap;  
  
public class Test {  
    public static void main(String[] args) {  
        Map<String, Integer> treeMap = new TreeMap<>();  
  
        //put(Key,Value)存放键值对Key-Value  
        //Key在这里是String类型的name  
        //Value在这里是Integer类型的age  
        System.out.println("-----put(Key,Value)....");  
        treeMap.put("张三", 18);  
        treeMap.put("李四", 22);  
        treeMap.put("王五", 21);  
  
        //get(Key)获取Key"王五"对应的Value年龄  
        System.out.println("-----get(Key)....");  
        Integer age = treeMap.get("王五");  
        System.out.println(age);  
  
        //查看treeMap里是否有"李四"  
        System.out.println("-----containsKey(Object key)....");  
        boolean ret = treeMap.containsKey("李四");  
        System.out.println(ret);  
  
        //getOrDefault(Object key, V defaultValue)  
        System.out.println("-----getOrDefault(Object key , V defaultValue)....");  
        System.out.println(treeMap.getOrDefault("张三", 20));  
        System.out.println(treeMap.getOrDefault("钱七", 20));  
  
        //将treeMap转化为一个Set集合  
        //获取Entry对象,然后获取key和value  
        System.out.println("-----Set<Map.Entry<K,V>> entrySet()....");  
        for (Map.Entry<String, Integer> entry : treeMap.entrySet()) {  
            System.out.println("Key == " + entry.getKey() + "   Value == " + entry.getValue());  
        }  
  
    }  
}

运行结果:
在这里插入图片描述

2.3 TreeSet的常用方法

方法解释
boolean add(E e)添加集合中不存在元素,重复元素不会被添加成功
boolean contains(Object o)判断 o 是否在集合中
Iterator< E > iterator()返回迭代器对象
boolean remove(Object o)删除集合中的 o
boolean isEmpty()检测set是否为空,空返回true,否则返回false
void clear()清空集合
int size()返回set中元素的个数
import java.util.*;  
  
public class Test {  
    public static void main(String[] args) {  
        Set<String> treeSet = new TreeSet<>();  
  
        //add(Key)添加元素  
        System.out.println("-----add(Key).....");  
        treeSet.add("张三");  
        treeSet.add("李四");  
        treeSet.add("王五");  
        treeSet.add("钱七");  
  
        //remove(Key)删除元素 "张三"  
        System.out.println("-----remove(Key).....");  
        treeSet.remove("张三");  
  
        //是否包含元素 "钱七"  
        System.out.println("-----contains(Key).....");  
        boolean isContain = treeSet.contains("钱七");  
        System.out.println("是否包含元素钱七: " + isContain);  
  
        //集合是否为null  
        System.out.println("-----isEmpty().....");  
        boolean empty = treeSet.isEmpty();  
        System.out.println("集合是否为空: " + empty);  
  
        //得到集合元素个数  
        System.out.println("-----size().....");  
        int size = treeSet.size();  
        System.out.println("元素个数: " + size);  
  
        //迭代器遍历得到Set集合内容  
        System.out.println("-----iterator().....");  
        System.out.print("集合里面的元素:");  
        Iterator<String> iterator = treeSet.iterator();  
        while (iterator.hasNext()) {  
            System.out.print(iterator.next() + " ");  
        }  
        System.out.println();  
  
        //删除所有元素  
        System.out.println("-----clear().....");  
        treeSet.clear();  
    }  
}

执行结果如下:
在这里插入图片描述

前面我们提到Set是天然去重的,也就是说我们可以利用Set来完成我们的去重操作,例如:当一个序列中数据为:{1 , 1 , 1, 23 ,77 , 2 , 92 ,77},对于一个TreeSet集合,把每个元素添加到Set集合就可以完成去重操作。

import java.util.*;  
  
public class Test {  
    public static void main(String[] args) {  
        int arr[] = {1 , 1 , 1, 23 ,77 , 2 , 92 ,77};  
        Set<Integer> treeSet = new TreeSet<>();  
  
        //去重前打印  
        System.out.println("去重前:");  
        for (int val : arr) {  
            System.out.print(val + " ");  
        }  
        System.out.println();  
  
  
        //去重  
        for (int val : arr) {  
            treeSet.add(val);  
        }  
  
        //去重后, 打印  
        System.out.println("去重后:");  
        for (int val : treeSet) {  
            System.out.print(val + " ");  
        }  
        System.out.println();  
    }  
}

运行结果如下:
在这里插入图片描述

2.4 总结

  • Set是继承自Collection的一个接口类
  • Set中只存储了key,并且要求key一定要唯一
  • TreeSet的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的
  • Map中存放键值对的Key是唯一的,value是可以重复的
  • TreeMap和TreeSet的底层数据结构是红黑树,所以插入的元素都必须是能够比较大小的

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

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

相关文章

[Qt] 信号与槽:深入浅出跨UI与跨线程的信号发送

文章目录 如何自定义信号并使用自定义信号的步骤1.使用 signals 声明信号2. 信号的返回值是 void3. 在需要发送信号的地方使用 emit4. 使用 connect 链接信号和槽5. 完整代码示例总结 如何跨UI发送信号Qt跨UI发送信号机制详解案例概述Qt 信号与槽机制简介代码逻辑详解主窗口 Wi…

九APACHE

## 一 、HTTP协议与URL * HTTP协议&#xff1a;超文本传输协议&#xff0c;用于从Web服务器传输超文本到本地浏览器的传输协议&#xff0c;属于应用层协议。 超文本语言&#xff0c;用来创建超文本文件的标签 * URL&#xff1a;统一资源定位符&#xff0c;是互联网上标准资源…

centos 8.4学习小结

1.权限委派 2.vim快捷方式 2.1非正常关闭文本处理方式 2.2快捷方式 2.3TAB键补齐安装包 [ rootcloud Packages]# rpm -ivh bash-completion-2.7-5.el8.noarch.rpm 2.4#history 查询历史记录 [rootcloud ~]# vim /etc/profile HISTSIZE1000&#xff08;默认保存1000条历史记…

基于SSM的老年人身心健康监管平台

文未可获取一份本项目的java源码和数据库参考。 选题意义 21世纪是全球人口老龄化的时代。联合国经济和社会事务部人口司发布的统计数据显示&#xff0c;截止到2018年7月,全球60岁及以上人口约为9.62亿&#xff0c;占总人口的比重约为12.8%。2018年底&#xff0c;我国60岁及以…

需求11——解决字段无法清空的两个小bug

目录 背景 第一个小bug——问题阐述 第一个小bug——解决方案 第二个小bug——问题阐述 第二个小bug——解决方案 总结 背景 已经写了一个上午的文章了&#xff0c;写完这篇就可以去吃饭了。这也是这几个月的我写的最后一个小bug文章&#xff0c;把这篇文章写完就搞定了…

vue 数组变化侦测

变更方法 Vue 能够侦听响应式数组的变更方法&#xff0c;并在它们被调用时触发相关的更新。这些变更方法包括: push() pop() shift() unshift() splice() sort() reverse() <template><div><p>点击按钮为列表添加元素</p><button click"cli…

【浏览器】如何正确使用Microsoft Edge

1、清理主页广告 如今的Microsoft Edge 浏览器 主页太乱了&#xff0c;各种广告推送&#xff0c;点右上角⚙️设置&#xff0c;把快速链接、网站导航、信息提要、背景等全部关闭。这样你就能得到一个超级清爽的主页。 网站导航       关闭 …

Mybatis高级查询-一对一查询

表介绍和表关系说明 新建以下4张表 tb_user&#xff1a;用户表 tb_order&#xff1a;订单表 tb_item&#xff1a;商品表 tb_orderdetail&#xff1a;订单详情表 【表关系】 1.tb_user和 tb_order表关系tb_user 《》 tb_order&#xff1a;一对多&#xff0c; 一个人可以下多…

第 4 章 Spring IoC容器之BeanFactory

Spring 的 IoC 容器是一个提供 IoC 支持的轻量级容器&#xff0c;除了基本的 IoC 支持&#xff0c;它作为轻量级容器还提供了 IoC 之外的支持。 Spring 提供了两种容器类型&#xff1a;BeanFactory 和 ApplicationContext&#xff1a; BeanFactory&#xff0c;基础类型 IoC 容…

布隆过滤器(Bloom Filter)详解

一、引言 在处理大量数据的场景中&#xff0c;我们经常会遇到判断一个元素是否在某个集合中的问题。传统的方法可能是使用 HashMap 等集合将数据保存起来&#xff0c;然后进行比较确定&#xff0c;但在元素很多的情况下&#xff0c;这种方式会非常浪费空间&#xff0c;检索速度…

dayu_widgets-简介

前言: 越来越多的人开始使用python来做GUI程序&#xff0c;市面上却很少有好的UI控件。即使有也是走的商业收费协议&#xff0c;不敢使用&#xff0c;一个不小心就收到法律传票。 一、原始开源项目: 偶然在GitHub上发现了这个博主的开源项目。https://github.com/phenom-films…

Fiddler配合wireshark解密ssl

环境&#xff1a; win11&#xff08;wireshark&#xff09;--虚拟机win7&#xff08;Fiddler&#xff09;---虚拟机win7&#xff08;HTTPS站点&#xff09; 软件安装问题&#xff1a; 需要.net环境&#xff0c;NDP461-KB3102436-x86-x64-AllOS-ENU.exe。 安装fiddler后安装下…

Pytest测试用例生命周期管理-Fixture

1、Fixture 用法 Fixture 特点及优势 1&#xff64;命令灵活&#xff1a;对于 setup,teardown,可以不起这两个名字2&#xff64;数据共享&#xff1a;在 conftest.py 配置⾥写⽅法可以实现数据共享&#xff0c;不需要 import 导⼊。可以跨⽂件共享3&#xff64;scope 的层次及…

从FastBEV来学习如何做PTQ以及量化

0. 简介 对于深度学习而言&#xff0c;通过模型加速来嵌入进C是非常有意义的&#xff0c;因为本身训练出来的pt文件其实效率比较低下&#xff0c;在讲完BEVDET后&#xff0c;这里我们将以CUDA-FastBEV作为例子&#xff0c;来向读者展示如何去跑CUDA版本的Fast-BEV&#xff0c;…

动态规划算法-路径问题——LCR.166.珠宝的最高价值

1.题目解析 题目来源&#xff1a;LCR.166珠宝的最高价值 原名&#xff1a;剑指offer47.礼物的最大价值 测试用例 2.算法原理 1.状态表示 创建dp表&#xff0c;dp[i][j]表示从最左上角到该目标位置的最大礼物值&#xff0c;代表dp[i][j]的状态 2.状态转移方程 目标位置的最大礼物…

SOMEIP_ETS_178: Subscribe_using_wrong_SOMEIP_MessageID

测试目的&#xff1a; 验证DUT能够拒绝一个SOME/IP头部使用错误消息ID进行服务发现的SubscribeEventgroup消息&#xff0c;并以SubscribeEventgroupNAck作为响应。 描述 本测试用例旨在确保DUT遵循SOME/IP协议&#xff0c;当接收到一个使用错误消息ID的服务发现SubscribeEve…

【livox】雷达初始化成功,但没有点云(已解决)

设备&#xff1a; 一台orin&#xff08;arm&#xff09; 接网线&#xff0c;本地ip&#xff1a;192.168.1.6 livox雷达&#xff1a;HAP 雷达初始ip&#xff1a;192.168.1.100 实物如下图&#xff1a; 然后 安装 livox_SDK 和 驱动 livox_ros_driver2 参考 【Livox】安…

通信界的5G-A/F5G-A新技术,你知道多少?

2024年已经过去了一大半&#xff0c;风起云涌的AI浪潮&#xff0c;又发生了不小的变化。 一方面&#xff0c;AI大模型的复杂度不断提升&#xff0c;模型参数持续增加&#xff0c;智算集群的规模也随之增加。万卡级、十万卡级集群&#xff0c;已经逐渐成为训练标配。这对智算网络…

心觉:开发潜意识的详细流程和步骤是什么

Hi&#xff0c;我是心觉&#xff0c;与你一起玩转潜意识、脑波音乐和吸引力法则&#xff0c;轻松掌控自己的人生&#xff01; 挑战每日一省写作195/1000天 最近领教了一下潜意识的力量和吸引力法则 我想要一张可以放在榻榻米壁柜上的迷你型的电动升降桌&#xff0c;桌面60cm…

《花100块做个摸鱼小网站! 》第七篇—谁访问了我们的网站?

⭐️基础链接导航⭐️ 服务器 → ☁️ 阿里云活动地址 看样例 → &#x1f41f; 摸鱼小网站地址 学代码 → &#x1f4bb; 源码库地址 一、前言 大家好呀&#xff0c;我是summo&#xff0c;最近发生了些事情(被裁员了&#xff0c;在找工作中)导致断更了&#xff0c;非常抱歉。…