当前位置: 首页 > news >正文

【数据结构】(11) Map 和 Set

一、Map 和 Set 的简介

1、Set 和 Map

 

        Map 和 Set 是集合类框架学习的最后一部分。Map 和 Set 都是接口,需要通过 TreeSet、HashSet 和 TreeMap、HashMap 实例化。注意,Set 实现了 Collection,Map 并没有。

        Set 存放的是键(Key)必须唯一;而 Map 存放的是键值对(Key-Value)Value可以不唯一

2、Set 和 Map 的应用场景

        应用于需要搜索的场景。我们常用的搜索方法有:

  • 遍历。(大数据量时,效率低,O(N))。
  • 二分查找。(效率较高,O(logN),但要求数据有序)。

        以上两种适用于静态数据的搜索,比如,用二分查找搜索动态数据,每次更改数据值都会使数据重新排序,导致效率低下。动态数据的搜索场景适合用 Set 和 Map,一是效率高,最高达 O(logN) 或 O(1);二是动态扩展性能良好,能自动调整内部结构,保持原有性质。

3、Map 的使用

3.1、Map.Entry<K, V>

        内部类,相当于二叉树中的 Node。

        属性:

        提供的方法:获取 key、value,设置 value,重写的 equals、toString、hashCode。

3.2、常用方法

        补充:HashMap 不是线程安全的,currentHashMap 是线程安全的。

4、Set 的使用

4.1、常用方法

4.2、其它

  • Set 的底层是 Map,TreeSet 中默认 value 是 Object 对象插入TreeMap。 

  • LinkedHashSet 是在 HashSet 的基础上,维护一个双向链表记录元素的插入顺序或访问顺序,使 HashSet 可以按照这个顺序迭代。

二、二叉搜索树

1、什么是二叉搜索树

  • 左子树所有结点小于根结点。
  • 右子树所有结点大于根结点。
  • 所有子树都满足以上条件。

2、二叉搜索树的性能

        如果搜索 key,从 root 开始判断,比 root.val 小直接往左走,比 root.val 小直接往右走。对于完全二叉搜索树来说,最差时间复杂度是树高 O(logN);对于极度不平衡的二叉搜索树来说,最差时间复杂度是 O(N),相当于遍历了所有结点。

        因此,我们希望二叉搜索树尽量平衡。在源码中,TreeMap 和 TreeSet 的底层是用红黑树实现的,红黑树能通过各种操作使二叉搜索树平衡(高度差≤1)。

3、实现简单的二叉搜索树(不考虑平衡)

3.1、属性

public class BinarySearchTree {static class TreeNode {int val;TreeNode left;TreeNode right;public TreeNode(int val) {this.val = val;}}private TreeNode root;......
}

3.2、查找

  • 子树为空,结束查找,未找到,返回 null。
  • key 与子树根值相同,返回根。
  • key 与子树根值不相同:若 key 比子树根值小,继续查找子树的左子树;若 key 比子树根值大,继续查找子树的右子树。
    public TreeNode search(int key) {TreeNode curRoot = root; // 当前子树根节点while (curRoot!= null) {if (key == curRoot.val) { // 找到return curRoot;}else if (key < curRoot.val) { // 继续查找左子树curRoot = curRoot.left;}else {curRoot = curRoot.right; // 继续查找右子树}}return null; // 没有找到}

3.3、插入

  • 树为空,新结点作为根。
  • 比子树根小,插入到左子树中。
  • 比子树根大,插入到右子树中。
  • 子树为空,插入为其父结点的孩子节点。若空树是父节点的左子树(key 比父节点小),插入为左孩子;若空树是父节点的右子树(key 比父节点大),插入为右孩子。
    public void insert(int val) {TreeNode newNode = new TreeNode(val);if (root == null) { // 树为空,直接插入为根节点root = newNode;return;}TreeNode preRoot = null; // 记录父节点TreeNode curRoot = root;while (curRoot!= null) {if (val == curRoot.val) { // 已经存在,不再插入return;} else if (val < curRoot.val) { // 插入左子树preRoot = curRoot;curRoot = curRoot.left;} else { // 插入右子树preRoot = curRoot;curRoot = curRoot.right;}}if (val < preRoot.val) { // 插入为父节点的左子树preRoot.left = newNode;} else { // 插入为父节点的右子树preRoot.right = newNode;}}

3.4、删除

  • 空树,不需要删除。
  • search,找到需要删除的节点 node。
  • 如果 node 没有左孩子,右孩子替换 node 。node 为树根,右孩子作为树根;node 不为树根,node 的父节点接 node 的右孩子。
  • 如果 node 没有右孩子,左孩子替换 node 。node 为树根,左孩子作为树根;node 不为树根,node 的父节点接 node 的左孩子。
  • 如果 node 有左右孩子,找到 node 的左子树中的最大节点(最深的一个右孩子)或者右子树中的最小节点(最深的一个左孩子),替换 node。

情况1,delete 不是 parent:parent 的 left 接 target 的 right。

情况2,delete 是 parent:parent 的 right 接 target 的 right。

    public void delete(int val) {
//        if (root == null) { // 树为空,不再删除
//            return;
//        }// 找到待删除节点TreeNode preRoot = null; // 记录待删除节点的父节点TreeNode curRoot = root;while (curRoot!= null) {if (val == curRoot.val) { // 找到待删除节点break;} else if (val < curRoot.val) { // 继续查找左子树preRoot = curRoot;curRoot = curRoot.left;} else { // 继续查找右子树preRoot = curRoot;curRoot = curRoot.right;}}if (curRoot == null) { // 待删除节点不存在,包含树空的情况return;}deleteNode(curRoot, preRoot);}private void deleteNode(TreeNode node, TreeNode preNode) {// 待删除节点左子树为空if (node.left == null) {if(root == node) { // 待删除节点是根节点root = node.right;} else if (preNode.left == node) { // 待删除节点是父节点的左子树preNode.left = node.right;} else { // 待删除节点是父节点的右子树preNode.right = node.right;}return;}// 待删除节点右子树为空if (node.right == null) {if(root == node) { // 待删除节点是根节点root = node.left;} else if (preNode.left == node) { // 待删除节点是父节点的左子树preNode.left = node.left;} else { // 待删除节点是父节点的右子树preNode.right = node.left;}return;}// 待删除节点左右子树均不为空// 找到右子树的最小值,用最小值替换待删除节点,并删除最小值TreeNode parent = node;TreeNode target = node.right;// 找到右子树中最深的左孩子 target,即最小值while (target.left != null) {parent = target;target = target.left;}// 最小值替换待删除节点node.val = target.val;// 情况1:target 是右子树的根if (parent == node) {parent.right = target.right;} else { // 情况2:target 是右子树的孩子parent.left = target.right;}}

3.5、性能分析

        插入、删除操作都要经历搜索操作,因此查找效率 O(logN) 代表了二叉搜索树各个操作的性能。

三、哈希表

3.1、什么是哈希表

        通过哈希函数,输入 key 计算 value 的存放位置,通过这种哈希方法构造的结构就叫哈希表(散列表)。因为只需要计算一次哈希函数,所以删除、插入、搜索操作都是 O(1)

3.2、哈希冲突

3.2.1、什么是哈希冲突

        不同的 key,通过哈希函数,得到相同的映射。哈希冲突是必然发生的,我们需要尽可能降低哈希冲突发生的概率。

3.2.2、如何避免哈希冲突

  • 设计合理的哈希函数:地址值域应包含映射值域;映射值分布均匀;比较简单。

常见的哈希函数:

① 直接定址法:hash(key) = a*key+b,根据 key 的分布确定线性函数。

② 除留余数法:hash(key) = key % p,p ≤ m 且尽量接近 m 的质数,m 是地址范围大小。

  • 调节负载因子:负载因子 = 填入表中的元素个数 / 散列表长度。冲突率与负载因子的增减趋势一致,想降低冲突率就要降低负载因子。数据是必须要添加的,因此只能增加散列表长度。在源码中,负载因子超过一定值,就会自动扩容哈希表。

3.2.3、如何解决哈希冲突

        当冲突发生,我们要解决冲突,让每个元素都能填入。

  • 闭散列地址法(开放):冲突发生,表未满,找下一个空位置。

① 线性探测法:从冲突位置开始,依次往后找第一个空位置,插入元素。

缺点:容易造成冲突元素堆积;不能随意删除,需要设置伪删除标记。

② 二次探测法:Hash_i(key) = (Hash_0 ± i^2) % m,m 为散列表大小,i 为冲突次数。

缺点:虽然冲突堆积更分散,但还是会造成堆积。

散列表优点:不使用额外数据结构(链表)。

  •  开散列(哈希桶 / 链地址法):同映射的 key 为一个集合(桶),用链表串起来。源码用的哈希桶。
  • 冲突严重时,一个桶的搜索效率不佳(太多元素堆积)。当桶超过一定长度,可以将这个桶转为搜索树或者哈希表

3.3、实现简单的哈希桶

3.3.1、属性

public class HashBucket {static class Node {int key;int value;Node next;public Node(int key, int value) {this.key = key;this.value = value;}}public Node[] arr = new Node[10];public int usedSize; // 已有元素数量public static final float LOAD_FACTOR = 0.75f; // 装载因子阈值......
}

3.3.2、添加元素

    // 哈希函数public int hash(int key) {return key % arr.length;}// 计算负载因子public float loadFactor() {return (float) usedSize / arr.length;}// 扩容public void resize() {Node[] newArr = new Node[arr.length * 2]; // 扩容为原数组的两倍// 遍历原数组,将元素添加到新数组中for (Node node : arr) {Node curr = node;while (curr != null) {int newIndex = hash(curr.key); // 计算新索引位置// 头插法,将元素添加到新数组中Node next = curr.next;curr.next = newArr[newIndex];newArr[newIndex] = curr;curr = next;}}arr = newArr;}// 添加元素public void add(int key, int value) {int index = hash(key); // 计算索引位置Node newNode = new Node(key, value);// 如果该位置为空,则直接添加if (arr[index] == null) {arr[index] = newNode;} else { // 如果该位置不为空,则遍历链表,找到对应的key,更新valueNode curr = arr[index];while (curr.next!= null) {if (curr.key == key) {curr.value = value;return;}curr = curr.next;}// 如果遍历完链表,没有找到对应的key,则添加到链表尾部curr.next = newNode;}usedSize++;// 如果已有元素数量超过负载因子阈值,则扩容,目的是降低冲突概率if (loadFactor() >= LOAD_FACTOR) {resize();}}

3.3.3、根据 key 查找 value

    public int get(int key) {int index = hash(key); // 计算索引位置Node curr = arr[index];// 遍历链表,找到对应的key,返回valuewhile (curr!= null) {if (curr.key == key) {return curr.value;}curr = curr.next;}return -1; // 没有找到对应的key}

3.3.4、删除元素

    public void delete(int key) {int index = hash(key); // 计算索引位置Node curr = arr[index];Node prev = null;// 遍历链表,找到对应的key,删除节点while (curr!= null) {if (curr.key == key) {if (prev == null) { // 如果是头节点,则直接删除arr[index] = curr.next;} else { // 如果不是头节点,则将前节点的next指针指向当前节点的next指针prev.next = curr.next;}usedSize--;return;}prev = curr;curr = curr.next;}}

3.3.5、泛型版

public class HashBucket2<K, V> {static class Node<K, V> {K key;V value;Node<K, V> next;public Node(K key, V value) {this.key = key;this.value = value;}}public Node<K, V>[] arr = (Node<K, V>[])new Node[10];public int usedSize; // 已有元素数量public static final float LOAD_FACTOR = 0.75f; // 装载因子阈值// 哈希函数public int hash(K key) {return key.hashCode() % arr.length;}// 计算负载因子public float loadFactor() {return (float) usedSize / arr.length;}// 扩容public void resize() {Node<K, V>[] newArr = (Node<K, V>[])new Node[arr.length * 2]; // 扩容为原数组的两倍// 遍历原数组,将元素添加到新数组中for (Node<K, V> node : arr) {Node<K, V> curr = node;while (curr != null) {int newIndex = hash(curr.key); // 计算新索引位置// 头插法,将元素添加到新数组中Node<K, V> next = curr.next;curr.next = newArr[newIndex];newArr[newIndex] = curr;curr = next;}}arr = newArr;}// 添加元素public void add(K key, V value) {int index = hash(key); // 计算索引位置Node<K, V> newNode = new Node<>(key, value);// 如果该位置为空,则直接添加if (arr[index] == null) {arr[index] = newNode;} else { // 如果该位置不为空,则遍历链表,找到对应的key,更新valueNode<K, V> curr = arr[index];while (curr.next!= null) {if (curr.key.equals(key)) {curr.value = value;return;}curr = curr.next;}// 如果遍历完链表,没有找到对应的key,则添加到链表尾部curr.next = newNode;}usedSize++;// 如果已有元素数量超过负载因子阈值,则扩容,目的是降低冲突概率if (loadFactor() >= LOAD_FACTOR) {resize();}}// 根据key查找valuepublic V get(K key) {int index = hash(key); // 计算索引位置Node<K, V> curr = arr[index];// 遍历链表,找到对应的key,返回valuewhile (curr!= null) {if (curr.key.equals(key)) {return curr.value;}curr = curr.next;}return null; // 没有找到对应的key}// 删除元素public void delete(K key) {int index = hash(key); // 计算索引位置Node<K, V> curr = arr[index];Node<K, V> prev = null;// 遍历链表,找到对应的key,删除节点while (curr!= null) {if (curr.key.equals(key)) {if (prev == null) { // 如果是头节点,则直接删除arr[index] = curr.next;} else { // 如果不是头节点,则将前节点的next指针指向当前节点的next指针prev.next = curr.next;}usedSize--;return;}prev = curr;curr = curr.next;}}
}

重点:

  • 类型改成泛型。
  • key 的哈希编码使用 hashCode 计算。
  • key 的比较使用 equals。

四、二叉搜索树和哈希表的对比

TreeSet / TreeMapHashSet / HashMap
key 大小是否有序有序无序
底层实现红黑树哈希桶
比较与重写key 必须能够比较(自定义类重写 compareTo 或者传入自定义比较器,重写了 compare)。自定义类必须重写 equals 和 hashCode

五、源码阅读

5.1、属性

5.2、哈希编码

5.3、构造函数

5.4、添加一个键值对

更错:(n-1) & hash 等价于 hash % arr.length。

5.5、扩容

5.6、树化

5.7、关键点总结

  • 通过扩容代码得知,调用无参构造方法,第一次 put 添加元素,会分配 16 大小的内存。
  • 通过扩容代码得知,正常扩容的情况,每次 2 倍扩容。
  • 通过添加键值对的代码得知,链表转化为红黑树的第一个条件是,链表长度达到 8。
  • 通过树化的代码得知,链表转化为红黑树的第二个条件是,数组容量达到 64。

六、OJ 题练习

6.1、只出现一次的数

136. 只出现一次的数字 - 力扣(LeetCode)

思路:遍历所有元素,不存在就添加,存在就删除,最后剩下的就是单身狗。使用 HashSet,它的搜索、删除、添加操作都是 O(1) 的复杂度,遍历一边就是 O(N)。

class Solution {public int singleNumber(int[] nums) {HashSet<Integer> set = new HashSet<>();// 遍历数组for(int num : nums) {// 如果不存在于 set,添加if(!set.contains(num)) {set.add(num);} else { // 存在则删除set.remove(num);}}// set 中剩下的那个就是单身狗return set.toArray(new Integer[0])[0];}
}

6.2、随机链表的复制

138. 随机链表的复制 - 力扣(LeetCode)     

  

思路

1、遍历一遍链表,记录当前节点的上一个节点,创建好当前节点后,用记录好的上一个节点与当前节点连接起来。但是 random 是随机的,无法在第一遍中复制。如果再遍历一次,依然无法直接复制 random,除非先找到旧 random,再计算当前节点与 random 节点的距离,最后根据距离找到新节点的 random 节点。非常麻烦。

2、将旧、新节点绑定为键值对(遍历一次),再通过搜索旧节点(next 和 random)找到绑定的新节点(第二次遍历)。使用HashMap实现。注:节点的地址是唯一的,可作key。

/*
// Definition for a Node.
class Node {int val;Node next;Node random;public Node(int val) {this.val = val;this.next = null;this.random = null;}
}
*/class Solution {public Node copyRandomList(Node head) {Map<Node, Node> map = new HashMap<>();// 遍历旧链表,创建新节点,并与旧节点绑定Node tmp = head;while(tmp != null) {Node newNode = new Node(tmp.val);map.put(tmp, newNode);tmp = tmp.next;}// 遍历旧链表,搜索 map 中的旧 next 和 random 节点,找到对应的新节点,并连接tmp = head;Node newHead = map.get(tmp);Node newTmp = newHead;while(tmp != null) {newTmp.next = map.get(tmp.next);newTmp.random = map.get(tmp.random);tmp = tmp.next;newTmp = newTmp.next;}return newHead;}
}

6.3、宝石和石头        

771. 宝石与石头 - 力扣(LeetCode)

思路:遍历宝石,放入 set(宝石唯一,不需要重复,且便于后面进行搜索)。遍历石头,存在于 set 就计数。

class Solution {public int numJewelsInStones(String jewels, String stones) {Set<Character> set = new HashSet<>();int cnt = 0;// 宝石放入 setfor(int i = 0; i < jewels.length(); i++) {set.add(jewels.charAt(i));}// 遍历石头,是宝石的就计数for(int i = 0; i < stones.length(); i++) {char ch = stones.charAt(i);if(set.contains(ch)) {cnt++;}}return cnt;}
}

6.4、坏键盘

旧键盘 (20)__牛客网

<br/> 是换行,不管。第一行是应该输入,第二行是实际输入。

思路:英文字母坏键只输出大写,将输入转为大写。用 set 存放实际输入,让字符无重复,便于搜索(类似于宝石/实际输入和石头/应该输入)。遍历应该输入,不存在于 set 的就是坏键。不同于宝石和石头的是,坏键不能重复,用 set 存(需要搜索坏键是否已存)。

import java.util.Scanner;
import java.util.Set;
import java.util.HashSet;public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);while (in.hasNextLine()) { // 注意 while 处理多个 caseString str1 = in.nextLine(); // 应该输入String str2 = in.nextLine(); // 实际输入// 实际输入转大写放入 setSet<Character> set = new HashSet<>();for(char ch : str2.toUpperCase().toCharArray()) {set.add(ch);}// 遍历应该输入(转大写),在 set 中搜索是否存在,不存在的是坏键盘,放入无重复的 brokenSetSet<Character> brokenSet = new HashSet<>();for(char ch : str1.toUpperCase().toCharArray()) {if(!set.contains(ch) && !brokenSet.contains(ch)) {brokenSet.add(ch);System.out.print(ch);}}}}
}

6.5、二叉搜索树转双向链表

二叉搜索树与双向链表_牛客题霸_牛客网

分析:中序遍历,把打印改成调整节点。需要记录调整的前一个结点 preNode,调整当前节点时,与 preNode 连接起来。

public class Solution {private TreeNode head;private TreeNode pre;public TreeNode Convert(TreeNode pRootOfTree) {Convert2(pRootOfTree);return head;}public void Convert2(TreeNode pRootOfTree) {// 空树,不调整if (pRootOfTree == null){return;}Convert(pRootOfTree.left); // 遍历左子树// 调整结点if (pre == null) { // 当前节点是头节点,没有 prehead = pRootOfTree;} else {pRootOfTree.left = pre;pre.right = pRootOfTree;}pre = pRootOfTree; // 更新 preConvert(pRootOfTree.right); // 遍历右子树}
}

6.6、前 K 个高频单词

692. 前K个高频单词 - 力扣(LeetCode)

分析:单词(不可重复)和计数(可重复)为键值对,使用 HashMap。遍历数组,map 不存在该单词,添加,计数;存在,仅计数。k-top 大算法,使用优先级队列,建立小根堆,大小为 k。大的入堆,计数不同时,计数多的优先;计数相同时,字典排序小的字符串优先。

    public List<String> topKFrequent(String[] words, int k) {// 建立HashMap,并遍历数组,计数Map<String, Integer> map = new HashMap<>();for(String word : words) {// 不存在,添加并默认计数 0+1=1// 存在,仅自增1map.put(word, map.getOrDefault(word, 0)+1); }// k-top 大,k 大小的优先级队列// 小根堆。计数不等,计数小的优先;计数相等,字典排序大的优先Comparator<Map.Entry<String, Integer>> comparator = (a, b) -> !a.getValue().equals(b.getValue())? a.getValue()-b.getValue() : b.getKey().compareTo(a.getKey());Queue<Map.Entry<String, Integer>> queue = new PriorityQueue<>(k, comparator);// map 中前 k 个入队// map 不可以 forEach 迭代,转为 set 可迭代for(Map.Entry<String, Integer> entry : map.entrySet()) {if(queue.size() < k) {queue.offer(entry);} else {// 比堆顶大的就删掉堆顶,入队if(comparator.compare(entry, queue.peek()) > 0) {queue.poll();queue.offer(entry);}}}// 前 K 大出队,单词包装成 list,升序List<String> list = new ArrayList<>(k);while(!queue.isEmpty()) {list.add(queue.poll().getKey());}// 降序Collections.reverse(list);return list;}

6.7、存在重复元素

217. 存在重复元素 - 力扣(LeetCode)

分析:遍历一遍,使用 Set 存储。该数字存在,直接返回 true;不存在,就添加。遍历结束了都没有重复的,返回 false。

    public boolean containsDuplicate(int[] nums) {Set<Integer> set = new HashSet<>();for(int num : nums) {if(set.contains(num)) {return true;}set.add(num);}return false;}

6.8、存在重复元素Ⅱ

219. 存在重复元素 II - 力扣(LeetCode)

分析:遍历一遍,使用 Map 存储,value 是索引。该数字存在,且索引值满足条件,直接返回 true;不存在,就添加。遍历结束了都没有重复的,返回 false。

    public boolean containsNearbyDuplicate(int[] nums, int k) {Map<Integer, Integer> map = new HashMap<>();int index = 0;for(int j = 0; j < nums.length; j++) {if(map.containsKey(nums[j]) && Math.abs(map.get(nums[j]) - j) <= k) {return true;}map.put(nums[j], index++);}return false;}

相关文章:

【数据结构】(11) Map 和 Set

一、Map 和 Set 的简介 1、Set 和 Map Map 和 Set 是集合类框架学习的最后一部分。Map 和 Set 都是接口&#xff0c;需要通过 TreeSet、HashSet 和 TreeMap、HashMap 实例化。注意&#xff0c;Set 实现了 Collection&#xff0c;Map 并没有。 Set 存放的是键&#xff08;Key&a…...

DeepSeek 提示词:高效的提示词设计

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

【Redis】在Java中以及Spring环境下操作Redis

Java环境下&#xff1a; 1.创建maven 项目 2.导入依赖 <!-- redis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.3.2</version></dependency> 此处使用的是Jedis&…...

Linux红帽:RHCSA认证知识讲解(二)配置网络与登录本地远程Linux主机

Linux红帽&#xff1a;RHCSA认证知识讲解&#xff08;二&#xff09;配置网络与登录本地远程Linux主机 前言一、使用命令行&#xff08;nmcli 命令&#xff09;配置网络&#xff0c;配置主机名第一步第二步修改主机名称 二、使用图形化界面&#xff08;nmtui 命令&#xff09;配…...

新数据结构(13)——I/O

字符流 字符输入流&#xff08;Reader&#xff09; 字符输入流用于从数据源&#xff08;如文件、字符串等&#xff09;读取字符数据。Reader 是所有字符输入流的抽象基类。 常用实现类 FileReader 用于从文件中读取字符数据。 InputStreamReader 将字节流转换为字符流&…...

C语言学习,希尔排序

C语言&#xff0c;希尔排序是插入排序的一种&#xff0c;也称为递减增量排序。通过比较距离较远的元素&#xff0c;然后逐渐缩小间隔&#xff0c;直到整个数组变成有序的。这种排序方法减少了插入排序&#xff0c;大数据集的移动次数&#xff0c;提高了效率。 示例&#xff1a…...

Powershell Install deepseek

前言 deepseekAI助手。它具有聊天机器人功能&#xff0c;可以与用户进行自然语言交互&#xff0c;回答问题、提供建议和帮助解决问题。DeepSeek 的特点包括&#xff1a; 强大的语言理解能力&#xff1a;能够理解和生成自然语言&#xff0c;与用户进行流畅的对话。多领域知识&…...

22、《Spring Boot消息队列:RabbitMQ延迟队列与死信队列深度解析》

Spring Boot消息队列实战&#xff1a;RabbitMQ延迟队列与死信队列深度解析 引言 在现代分布式系统中&#xff0c;消息队列承担着解耦、削峰填谷和异步通信的重要职责。本文将深入探讨Spring Boot与RabbitMQ的整合应用&#xff0c;重点解析延迟队列与死信队列的实现原理及实战…...

性能测试项目实战

项目介绍和部署 项目背景 轻商城项目是一个现在流行的电商项目。我们需要综合评估该项目中各个关键接口的性能&#xff0c;并给出优化建议&#xff0c;以满足项目上线后的性能需要。 项目功能架构 前台商城&#xff1a;购物车、订单、支付、优惠券等 后台管理系统&#xff1a;商…...

LabVIEW中显微镜下位移误差的畸变

在显微实验中&#xff0c;位移台通过电机驱动探针进行微米级精确移动&#xff0c;配合显微镜和相机实时观察探针的位置。然而&#xff0c;实验中发现&#xff0c;当电机移动相同的物理距离时&#xff0c;图像中探针的像素位移量存在显著的非线性偏差。经测试&#xff0c;电机的…...

Spark MLlib中的机器学习算法及其应用场景

Spark MLlib是Apache Spark框架中的一个机器学习库&#xff0c;提供了丰富的机器学习算法和工具&#xff0c;用于处理和分析大规模数据。以下是Spark MLlib中的机器学习算法及其应用场景的详细描述&#xff1a; 一、Spark MLlib中的机器学习算法 分类算法&#xff1a; 逻辑回…...

Angular 中获取 DOM 节点的几种方法

文章目录 1. 使用ViewChild获取单个 DOM 节点2. 使用ViewChildren获取多个 DOM 节点3. 使用ElementRef直接访问 DOM4. 使用Renderer2操作 DOM5. 总结 在 Angular 开发中&#xff0c;虽然框架鼓励我们通过组件和模板来操作 DOM&#xff0c;但在某些情况下&#xff0c;直接访问和…...

R Excel 文件:高效数据处理的利器

R Excel 文件:高效数据处理的利器 在数据分析领域,R语言因其强大的统计分析和可视化功能而备受推崇。而R Excel文件,作为R语言与Excel的桥梁,使得数据在R和Excel之间的高效转换成为可能。本文将详细介绍R Excel文件的概念、应用场景以及操作方法。 一、R Excel文件的概念…...

手撕跳表/数据结构

昨天leetcode每日一题是跳表&#xff0c;之前学redis就没去写跳表&#xff0c;这次就逃不过了。 这里使用了len数组&#xff0c;来表示每个数字之间的间隔&#xff0c;方便复杂的查询功能。 主要问题有 为什么len数组记录的是数字之间的间隔&#xff0c;不是每一层从头到尾…...

在 Vue 中处理跨域请求:全面解析与实践指南

在 Vue 中处理跨域请求&#xff1a;全面解析与实践指南 在现代 Web 开发的复杂生态中&#xff0c;跨域请求&#xff08;CORS&#xff09;如同一个无处不在的难题&#xff0c;时刻考验着开发者的技术能力。当我们构建基于 Vue.js 的前端应用时&#xff0c;这一问题尤为凸显。因为…...

爬虫与反爬-Ja3指纹风控(Just a moment...)处理方案及参数说明

概述&#xff1a;本文将针对Ja3 指纹检测风控进行处理&#xff0c;举例了一个案例并使用两种不同的破解方案进行突破&#xff0c;同时深入了解指纹间不同字符所代表的含义 指纹检测背景&#xff1a; 1、每一个设备、软件都有独属于自己的设备信息、版本号、加密算法、椭圆算法…...

WPF-Avalonia实践一两个页面的相关传递

文章目录 注册两个ViewModel关联-Interaction在 Avalonia 框架中的 Interaction作用目的典型的使用场景显示对话框:文件操作:定义交互属性示例代码视图层处理交互总结例子-实现两个界面信息传递Interaction注册在主VIEWModel中注册异步方法按钮主viewModel对应的显示xaml-使用…...

无人机实战系列(三)本地摄像头+远程GPU转换深度图

这篇文章将结合之前写的两篇文章 无人机实战系列&#xff08;一&#xff09;在局域网内传输数据 和 无人机实战系列&#xff08;二&#xff09;本地摄像头 Depth-Anything V2 实现了以下功能&#xff1a; 本地笔记本摄像头发布图像 远程GPU实时处理&#xff08;无回传&#…...

LeetCode:数组异或操作

数组异或操作 描述 给你两个整数&#xff0c;n 和 start 。 数组 nums 定义为&#xff1a;nums[i] start 2*i&#xff08;下标从 0 开始&#xff09;且 n nums.length 。 请返回 nums 中所有元素按位异或&#xff08;XOR&#xff09;后得到的结果。 示例 1&#xff1a;…...

【前端】Axios AJAX Fetch

不定期更新&#xff0c;建议关注收藏点赞。 目录 AxiosAJAXCORS 允许跨域请求 Fetch Axios axios 是一个基于 Promise 的 JavaScript HTTP 客户端&#xff0c;用于浏览器和 Node.js 中发送 HTTP 请求。它提供了一个简单的 API 来发起请求&#xff0c;并处理请求的结果。axios …...

C++ 继承与运算符重载的简单练习

1.长方形的继承类 #include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory>using namespace std; class AB{ private:int a;int …...

pycharm技巧--鼠标滚轮放大或缩小 Pycharm 字体大小

1、鼠标滚轮调整字体 设置 Ctrl 鼠标滚轮调整字体大小 备注&#xff1a; 第一个是活动窗口&#xff0c;即缩放当前窗口 第二个是所有编辑器窗口&#xff0c;即缩放所有窗口的字体 2、插件 汉化包&#xff1a; Chinese Simplified 包...

deepseek 导出导入模型(docker)

前言 实现导出导入deepseek 模型。deepseek 安装docker下参考 docker 导出模型 实际生产环境建议使用docker-compose.yml进行布局&#xff0c;然后持久化ollama模型数据到本地参考 echo "start ollama" docker start ollama#压缩容器内文件夹&#xff0c;然后拷贝…...

STM32——HAL库开发笔记21(定时器2—输出比较)(参考来源:b站铁头山羊)

本文主要讲述输出比较及PWM信号相关知识。 一、概念 所谓输出比较&#xff0c;就是通过单片机的定时器向外输出精确定时的方波信号。 1.1 PWM信号 PWM信号即脉冲宽度调制信号。PWM信号的占空比 &#xff08;高电压 所占周期 / 整个周期&#xff09; * 100% 。所以PWM信号…...

【报错解决】vue打开界面报错Uncaught SecurityError: Failed to construct ‘WebSocket‘

问题描述&#xff1a; vue运行时正常&#xff0c;但是打开页面后报错 Uncaught SecurityError: Failed to construct WebSocket: An insecure WebSocket connection may not be initiated from a page loaded over HTTPS. 解决方案&#xff1a; 在项目列表中的public下的ind…...

【初探数据结构】时间复杂度和空间复杂度

&#x1f4ac; 欢迎讨论&#xff1a;在阅读过程中有任何疑问&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果你觉得这篇文章对你有帮助&#xff0c;记得点赞、收藏&#xff0c;并分享给更多对数据结构感…...

将DeepSeek接入vscode的N种方法

接入deepseek方法一:cline 步骤1:安装 Visual Studio Code 后,左侧导航栏上点击扩展。 步骤2:搜索 cline,找到插件后点击安装。 步骤3:在大模型下拉菜单中找到deep seek,然后下面的输入框输入你在deepseek申请的api key,就可以用了 让deepseek给我写了一首关于天气的…...

《TransMamba:一种混合Transformer-Mamba网络用于单图像去雨》学习笔记

paper&#xff1a;2409.00410 GitHub&#xff1a;sunshangquan/TransMamba 目录 摘要 1、介绍 2、相关工作 2.1 单图像去雨 2.2 视觉Transformer 2.3 光谱域中的Transformer 2.4 光谱域中的图像恢复 2.5 视觉Mamba 3、方法 3.1 整体网络架构 3.2 光谱域变换块&am…...

危化品经营单位安全管理人员的职责及注意事项

危化品经营单位安全管理人员肩负着保障经营活动安全的重要责任&#xff0c;以下是其主要职责及注意事项&#xff1a; 职责 1. 安全制度建设与执行&#xff1a;负责组织制定本单位安全生产规章制度、操作规程和生产安全事故应急救援预案&#xff0c;确保这些制度符合国家相关法…...

安装Redis并把Redis设置成windows下的服务然后进行Redis实例演示

目录 &#xff08;一&#xff09;安装Redis &#xff08;二&#xff09;Redis设置成windows下的服务 1、把redis设置成windows下的服务 2、设置服务命令 &#xff08;三&#xff09;Redis实例演示 1、Redis插入数据 2、Redis修改数据 3、Redis删除数据 4、Redis查询数…...

基于 Python 的项目管理系统开发

基于 Python 的项目管理系统开发 一、引言 在当今快节奏的工作环境中&#xff0c;有效的项目管理对于项目的成功至关重要。借助信息技术手段开发项目管理系统&#xff0c;能够显著提升项目管理的效率和质量。Python 作为一种功能强大、易于学习且具有丰富库支持的编程语言&…...

牛客周赛 Round 82(思维、差分、树状数组、大根堆、前后缀、递归)

文章目录 牛客周赛 Round 82&#xff08;思维、差分、树状数组、大根堆、前后缀、递归&#xff09;A. 夹心饼干B. C. 食堂大作战&#xff08;思维&#xff09;D. 小苯的排列计数(差分、树状数组)E. 和和&#xff08;大根堆&#xff0c;前缀和&#xff09;F. 怎么写线性SPJ &…...

【python】解析自动化脚本文件并按照=测试周期=存储记录

【python】连接Jira获取token以及jira对象 【python】解析自动化脚本文件并按照测试周期存储记录 【python】向Jira推送自动化用例执行成功 【python】向Jira测试计划下&#xff0c;附件中增加html测试报告 将已编写的自动化测试用例按照jira号解析出来&#xff0c;并按照测试计…...

一种简单有效的分析qnx+android智能座舱项目中的画面闪烁的方法(8155平台)

在智能座舱项目的开发过程中&#xff0c;画面闪烁问题是一个常见但棘手的挑战。由于这些闪烁现象往往转瞬即逝&#xff0c;传统的分析工具如截图、录屏或dump图层等方法难以捕捉和定位问题根源。针对这一难题&#xff0c;本文介绍了一种较为有效的分析方法&#xff0c;能够帮助…...

架构师论文《论湖仓一体架构及其应用》

软考论文-系统架构设计师 摘要 作为某省级商业银行数据中台建设项目技术负责人&#xff0c;我在2020年主导完成了从传统数据仓库向湖仓一体架构的转型。针对日益增长的支付流水、用户行为埋点及信贷审核影像文件等多模态数据处理需求&#xff0c;原有系统存在存储成本激增、实…...

一篇文章学懂Vuex

一、基于VueCli自定义创建项目 233 344 二、Vuex 初始准备 建项目的时候把vuex勾选上就不用再yarn add vuex3了 store/index.js // 这里面存放的就是vuex相关的核心代码 import Vuex from vuex import Vue from vue// 插件安装 Vue.use(Vuex)// 创建仓库&#xff08;空仓库…...

模拟实现Java中的计时器

定时器是什么 定时器也是软件开发中的⼀个重要组件. 类似于⼀个 "闹钟". 达到⼀个设定的时间之后, 就执⾏某个指定好的代码. 前端/后端中都会用到计时器. 定时器是⼀种实际开发中⾮常常⽤的组件. ⽐如⽹络通信中, 如果对⽅ 500ms 内没有返回数据, 则断开连接尝试重…...

全面理解-深拷贝与浅拷贝

在 C 中&#xff0c;深拷贝&#xff08;Deep Copy&#xff09; 和 浅拷贝&#xff08;Shallow Copy&#xff09; 是两种完全不同的对象拷贝策略&#xff0c;主要区别在于对指针和动态分配资源的处理方式。正确理解二者的区别是避免内存泄漏、悬空指针和程序崩溃的关键。 一、核…...

20250212:https通信

1:防止DNS劫持:使用 https 进行通信。 因为是SDK授权开发,需要尽量压缩so库文件和三方依赖。所以第一想法是使用 head only 的 cpp-httplib 进行开发。 cpp-httplib 需要 SSL 版本是 3.0及以上。但本地已经在开发使用的是1.0.2a版本,不满足需求。 方案1:升级OpenSSL 将Op…...

如何使用爬虫获取淘宝商品详情:API返回值说明与案例指南

在电商数据分析和运营中&#xff0c;获取淘宝商品详情是常见的需求。淘宝开放平台提供了丰富的API接口&#xff0c;允许开发者通过合法的方式获取商品信息。本文将详细介绍如何使用PHP编写爬虫&#xff0c;通过淘宝API获取商品详情&#xff0c;并解析API返回值的含义和结构。 一…...

List 接口中的 sort 和 forEach 方法

List 接口中的 sort 和 forEach 方法是 Java 8 引入的两个非常实用的函数&#xff0c;分别用于 排序 和 遍历 列表中的元素。以下是它们的详细介绍和用法&#xff1a; sort 函数 功能 对列表中的元素进行排序。 默认使用自然顺序&#xff08;如数字从小到大&#xff0c;字符…...

7.建立文件版题库|编写model文件|使用boost split字符串切分(C++)

建立文件版题库 题目的编号题目的标题题目的难度题目的描述&#xff0c;题面时间要求(内部处理)空间要求(内部处理) 两批文件构成第一个&#xff1a;questions.list : 题目列表&#xff08;不需要题目的内容&#xff09;第二个&#xff1a;题目的描述&#xff0c;题目的预设置…...

鸿蒙NEXT应用App测试-专项测试(DevEco Testing)

注意&#xff1a;大家记得先学通用测试在学专项测试 鸿蒙NEXT应用App测试-通用测试-CSDN博客 注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注…...

一周学会Flask3 Python Web开发-Jinja2模板访问对象

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 如果渲染模板传的是对象&#xff0c;如果如何来访问呢&#xff1f; 我们看下下面示例&#xff1a; 定义一个Student类 cla…...

docker离线安装记录

1.安装包 首先需要从官方网站下载Docker的离线安装包&#xff0c;可以通过以下地址找到自己想安装的版本&#xff1a; wget https://download.docker.com/linux/static/stable/x86_64/docker-20.10.7.tgz 【Docker】Docker学习之一&#xff1a;离线安装Docker步骤_docker离线…...

FreiHAND (handposeX-json 格式)数据集-release >> DataBall

FreiHAND &#xff08;handposeX-json 格式&#xff09;数据集-release 注意&#xff1a; 1)为了方便使用&#xff0c;按照 handposeX json 自定义格式存储 2)使用常见依赖库进行调用,降低数据集使用难度。 3)部分数据集获取请加入&#xff1a;DataBall-X数据球(free) 4)完…...

unity学习51:所有UI的父物体:canvas画布

目录 1 下载资源 1.1 在window / Asset store下下载一套免费的UI资源 1.2 下载&#xff0c;导入import 1.3 导入后在 project / Asset下面可以看到 2 画布canvas&#xff0c;UI的父物体 2.1 创建canvas 2.1.1 画布的下面是 event system是UI相关的事件系统 2.2 canvas…...

anaconda不显示jupyter了?

以前下载的anaconda显示jupyter&#xff0c;但是最近学习吴恩达的机器学习视频&#xff0c;需要用到jupyter&#xff0c;以前的jupyter运行不了&#xff0c;就重新下载了一个anaconda&#xff0c;发现新版的anaconda首页不显示jupyter了&#xff0c;在查找资料之后&#xff0c;…...

WordPress平台如何接入Deepseek,有效提升网站流量

深夜改代码到崩溃&#xff1f;《2024全球CMS生态报告》揭露&#xff1a;78%的WordPress站长因API对接复杂&#xff0c;错失AI内容红利。本文实测「零代码接入Deepseek」的保姆级方案&#xff0c;配合147SEO的智能发布系统&#xff0c;让你用3个步骤实现日均50篇EEAT合规内容自动…...

第十三:路由两个注意点:

4.3. 【两个注意点】 路由组件通常存放在pages 或 views文件夹&#xff0c;一般组件通常存放在components文件夹。 通过点击导航&#xff0c;视觉效果上“消失” 了的路由组件&#xff0c;默认是被卸载掉的&#xff0c;需要的时候再去挂载。 <script setup lang"ts&q…...