【数据结构】Map和Set

Map和Set

1. 搜索树

1.1 概念

二叉搜索树是左子树比根节点小,右子树比根节点大的二叉树。(如果左右子树不为空的话是这样,但是左右子树也可以为空)

1.2 操作——查找

查找的思想与二分查找类似。

如果根节点的值和所要查找的值相同,那么就返回。

如果根节点的值比查找的值小,那么就往这个结点的右子树走。

如果根节点的值比查找的值大,那么就往这个结点的左子树走。

(每次都可以筛选掉一半的数据)

1.2.1 代码
/*** 操作——搜索* @param val 搜索的值* @return boolean*/
public boolean search(int val) {
// 1. 定义一个cur进行遍历
TreeNode cur = root;
// 2. 比较
while (cur != null) {if (cur.val < val) {cur = cur.right;} else if (cur.val > val) {cur = cur.left;} else  {return true;}
}
return false;
}

1.3 操作——插入

插入和查找差不多。

先找到要插入数据应该存放的位置(一定会是叶子结点)

然后看是放在这个结点的左边还是右边。

/*** 操作——插入* @param val*/
public boolean insert(int val) {// 1. 如果是空树if (root == null) {root = new TreeNode(val);return true;}// 2. 定义一个cur进行遍历,一个parent进行保存上一步cur的位置TreeNode cur = root;TreeNode parent = cur;while (cur != null) {// 保存这次的cur结点,方便后续的插入结点parent = cur;if (cur.val < val) {cur = cur.right;} else if (cur.val > val) {cur = cur.left;} else  {return false;}}if (parent.val < val) {parent.right = new TreeNode(val);} else {parent.left = new TreeNode(val);}return true;
}

1.4 操作——删除(难点)

删除的情况分为三种:

规定 node为将要删除的结点,parent 为要删除结点的父节点。

  1. node.left = null

    1.1 如果 node是根节点,root.right = node.right;

    1.2 如果node不是根节点,是parent 的右子树, parent.right = node.right

    1.3 如果node不是根节点,是parent 的左子树, parent.left= node.left

  2. node.right = null

    1.1 如果 node是根节点,root.left= node.left;

    1.2 如果node不是根节点,是parent 的右子树, parent.right = node.right

    1.3 如果node不是根节点,是parent 的左子树, parent.left= node.left

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

    使用 “替罪羊” 方式进行删除:并灭有真正删除node结点,而是将node左子树的最大值进行替换上去,然后

/*** 删除的子函数* @param node* @param parent*/
public void removeNode(TreeNode node, TreeNode parent) {// 叶子节点——直接删除if (node.left == null && node.right == null) {node = null;}// 1. node没有左子树if (node.left == null) {if (node == parent.left) {parent.left = node.right;} else {parent.right = node.left;}// 2. node 没有右子树} else if (node.right == null) {if (node == parent.left) {parent.left = node.right;} else {parent.right = node.left;}// 3. node 左右子树都有// “替罪羊” 删除方式} else {// 找到node左子树的最右边,即为左子树中最大的值,进行填补;也可以选择右子树的最大值进行填补(最左边// target即为替罪羊TreeNode target = node.left;TreeNode targetParent = target;while (target.right != null) {targetParent = target;target = target.right;}// 到达最右边node.val = target.val;// “虚假”的删除targetParent.right = target.left;// target没有右子树,直接接管左子树即可}
}

1.5 性能分析

最好的情况:为平衡二叉树的时候,即为log2N。

最坏的情况:为单分支树的时候,即为N(产生了AVL树来防止这种情况的发生,其可以左旋,右旋,左右双旋,右左双旋)

2. 搜索

2.1 场景

在以往学过的搜索思想中,我们只学过二分查找(log2N,必须是有序的数组)和直接遍历(n,对数据无要求,效率低下),这种思想对于静态的数据能够有效地检索,但是无法进行删除、添加操作,但是,Map和Set可以进行动态的操作。

2.2 模型

Map:存储的是key-value键值对,key就是所要存储的数据据,value就是这个数据所附带的值(可以是这个key出现的次数,也可以是这个key的关键字等信息)。

Set:存储的是key的集合,不能够有重复,底层仍然使用Map进行初始化。

3. Map的使用

3.1 关于Map的说明

Map 是单独的一个接口,没有继承自Collection接口,并且存储的类型是<key, value>,key不可以重复(搜索的时候就是按照key的值进行搜索,重复便不能够进行搜索)

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

Entry<K, V> 是Map中的一个内部接口,其能够返回Map的key以及value,并且能够设置value(但是没有提供设置key的方法),Entry<K, V>返回的是一个包含<K, V>键值对的对象,能够用其初始化Iterator,可直接定义变量。

Map.Entry<K, V>能够表示Map的映射项,而Map又没有实现iterator,所以通常用来遍历Map。

Map中有**values()方法来获得value,有keySet()**方法来获得key的一个Set,但是都一次只能访问一个内容,Entry<K, V>很好地实现了这个一次访问两个的效果。

方法解释
K getKey()获得Map中的key
V getValue()获得Map中的value
V setValue(V value);设置Map中的value

使用方法:

Map<String, Integer> map = new TreeMap<>();
map.put("初中读了几年",3);
map.put("高中读了几年",3);
map.put("大学读了几年",2);for (Map.Entry<String,Integer> entry1 : map.entrySet()) {System.out.println("String:" + entry1.getKey() + " Value:" + entry1.getValue());
}// Map.Entry<String,Integer>得到的是一个<String,Integer>的变量,可直接看作<String,Integer>
Map.Entry<String,Integer> entry;
Iterator<Map.Entry<String,Integer>> it = map.entrySet().iterator();
it.next().setValue(99);for (Map.Entry<String,Integer> entry2 : map.entrySet()) {System.out.println("String:" + entry2.getKey() + " Value:" + entry2.getValue());
}
3.3 Map的常用方法说明
方法解释
V put(K key, V value);将K,V的元素放进Map
V get(Object key);得到Map中某个key对应的value
Set keySet();返回一个全是key的Set
V remove(Object key);删除key结点
Set<Map.Entry<K, V>> entrySet();返回<K, V>键值对的Set集合(可用<K, V>键值对遍历)
boolean containsKey(Object key);返回Map中是否存在这个key
boolean containsValue(Object value);返回Map中是否存在这个value
default V getOrDefault(Object key, V defaultValue);返回Map中是否存在这个key,如果不存在,则返回defaultValue
  • 注意:
  1. 关于Set<Map.Entry<K, V>> entrySet()产生的代码如下,
Set<Map.Entry<String, Integer>> set = map.entrySet();
for(Map.Entry<String, Integer> e : set) {System.out.println("String:" + e.getKey() + " Value:" + e.getValue());
}
  1. Map有两种实现类,分别是HashMap和TreeMap。

    Map底层结构TreeMapHashMap
    底层结构红黑树哈希桶
    插入/删除/查找时间复杂度O(log2N)O(1)
    是否有序有序(插入的时候对于key进行了排序)无序(存储是按照HashCode进行存储)
    线程安全不安全不安全
    插入/查找/删除区别按照key值插入,进行了元素之间的比较通过哈希函数,按照哈希地址插入
    比较与覆写key值必须能够比较,否则抛出ClassCastException异常(需要实现Comparable/Comparator接口)自定义类型需要覆写equals和 hashCode方法(需要利用哈希函数进行插入)
    应用场景需要key有序的时候更需要效率的时候
  2. Map中不存在两个相同的key值,但是value可以重复,当多次put进同一个key时,会更新这个key的值。

  3. 在TreeMap中key值不能为空,但是HashMap的key可以为空

    主要是因为TreeMap中,put方法会调用比较器函数,如果传过来的是一个null,那么也就无法比较。

    但是HashMap中,虽然用的是哈希函数,但是在哈希函数的内部使用了hash()方法,里面对于key为null的情况作了处理:如果key为null,那么就将其置为0。(但是只能有一个key为0的结点,因为同样,一个0只能计算出一个hash地址,重复插入key为null的结点,只会更新其value值)

  4. Map中的key不能修改,要修改只能删除后再添加进行修改

4. Set的使用

Set与Map的不同点在于Set只存储key,并且Set继承自Collection接口。

4.1 常见方法说明

方法解释
boolean add(E e);向集合中添加元素e
Object[] toArray();将集合变为数组
boolean contains(Object o);查看集合中是否存在o元素
Iterator iterator();返回迭代器
boolean containsAll(Collection<?> c);如果调用方包含c中的所有元素则返回true
boolean addAll(Collection<? extends E> c);将集合c中的所有元素加到调用方中,可以达到去重的效果
boolean retainAll(Collection<?> c);使调用方成为与c的子集

注意:

  1. Set中的key不能修改,要修改只能删除后再添加进行修改

  2. Set的key是唯一值

  3. TreeSet的底层是使用TreeMap进行初始化的,仍然使用键值对进行初始化,但是只传入了Set中的K,另外一个使用了Object对象。

    public TreeSet() {this(new TreeMap<E,Object>());
    }
    
  4. 同Map,TreeSet不能插入null的key,但是HashSet可以。

  5. Set的实现类有HashSet、TreeSet、LinkedHashSet(能够记录插入次序、继承了HashMSet、HashSet中有HashMap、HashMap中有这个结点,所以能够记录次序)

    Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;
    }
    
  6. Set最大的功能就是对元素进行去重

    Set底层结构TreeSetHashSet
    底层结构红黑树哈希桶
    插入/删除/查找时间复杂度O(log2N)O(1)
    是否有序关于key有序b不一定有序
    插入/删除/查找区别利用红黑树进行操作计算出哈希地址后才会进行对应的操作
    比较与覆写key必须能够比较,否则抛异常:ClassCastException自定义类型需要覆写equals和HashCode方法
    应用场景需要key有序查找效率为先

5. 哈希表

在以往的搜索方法中,都是需要遍历(直接搜索、二分查找)进行,但是理想的搜索方式是不需要遍历,直接能够找到,就像抓中药的时候,药师会直接抽出一个箱子,取出对应的中药,而不是一个一个箱子拉开看是不是符合自己需要的中药。

所以,哈希表也应该像中药一样,能够建立起中药和中药箱上的关系,使得药师能够根据药箱的外表就能够找到中药。所以哈希表也需要有一个映射关系,使得数据的存储位置和它的关键码能够产生联系。

实现思路:

插入元素:

根据待插入元素的关键码,根据某个计算方法计算出一个地址,按此地址进行存放。

搜索元素:

对于待搜索元素的关键码进行同样的计算,根据得到的地址进行查找。

上述的计算方法即为哈希方法,地址即为哈希地址,数据按此方式进行存放组成的结构叫做散列表(HashTable)。

5.2 冲突——概念

当进行插入的时候,势必会对多个插入元素计算出相同的哈希地址,比如我们要存储int,采用的哈希函数是:hash(key) = key % array,length;那么当我们数组长度为10,插入一个4,再插入一个14的时候,他俩都需要往下标为4的地址上存放,这样的情况就叫做哈希冲突。

5.3 冲突——避免

我们可以采用更改哈希函数、增大容器容量等方法来进行避免冲突,但是这只是一时的避免,在日后的数据越来越多的情况下,势必会再次出现冲突,那时候就又需要进行处理新的冲突。

5.3.4 冲突——避免——哈希函数设计

引起冲突的一个原因可能就是哈希函数设置的不够合理,就比如上述:hash(key) = key % array,length这个函数简单易懂,但是去容易产生冲突,如果我们更改为:hash(key) = (key + i^2) % array,length(其中 i 为插入数据的个数)那就降低了冲突的可能性。

哈希函数的设计原则:

  1. 应该尽可能的简单

  2. 函数计算出来的地址应该能够使得数据能够均匀的分布在散列表当中

  3. 哈希函数的定义域必须包括预备存储的所有数据的关键码(这样才能对于所有的数据进行计算),

    而且值域(算出来的哈希地址)必须在散列表的容量之中。

常见的哈希函数:

  1. 直接定制法:(常用)

    使用线性函数:hash(key) = a * key +b

    优点:简单均匀

    缺点:需要事先知道数据的分布才能实现函数

  2. 除留余数法:(常用)

    hash(key) = key % array.length

    优点:简单

    缺点:如果数据分散,则空间利用率低,适合处理数据集中的情况

  3. 平方取中法(了解)

    对数据进行平方后,取中间的x位作为哈希地址。

    适合数据不是很大,又不知道其分布的情况

  4. 折叠法(了解)

    将数据折叠成几段长度相等的部分,然后叠加求和,对于后几位按照散列表的长度进行取余。

  5. 随机数法(了解)

    hash(key) = random(key)

  6. 数学分析法(了解)

    通过对于数据的分析,选择出不宜重复的几项,然后将其选做哈希地址。

    比如:如果要存储公司里员工及其手机号,那么就可以选择使用后四位作为散列表地址。

5.5 冲突——避免——负载因子调节(重点掌握)

负载因子:α = 已存储的数据个数 / 散列表的长度

一般负载因子不会超过0.8,超过0.8就会频繁出现冲突问题。

所以当负载因子超过0.8的时候,我们就需要对于散列表进行调整。

5.6 冲突——解决

常见的两种方法:开散列和闭散列。

5.7 闭散列

闭散列:也叫开放地址法,当发生冲突的时候,如果哈希表未被装满,那就可以把发生冲突的数据存放到下一个空位置

  1. 线性探测
    在这里插入图片描述

这种处理方式的坏处是不能够随便删除元素,比如下表存储了12在2的后面,如果把2删除,那么32就会找不到,因为这种存储方式是通过依次遍历空位置来查找元素的,当22删除后,那个位置就为空,32就被误认为是没有尾数为2的元素的。
在这里插入图片描述


  1. 二次探测(二次方)

    线性探测对于在哈希地址同一个位置的数据进行了简单的处理,但是这种方式过于粗暴(直接挨着往后找),所以二次探测找下一个空位置的方式:Hi = (H0 ± i2) % array.length,其中 i = 1,2,3…,H0 为发生冲突的为位置。

    当发生冲突时,先取 i=1 试试看,如果这个位置仍然有元素,那就取 i = 2 看。

    在这里插入图片描述

研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不 会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容(增容后,全部元素需要重新哈希)

综上,闭散列的最大缺陷就是空间利用率低,这也是哈希的缺陷。

5.8 开散列/哈希桶

开散列法又叫链地址法,因为开散列是通过一个每个元素是一个链表结点的数组(哈希表)来存储数据的。

首先对关键码利用哈希函数进行计算散列地址,如果有相同的地址,那就归于一个下标,在这个下表下有一个单链表,多出来的元素往上串即可,各链表的头结点都在哈希表中。

在开散列中进行搜索是先找到下标,然后在这个下标存储的链表中进行遍历查找所需元素。

5.9 冲突严重时的解决办法

  1. 每个桶的背后是另一张哈希表
  2. 每个桶的背后是一棵搜索树

5.10 实现

public class HashBucket {// 一个结点就是一个桶,每个桶里装单链表的头结点static class Node {private int key;private int value;Node next;public Node() {}public Node(int key, int value, Node next) {this.key = key;this.value = value;this.next = next;}}public Node[] array;public int size;public static final double LOAD_FACTOR = 0.75;/*** 构造函数*/public HashBucket() {array = new Node[8];size = 0;}/*** 插入函数* @param key* @param value*/public int put(int key, int value) {// 这里使用最简单粗暴的哈希函数int index = key % array.length;//先找是否已经存在keyfor (Node node = array[index]; node != null; node = node.next) {if (node.key == key) {int oldValue = node.value;node.value = value;// 返回更新前的值return oldValue;}}Node newNode = new Node(key, value, null);if (array[index] == null) {array[index] = newNode;} else {// 尾插Node tail = array[index];while (tail.next != null) {tail = tail.next;}tail.next = newNode;}size++;// 判断是否需要扩容if (loadFactor() > LOAD_FACTOR) resize();// 使用函数的目的是为了每次进行判断都能重新计算return value;}double loadFactor() {return size * 1.0 / array.length;}/*** 扩容函数*/private void resize() {// 扩容需要将全部元素进行重新哈希Node[] newArray = new Node[array.length * 2];/*这种方式连着空位置一起进行了迁移for (int i = 0; i < array.length; i++) {int newIndex = array[i].key % newArray.length;newArray[newIndex] = array[i];}*/for (int i = 0; i < array.length; i++) {Node next = null;// 把array的每个结点都遍历一遍,如果为空就直接跳过,不为空则一直遍历进行重新哈希for (Node cur = array[i]; cur != null; cur = next){next = cur.next;int newIndex = cur.key % newArray.length;newArray[newIndex] = array[i];}}array = newArray;}/*** 得到key对应的value值* @param key* @return*/public int get (int key) {int index = key % array.length;Node cur = array[index];while (cur.key != key) {cur = cur.next;}if (cur != null) return cur.value;else return -1;}
}

5.11 性能分析

因为哈希表构建了数据关键码和散列表地址的映射,能够根据数据直接取出对应的值,虽然总是存在冲突,但是这并不是不可解决的问题,所以时间复杂度一般认为是O(1)。

5.12 和Java类集的关系

  1. HashMap和HashSet就是利用了哈希表实现的Map和Set
  2. Java中使用哈希桶解决冲突
  3. Java中负载因子超过0.75后会转变为红黑树提高效率
  4. Java中计算哈希地址就是使用 hashcode()方法进行计算,如果要使用自定义的类进行插入,那么必须覆写 equals()hashcode()

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端知识与基础应用

前端知识 什么是前端&#xff1a;所有和用户打交道的操作页面&#xff0c;我们都称之为前端 例如&#xff1a;pc页面&#xff0c;浏览器的主页面&#xff0c;手机页面等等&#xff0c;可以用肉眼看到的就是前端 什么是后端&#xff1a; 就是一堆代码&#xff0c;用户不能够直接…

Kitex踩坑 [Error] KITEX: processing request error,i/o timeout

报错问题 2023/010/28 17:20:10.250768 default_server_handler.go:234: [Error] KITEX: processing request error, remoteService, remoteAddr127.0.0.1:65425, errordefault codec read failed: read tcp 127.0.0.1:8888->127.0.0.1:65425: i/o timeout 分析原因 Hert…

内置视图联动查看器,实现数据关联分析

前言 在数据驱动业务发展的今天&#xff0c;数据的关联分析变得越来越重要。作为一种强大的数据挖掘工具&#xff0c;它可以帮助企业发现数据之间的关联和模式&#xff0c;从而更好地理解市场和客户的行为。通过关联分析&#xff0c;企业可以发现看似无关的数据之间的联系&…

假如我有一台服务器,我会让它提供三种服务

一、提供照片上传、存储和下载服务 随着移动互联网时代的持续快速发展&#xff0c;PC互联网日益势微&#xff0c;各大互联网门户网站的博客、空间也跟着凋零&#xff0c; 作为博客、空间的标配功能的相册也随之被关闭。 2019年3月6日网易相册发布停运公告并于当年5月8日正式停…

在线主动学习算法评估策略:prequential evaluation procedure

在线主动学习算法评估策略&#xff1a;prequential evaluation procedure 在在线主动学习领域(Online Active Learning)&#xff0c;对在线主动学习算法的评估策略有多种方法&#xff0c;而现如今常用的方法是prequential evaluation procedure(出自论文《High density-focuse…

SpringBoot 源码分析(四) 内置Tomcat分析

一、Tomcat相关知识 1. tomcat目录结构 Tomcat文件的目录结构 2.启动流程 启动一个Tomcat服务是执行的bin目录下的脚本程序&#xff0c;startup.bat和 startup.sh.一个是windows的脚本&#xff0c;一个是Linux下的脚本&#xff0c;同样还可以看到两个停止的脚本 shutdown.ba…

Java入门与实践

Java基础 Java入门 idea的使用 idea快捷键 crtlaltt 对选中的代码弹出环绕选项弹出层 问题描述&#xff1a;idea光标变小黑块 解决&#xff1a;误触Insert键&#xff0c;再次按Insert键即可 java基础语法 注释 //单行注释/* 多行注释 *//** 文档注释&#xff0c;可提取到…

车载网关产品解析(附:车载网关详细应用案例及部署流程)

5G车载网关是一款功能强大的工业级无线通讯设备。它集成了4G/5G双模网络模块、M12接口设计、强大的路由和安全功能等特性,可以为车载和移动应用提供稳定可靠的无线数据连接。 链接直达&#xff1a;https://www.key-iot.com/iotlist/sv900.html ### 产品特性 5G车载网关最大的…

【多线程】线程互斥 {多执行流并发执行的数据竞争问题,互斥锁的基本用法,pthread_mutex系列函数,互斥锁的原理;死锁;可重入函数和线程安全}

一、进程线程间通信的相关概念 临界资源&#xff1a;多线程执行流共享的资源就叫做临界资源。确切的说&#xff0c;临界资源在同一时刻只能被一个执行流访问。临界区&#xff1a;每个线程内部&#xff0c;访问临界资源的代码&#xff0c;就叫做临界区。互斥&#xff1a;通过互…

计算机网络基础三

课程目标 理解路由表的作用 能够读懂路由表信息 能够使用图形抓包工具 wireshark 进行数据包的抓取 &#xff0c;如&#xff08; TCP/IP 的三次握手四次断开&#xff09; 一、路由表 思考&#xff1a; 什么是交换,什么是路由,什么是路由表&#xff1f;1. 交换是指同网络访…

Linux C语言开发-D15一维数组

数组&#xff1a;有一定顺序关系的数据类型相同变量的变量集合 形式&#xff1a;<存储类型> <数据类型> <数组名> [<表达式>] 数组名表示内存首地址&#xff0c;是一个地址常量&#xff0c;sizeof(数组名)是数组占用的总内存空间 编译时分配连续内存…

常见的22个软件测试面试题(含答案解析)

大家好&#xff0c;我是大圣。今天大圣给大家列举了API测试的22个面试题&#xff0c;快来看看吧。 1、什么是API? API是允许两个应用程序相互通信的代码。API使开发人员能够发出特定的调用或请求来发送或接收信息。 2、什么是以API为中心的应用程序? 以API为中心的应用程…

重置 VCSA 6.7 root密码和SSO密码

原贴地址&#xff1a;https://www.cnblogs.com/airoot/p/16059033.html 问题描述 1、用root用户登录 VMware vCenter Server Appliance虚拟机失败&#xff0c;无法登录 2、vCenter Server Appliance 6.7 U1的root帐户错误尝试次数超过3次已锁定或帐户已过期 官方说明 在VC…

帆软同时查看多个tab会卡换种方式用网页跳转就会提升效率

效果如图&#xff1a; 方法&#xff1a; 首先&#xff0c;要下载个插件–网页框控件&#xff1b; 接着&#xff0c;做个frm作为首页&#xff0c; 把地址和参数输入进去 最后&#xff0c;预览首页就可以了

【软件教程】如何用C++检查TCP或UDP端口是否被占用

一、检查步骤 使用socket函数创建socket_fd套接字。使用sockaddr_in结构体配置协议和端口号。使用bind函数尝试与端口进行绑定&#xff0c;成功返回0表示未被占用&#xff0c;失败返回-1表示已被占用。 二、CODE 其中port需要修改为想要检测的端口号&#xff0c;也可以将代码…

检测和缓解SQL注入攻击

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严&#xff0c;攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句&#xff0c;在管理员不知情的情况下实现非法操作&#xff0c;以此来实现欺骗数据库服务器执行非授权的任意查询&#…

Jetpack Compose | State状态管理及界面刷新

我们知道Jetpack Compose&#xff08;以下简称Compose&#xff09;中的 UI 可组合项是通过Composable 声明的函数来描述的&#xff0c;如&#xff1a; Composable fun Greeting() {Text(text "init",color Color.Red,modifier Modifier.fillMaxWidth()) }上面的代…

研究人员发现基于xmpp的即时通讯服务被窃听

攻击者使用我们的加密服务发布了几个新的TLS证书,这些服务被用来劫持加密的 星连接 在5222端口使用透明的[中间人]代理。 到目前为止收集到的证据指向在托管提供者网络上配置的流量重定向,排除了其他可能性&#xff0c;例如服务器中断或欺骗攻击。 据估计&#xff0c;窃听从20…

数据结构:优先级队列(堆)

概念 优先级队列是啥&#xff1f; 队列是一种先进先出 (FIFO) 的数据结构 &#xff0c;但有些情况下&#xff0c; 操作的数据可能带有优先级&#xff0c;一般出队 列时&#xff0c;可能需要优先级高的元素先出队列。 在这种情况下&#xff0c; 数据结构应该提供两个最基本的…

uniapp开发小程序 小米手机真机bottom:0无效 底部间隙 设备安全区域处理办法

uniApp自定义导航 CSS设置 bottom:0竟然无效&#xff0c;而iphone和开发模拟器没有问题 height: 150rpx;position: fixed;left: 0;right: 0;bottom: calc(var(--window-bottom,0)); 网上查了各种方法&#xff0c;包括设置bottom:-20啊以及 padding-bottom: constant(safe-are…