边学边记——数据结构☞和搜索有关的数据结构(搜索树,Set,Map,哈希表)

news2024/12/28 2:35:40

目录

模型

一、搜索

1. 场景

2. 搜索树

2.1 概念

2.2 查找

2.3 插入

2.4 删除

2.5 实现 

2.6 性能分析

2.7 和Java的关系

二、Set

1. 常见方法

2. 注意

三、Map

1. 关于Map.Entry的说明,>

2. Map的常用方法说明

3. 注意

四、哈希表

1. 概念

2. 冲突

2.1 概念

2.2 避免

3. 降低 / 解决冲突的办法

3.1 降低冲突的办法(提前准备)

3.2 降低冲突的办法(解决冲突)

4. 冲突严重时的解决办法

5. 实现

6. 性能分析

7. 和java类集的关系


模型

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

  • 纯key模型:Set接口(集合)
  • key-value模型:Map接口(映射)

一、搜索

1. 场景

(1). 静态的有序数组

二分查找——支持随机访问+有序

查找快,插入删除慢,所以只适合静态数据集(数据变化很少的情况)

(2). 搜索树

平衡搜索树:

AVL树/红黑树(内存),B-树系列(B+树),R-树系列

(3). 哈希表

2. 搜索树

2.1 概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

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

如图: 

 特点:中序遍历的结果是有序的。

2.2 查找

若根节点不为空:

  • 如果根节点key == 查找key,返回true。
  • 如果根节点key > 查找key,在其左子树查找。
  • 如果根节点key < 查找key,在其右子树查找。

否则,返回false。

2.3 插入

  • 如果树为空树,即 根 == null,直接插入。
  • 如果树不是空树,安装查找逻辑确定插入位置,插入新结点。

2.4 删除

设待删除结点为 cur, 待删除结点的双亲结点为 parent

1. cur.left == null

  • cur 是 root,则 root = cur.right
  • cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
  • cur 不是 root,cur 是 parent.right,则 parent.right = cur.right

2. cur.right == null

  • cur 是 root,则 root = cur.left
  • cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
  • cur 不是 root,cur 是 parent.right,则 parent.right = cur.left

3. cur.left != null && cur.right != null

  • 需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小)(也可以是左子树中最大的),用它的值填补到被删除节点中,再来处理该结点的删除问题。 

2.5 实现 

public class BinarySearchTree {
    public static class Node {
        int key;
        Node left;
        Node right;
        public Node(int key) {
            this.key = key;
        }
    }

    private Node root = null;

    //查找的操作
    public Node search(int key) {
        Node cur = root;
        while (cur != null) {
            if (key == cur.key) {
                return cur;
            } else if (key < cur.key) {
                cur = cur.left;
            } else {
                cur = cur.right;
            }
        }
        return null;
    }

    //插入的操作
    //插入过程中可能会失败(key重复了)
    public boolean insert(int key) {
        if (root == null) {
            root = new Node(key);
            return true;
        }
        Node cur = root;
        //记录双亲结点
        Node parent = null;
        while (cur != null) {
            if (key == cur.key) {
                //key重复
                return false;
            } else if (key < cur.key) {
                parent = cur;
                cur = cur.left;
            } else {
                parent = cur;
                cur = cur.right;
            }
        }

        //cur == null
        Node node = new Node(key);
        if (key < parent.key) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        return true;
    }

    //删除的操作
    public boolean remove(int key) {
        Node cur = root;
        Node parent = null;
        while (cur != null) {
            if (key == cur.key) {
                break;
            } else if (key < cur.key) {
                parent = cur;
                cur = cur.left;
            } else {
                parent = cur;
                cur = cur.right;
            }
        }
        // 该元素不在二叉搜索树中
        if(null == cur){
            return false;
        }
        /* 根据cur的孩子是否存在分四种情况
        1. cur左右孩子均不存在
        2. cur只有左孩子
        3. cur只有右孩子
        4. cur左右孩子均存在
        看起来有四种情况,实际情况1可以与情况2或者3进行合并,只需要处理是那种情况即可
        除了情况4之外,其他情况可以直接删除
        情况4不能直接删除,需要在其子树中找一个替代节点进行删除
        */
        if(cur.left == null){   //3.
            if(cur == root){    //parent == null
                root = cur.right;
            }else if(parent.left == cur){
                parent.left = cur.right;
            }else{
                //parent.right == cur;
                parent.right = cur.right;
            }
        }else if(cur.right == null){    //2.
            if(cur == root){
                root = cur.left;
            }else if(parent.left == cur){
                parent.left = cur.left;
            }else{
                parent.right = cur.left;
            }
        }else{  //4.
            //cur.left != null && cur.right != null
            //替换删除,此处选择右子树中最小的一个(即右子树中最左的一个)
            Node toDeleteParent = cur;
            Node toDelete = cur.right;
            while(toDelete.left != null){
                toDeleteParent = toDelete;
                toDelete = toDelete.left;
            }

            //此时toDelete是我们要删除的结点
            //先把值替换
            cur.key = toDelete.key;

            //删除toDelete
            //需要判断此时是toDelete左孩子还是右孩子
            //如果cur没有左孩子,那么此时toDelete就是cur的右孩子,此时toDeleteParent == cur
            if(toDeleteParent.left == toDelete){
                toDeleteParent.left = toDelete.right;
            }else{
                //tpDeleteParent.right == toDelete
                toDeleteParent.right = toDelete.right;
            }
        }
        return true;
    }
}

2.6 性能分析

        插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

        对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

        但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

  • 最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log_{2}N
  • 最差情况下,二叉搜索树退化为单支树,其平均比较次数为:\frac{N}{2}

2.7 和Java的关系

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

二、Set

Set官方文档

        Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key。

1. 常见方法

方法解释
boolean add(E e)添加元素,但重复元素不会被添加成功
void clear()清空集合
boolean contains(Object o)判断 o 是否在集合中
Iterator<E> iterator()返回迭代器
boolean remove(Object o)删除集合中的 o
int size()返回set中元素的个数
boolean isEmpty()检测set是否为空,空返回true,否则返回false
Object[] toArray()将set中的元素转换为数组返回
boolean containsAll(Collection<?> c)集合c中的元素是否在set中全部存在,是返回true,否则返回
false
boolean addAll(Collection<? extends
E> c)
将集合c中的元素添加到set中,可以达到去重的效果

2. 注意

  • Set是继承自Collection的一个接口类。
  • Set中只存储了key,并且要求key一定要唯一。
  • Set的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中。
  • Set最大的功能就是对集合中的元素进行去重。
  • 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。
  • Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入。
  • Set中不能插入null的key。

TreeSet和HashSet的区别:

Set底层结构TreeSetHashSet
底层结构红黑树哈希桶
插入/删除/查找时间
复杂度
O(1)
是否有序关于Key有序不一定有序
线程安全不安全不安全
  • 插入/删除/查找区别
按照红黑树的特性来进行插入和删除1. 先计算key哈希地址 2. 然后进行
插入和删除
比较与覆写key必须能够比较,否则会抛出
ClassCastException异常
自定义类型需要覆写equals和
hashCode方法
应用场景需要Key有序场景下Key是否有序不关心,需要更高的
时间性能

三、Map

Map官方文档

Map是一个接口类,该类没有继承自Collection,该类中存储的是<K,V>结构的键值对,并且K一定是唯一的,不能重复。

1. 关于Map.Entry<K, V>的说明

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

方法解释
K getKey()返回 entry 中的 key
V getValue()返回 entry 中的 value
V setValue(V value)将键值对中的value替换为指定value

注意:Map.Entry<K,V>并没有提供设置Key的方法。

2. Map的常用方法说明

方法解释
V get(Object key)返回 key 对应的 value
V getOrDefault(Object key, V defaultValue)返回 key 对应的 value,key 不存在,返回默认值
V put(K key, V value)设置 key 对应的 value
V remove(Object key)删除 key 对应的映射关系
Set<K> keySet()返回所有 key 的不重复集合
Collection<V> values()返回所有 value 的可重复集合
Set<Map.Entry<K, V>> entrySet()返回所有的 key-value 映射关系
boolean containsKey(Object key)判断是否包含 key
boolean containsValue(Object value)判断是否包含 value

3. 注意

  • Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap。
  • Map中存放键值对的Key是唯一的,value是可以重复的
  • Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)。
  • Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。
  • Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入。
  • TreeMap和HashMap的区别:
    Map底层结构TreeMapHashMap
    底层结构红黑树哈希桶
    插入/删除/查找时间
    复杂度
    O(1)
    是否有序关于Key有序无序
    线程安全不安全不安全
    插入/删除/查找区别需要进行元素比较通过哈希函数计算哈希地址
    比较与覆写key必须能够比较,否则会抛出
    ClassCastException异常
    自定义类型需要覆写equals和
    hashCode方法
    应用场景需要Key有序场景下Key是否有序不关心,需要更高的
    时间性能

四、哈希表

        哈希表:固定长度的数组(顺序表),也是一种搜索的数据结构。

        顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(log_{2}N ),搜索的效率取决于搜索过程中元素的比较次数。

        理想的搜索方法:可以不经过任何比较一次直接从表中得到要搜索的元素

        如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

1. 概念

当向该结构中:

  • 插入元素:根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
  • 搜索元素:对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。

        该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(HashTable)(或者称散列表)

例:

数据集合{1,7,6,4,5,9};

哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小。



 但是这种方式可能会产生冲突(碰撞),称之为哈希冲突(哈希碰撞)。 

2. 冲突

2.1 概念

        对于两个数据元素的关键字 和 (i != j),有 != ,但有:Hash( ) == Hash( ),即:不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。

        把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。

2.2 避免

        哈希冲突是必然的,无法避免的。

虽然冲突是不可避免的,但是要想办法降低冲突率。

3. 降低 / 解决冲突的办法

3.1 降低冲突的办法(提前准备)

3.1.1 哈希函数的设计——除留余数法

常见哈希函数:直接定制法,除留余数法,平方取中法,折叠法,随机数法,数学分析法。

一般我们使用——除留余数法:

        设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:

        Hash(key) = key% p(p<=m),将关键码转换成哈希地址。

3.1.2 负载因子调节

散列表的载荷因子定义为:\alpha = 填入表中的元素个数 / 散列表的长度

降低冲突率——设定一个阈值,当负载因子超过阈值时,进行数组的扩容。

3.2 降低冲突的办法(解决冲突)

3.2.1 封闭区间中解决(闭散列)——线性探测和二次探测

a. 线性探测

例:数据集合{1,7,6,4,5,9,44};

        比如在之前的场景,现在需要插入元素44,先通过哈希函数计算哈希地址,下标为4,因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。

线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

当插入44时,hash(44) = 44 % 4 = 4,但是4已经被占用,所以只能继续往后放,找到后面的第一个空位置即8位置放入44。

b. 二次探测

线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为:H_{i} = (H_{0} \pm i^{2}) % m,其中:i = 1,2,3....,H_{0} 是通过散列函数Hash(x)对元素的关键码key进行计算得到的位置,m是表的大小。

平均查找长度(成功/失败)的计算:

成功:sum = (查找成功的总次数)/ 数据集合总个数个数

失败:sum = (查找失败的总次数)/ 数据集合总个数个数

3.2.2 不封闭区间中解决(开散列)——哈系桶(链地址法)

        开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

还是上面的例子:

数据集合{1,7,6,4,5,9};

        开散列中每个桶中放的都是发生哈希冲突的元素。开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜索了。

4. 冲突严重时的解决办法

        哈希桶其实可以看作将大集合的搜索问题转化为小集合的搜索问题了,那如果冲突严重,就意味着小集合的搜索性能其实也时不佳的,这个时候我们就可以将这个所谓的小集合搜索问题继续进行转化,例如:

  • 每个桶的背后是另一个哈希表
  • 每个桶的背后是一棵搜索树

5. 实现

        哈希表的实现——除留余数法。

//元素类型:long类型
//元素取值范围:>= 0
//哈希函数:除留余数法:key % array.length
public class HashTableV1 {
    static class ListNode{
        long key;
        ListNode next;

        ListNode(long key){
            this.key = key;
        }
    }

    private ListNode[] array;
    private int size;

    public HashTableV1(){
        array = new ListNode[8];
        size = 0;
    }

    private int hashCode(long key){
        return (int)key % array.length;
    }

    public boolean add(long key){
        int index = hashCode(key);
        for (ListNode cur = array[index]; cur != null; cur = cur.next){
            if(cur.key == key){
                //重复
                return false;
            }
        }

        ListNode node = new ListNode(key);
        node.next = array[index];
        array[index] = node;
        size++;

        //由于负载因子过大,需要扩容
        if(1.0 * size / array.length > 0.75){
            grow();
        }
        return true;
    }

    private void grow(){
        ListNode[] newArray = new ListNode[this.array.length * 2];
        for (int i = 0; i < array.length; i++) {
            ListNode cur = array[i];
            while (cur != null){
                int index = (int) cur.key % newArray.length;

                ListNode next = cur.next;
                cur.next = newArray[index];
                newArray[index] = cur;

                cur = next;
            }
        }

        this.array = newArray;
    }

    public boolean remove(long key){
        int index = hashCode(key);
        if(array[index] == null){
            return false;
        }
        //头删
        if(array[index].key == key){
            array[index] = array[index].next;
            size--;
            return true;
        }

        ListNode prev = array[index];
        ListNode cur = array[index].next;
        while (cur != null){
            if(cur.key == key){
                prev.next = cur.next;
                size--;
                return true;
            }

            prev = cur;
            cur = cur.next;
        }

        return false;
    }

    //平均时间复杂度:O(1)
    public boolean contains(long key){
        //1. 把key转成下标——求哈希值的过程
        int index = hashCode(key);
        //2. 要查找的链表的头结点就是
        // 此时index不会越界
        ListNode head = array[index];
        for (ListNode cur = head; cur != null ; cur = cur.next) {
            if(cur.key == key){
                return true;
            }
        }
        return false;
    }
}

6. 性能分析

        虽然哈希表一直在和冲突做斗争,但在实际使用过程中,我们认为哈希表的冲突率是不高的,冲突个数是可控的,也就是每个桶中的链表的长度是一个常数,所以,通常意义下,我们认为哈希表的插入/删除/查找时间复杂度是O(1)。

7. 和java类集的关系

  • HashMap 和 HashSet 即 java 中利用哈希表实现的 Map 和 Set。
  • java 中使用的是哈希桶方式解决冲突的。
  • java 会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树)。
  • java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key 的 equals 方法。所以如果要用自定义类作为 HashMap 的 key 或者 HashSet 的值,必须覆写hashCode 和 equals 方法,而且要做到 equals 相等的对象,hashCode 一定是一致的。

如有建议或想法,欢迎一起讨论学习~

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

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

相关文章

[论文分享] VOS: Learning What You Don‘t Know by Virtual Outlier Synthesis

这篇文章是ICLR‘ 2022的一篇文章。 No.contentPAPER{ICLR’ 2022} VOS: Learning What You Don’t Know by Virtual Outlier SynthesisURL论文地址CODE代码地址 Motivation 现有OOD Detection方法大多依赖于真实的离群点数据集进行模型正则化&#xff0c;实际应用中过于昂…

常用位运算

一、求解二进制表示的第k位数字 #include<iostream> using namespace std;int main() {int n 10; // 例如&#xff0c;十进制10用二进制表示为1010 for(int k3;k>0;k--)cout << (n >> k & 1);// 第一步&#xff1a;右移k位// 第二步&#xff1a; &am…

JVM基础总结

文章目录 一、程序计数器二、Java虚拟机栈栈内存溢出【StackOverflowError】线程运行诊断 三、本地方法栈【Native Method Stacks】四、堆【Head】线程共享堆内存溢出【OutOfMemoryError&#xff1a;Java heap space】堆内存诊断 五、方法区【Method Area】线程共享运行时常量池…

Springboot +Flowable,为流程设置租户

一.简介 什么叫flowable的租户呢&#xff1f;这边举个例子&#xff1a; 假设现在有 A、B、C、D 四个子系统&#xff0c;四个子系统都要部署同一个名为 leave 的流程&#xff0c;如何区分四个不同子系统的的流程呢&#xff1f;通过租户就可以解决这个问题。Flowable 中的租户其…

dell r750服务器安装centos系统全记录

1、启动盘制作 1.1 下载系统 打开 https://www.centos.org/download/ 任意选择一个镜像网站&#xff0c;博主选择的是163镜像 下载内存为4g镜像文件 这里也可以参考 https://blog.csdn.net/weixin_46703995/article/details/121191113 1.2 下载启动盘制作软件 linux系统一…

Denoising Diffusion Probabilistic Model,DDPM阅读笔记——(二)

Denoising Diffusion Probabilistic Model&#xff0c;DDPM阅读笔记 一、去噪扩散概率模型&#xff08;Denoising Diffusion Probabilistic Model&#xff0c;DDPM&#xff09; 一、去噪扩散概率模型&#xff08;Denoising Diffusion Probabilistic Model&#xff0c;DDPM&…

C++数据结构:手撕红黑树

目录 一. 红黑树的概念及结构 二. 红黑树节点的定义 三. 红黑树节点的插入 3.1 初步查找插入节点的位置并插入节点 3.2 红黑树结构的调整 3.3 红黑树节点插入完整版代码 四. 红黑树的结构检查 4.1 检查是否为搜索树 4.2 检查节点颜色是否满足要求 附录&#xff1a;红黑…

TypeScript进阶

目录 TypeScript 与 Vue 文档说明 vscode 插件说明 准备页面基本结构 defineProps与Typescript defineEmits与Typescript ref与Typescript reactive与Typescript computed与Typescript 事件对象与Typescript 模板Ref与Typescript 可选链操作符和非空断言 TypeScript…

21.网络爬虫—js逆向详讲与实战

网络爬虫—js逆向 js逆向JavaScript逆向的详细讲解实战演示有道翻译设置密钥和初始向量对密钥和初始向量进行哈希处理创建AES对象并解密消息移除padding并返回结果 前言&#xff1a; &#x1f3d8;️&#x1f3d8;️个人简介&#xff1a;以山河作礼。 &#x1f396;️&#x1f…

python基于卷积神经网络实现自定义数据集训练与测试

样本取自岩心照片&#xff0c;识别岩心是最基础的地质工作&#xff0c;如果用机器来划分岩心类型则会大大削减工作量。 下面叙述中0指代Anhydrite_rock&#xff08;膏岩&#xff09;&#xff0c;1指代Limestone&#xff08;灰岩&#xff09;&#xff0c;2指代Gray Anhydrite_r…

深度学习-第T6周——好莱坞明星识别

深度学习-第T6周——好莱坞明星识别 深度学习-第T6周——好莱坞明星识别一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目3、查看数据 四、数据预处理1、 加载数据1、设置图片格式2、划分训练集3、划分验证集4、查看标签 2、数据可视化3、检查数据4、配置数据集 …

Flutter学习之旅 - 页面布局Stack层叠组件

文章目录 StackPositioned定位布局浮动导航(StackPositioned)FlutterMediaQuery获取屏幕宽度和高度StackAlign Stack Stack意思是堆的意思&#xff0c;我们可以用Stack结合Align或者Stack结合Positioned来实现页面的定位布局 属性说明alignment配置所有元素显示位置children子组…

23.Lambda表达式

Lambda表达式 一、Lambda表达式背景 Lambda 表达式(lambda expression)是一个匿名函数&#xff0c;Lambda表达式基于数学中的λ演算得名&#xff0c;直接对应于其中的lambda抽象(lambda abstraction)&#xff0c;是一个匿名函数&#xff0c;即没有函数名的函数。Lambda表达式…

2023-05-05 背包问题

背包问题 1 01背包和完全背包问题 01背包问题 有N件物品和一个容量为V的背包&#xff0c;第i件物品的体积是v[i]、价值是w[i]&#xff0c;每种物品只可以使用一次&#xff0c;求将哪些物品放入背包可以使得价值总和最大。这里的w是weight即权重的意思 这是最基础的背包问题&a…

【飞书ChatGPT机器人】飞书接入ChatGPT,打造智能问答助手

文章目录 前言环境列表视频教程1.飞书设置2.克隆feishu-chatgpt项目3.配置config.yaml文件4.运行feishu-chatgpt项目5.安装cpolar内网穿透6.固定公网地址7.机器人权限配置8.创建版本9.创建测试企业10. 机器人测试 转载自远控源码文章&#xff1a;飞书接入ChatGPT - 将ChatGPT集…

Ubuntu 如何查看 CPU 架构、系统信息、内核版本、版本代号?

Ubuntu 查看 CPU 架构、系统信息、内核版本、版本代号等相关信息有很多方式&#xff0c;本文介绍几种常用的命令。 x86 架构与 ARM 架构的 CPU 架构不同&#xff0c;如果回显为 aarch64 表示为 ARM 架构&#xff0c;如果回显为 x86_64 表示为 x86 架构&#xff0c;参考《CPU 架…

Prometheus快速入门

Prometheus快速入门 环境准备 三台主机&#xff0c;配置好主机名 各自配置好主机名 # hostnamectl set-hostname --static server.cluster.com ... 三台都互相绑定IP与主机名 # vim /etc/hosts 192.168.126.143 server.cluster.com 192.168.126.142 agent.clu…

归并排序(看了就会)

目录 概念1. 基本思想2. 实现逻辑3. 复杂度分析4、代码 概念 归并排序&#xff0c;是创建在归并操作上的一种有效的排序算法。算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用&#xff0c;且各层分治递归可以同时进行。归并排序思路简单&…

智头条|欧盟达成《人工智能法》协议,全球前沿科技齐聚AWE 2023

行业动态 华为云联手多方推进数字化&#xff0c;软通动力深度参与 华为云宣布启动“‘百城万企’应用现代化中国行”&#xff0c;旨在推动应用现代化进程、助力数字中国高质量落地。软通动力是该行动的参与者之一&#xff0c;共同探索符合区域特点、产业趋势、政企现状的数字化…

Python进阶(Linux操作系统)

一&#xff0c;操作系统 1.1&#xff0c;Linux系统基础操作 1.2&#xff0c;linux进程与线程 1.2.1并发&#xff0c;并行 &#xff08;1&#xff09;并发&#xff1a;在一段时间内交替的执行多个任务&#xff1a;对于单核CPU处理多任务&#xff0c;操作系统轮流让让各个任务…