【数据结构】(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;}

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

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

相关文章

关于CanvasRenderer.SyncTransform触发调用的机制

1&#xff09;关于CanvasRenderer.SyncTransform触发调用的机制 2&#xff09;小游戏Spine裁剪掉帧问题 3&#xff09;Dedicated Server性能问题 4&#xff09;.mp4视频放入RT进行渲染的性能分析闭坑指南 这是第421篇UWA技术知识分享的推送&#xff0c;精选了UWA社区的热门话题…

Kronecker分解(K-FAC):让自然梯度在深度学习中飞起来

Kronecker分解&#xff08;K-FAC&#xff09;&#xff1a;让自然梯度在深度学习中飞起来 在深度学习的优化中&#xff0c;自然梯度下降&#xff08;Natural Gradient Descent&#xff09;是一个强大的工具&#xff0c;它利用Fisher信息矩阵&#xff08;FIM&#xff09;调整梯度…

【HTML— 快速入门】HTML 基础

准备工作 vscode下载 百度网盘 Subline Text 下载 Sublime Text下载 百度网盘 vscode 下载 Sublime Text 是一款轻量好用的文本编辑器&#xff0c;我们在写前端代码时&#xff0c;使用 Sublime Text 打开比使用记事本打开&#xff0c;得到的代码体验更好&#xff0c;比 vscode…

鸿蒙开发深入浅出01(基本环境搭建、页面模板与TabBar)

鸿蒙开发深入浅出01&#xff08;基本环境搭建、页面模板与TabBar&#xff09; 1、效果展示2、下载 DevEco Studio3、创建项目4、新建页面模板5、更改应用信息6、新建以下页面7、Index.ets8、真机运行9、图片资源文件 1、效果展示 2、下载 DevEco Studio 访问官网根据自己的版本…

自动驾驶泊车算法详解(一)

自动驾驶泊车算法是自动驾驶技术中的重要组成部分&#xff0c;主要用于实现车辆在复杂场景下的自动泊车功能&#xff08;如垂直泊车、侧方位泊车、斜列泊车等&#xff09;。其核心目标是通过感知、规划和控制技术&#xff0c;使车辆在无人工干预的情况下安全、高效地完成泊车动…

鸿蒙next 点击穿透实现

点击穿透可以参考华为开发的保留文章,该章节只能在developer preview版本下查看 点击穿透 主要的方法是hitTestBehavior // xxx.ets Entry Component struct HitTestBehaviorExample {build() {// outer stackStack() {Button(outer button).onTouch((event) > {console.i…

27.[前端开发-JavaScript基础]Day04-函数基本使用-递归-变量作用域-函数式编程

一、JavaScript函数 1 认识JavaScript函数 程序中的foo、bar、baz 认识函数 函数使用的步骤 2 函数的声明和调用 声明和调用函数 函数的参数 有参数的函数练习 函数的返回值 函数的练习 arguments参数&#xff08;JS高级再学习&#xff09; 3 函数的递归调用 函数中调用函数…

蓝桥杯练习代码

一、最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 ""。 示例 1: 输入:strs = ["flower","flow","flight"] 输出:"fl"示例 2: 输入:strs = ["dog",&q…

添加成对约束后的标签传播算法研究:使用Python语言编写算法,在空手道数据集下验证算法的准确性,在一定程度上解决非对齐问题

背景&#xff1a; 辅导的过程中遇到了一个比较新颖的问题&#xff0c;下面是我对这个问题的分析和简要思路介绍。 思路分析&#xff1a; 这算机器学习下面的无监督学习&#xff0c;标签传播算法简称LPA&#xff0c;传统的标签传播算法会出现非对齐问题&#xff0c;一句话描述就…

鸿蒙开发第4篇__关于在鸿蒙应用中使用Java语言进行设计

本博文很重要 HarmonyOS从 API8 开始不再支持使用Java作为开发语言&#xff0c;未来的新功能将在ArkTS中实现. API 8对应的是HarmonyOS 3.0.0版本。请看下图&#xff1a; 因此&#xff0c; 读者如果看到类似《鸿蒙应用程序开发》(2021年版本 清华大学出版计)书 还使用Java语言…

sklearn机器学习 Python代码通用模板

以下是一个使用 scikit-learn&#xff08;sklearn&#xff09;进行机器学习的通用 Python 代码模板。这个模板涵盖了数据加载、预处理、模型训练、评估和预测的基本流程&#xff0c;适用于常见的机器学习任务。 python # 导入必要的库 import numpy as np import pandas as …

P9420 [蓝桥杯 2023 国 B] 双子数--最高效的质数筛【埃拉托斯特尼筛法】

P9420 [蓝桥杯 2023 国 B] 双子数 题目 分析代码 题目 分析 首先&#xff0c;我们如何找到双子数&#xff1f; 1&#xff09;找到所有质数满足范围内的质数&#xff08;即至少质数^2<23333333333333) 我们看见双子数x的范围2333<x<23333333333333&#xff0c;又因为…

deepseek 导出导入模型(docker)

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

mysql有索引但是查询没有使用索引是什么问题

关键原因分析 索引选择性问题 如果 order_id 没有索引&#xff0c;即使 insert_time 有索引&#xff0c;优化器可能认为先通过 order_id 过滤数据更高效。但由于 order_id 无索引&#xff0c;只能全表扫描后过滤。即使 insert_time 有索引&#xff0c;如果满足 insert_time >…

【Qt】为程序增加闪退crash报告日志

背景 随着软件代码量的增加&#xff0c;软件崩溃闪退的肯能行越来越大&#xff0c;其中一些是难以复现的&#xff0c;比如访问了访问了非法地址、被操作系统杀死等。 为此&#xff0c;在软件出现闪退情况时&#xff0c;尽可能多的记录闪退发生时信息&#xff0c;对排查闪退原…

C#从入门到精通(35)—如何防止winform程序因为误操作被关闭

前言: 大家好,我是上位机马工,硕士毕业4年年入40万,目前在一家自动化公司担任软件经理,从事C#上位机软件开发8年以上!我们在开发的上位机软件运行起来以后,一般在右上角都有一个关闭按钮,正常情况下点击关闭按钮就能关闭软件,但是不排除我们不想关闭软件,但是因为不…

ffmpeg avdevice_register_all 注册设备的作用

在 FFmpeg 中&#xff0c;avdevice_register_all() 是一个用于注册所有输入和输出设备的函数。它是 FFmpeg 的 libavdevice 模块的一部分&#xff0c;专门用于处理音频和视频的输入/输出设备&#xff08;如摄像头、麦克风、屏幕捕获等&#xff09;。 以下是对 avdevice_regist…

[RH342]tcpdump

[RH342]tcpdump 1. 题目2. 解题 1. 题目 服务器serverc 和 servera 之间有进程定期发送一个明文密码,找出它2. 解题 找出通信端口 抓包分析 tcpdump -X -vv port 6644红框中就是密码,所以密码是root123

连接Sql Server时报错无法通过使用安全套接字层加密与 SQL Server 建立安全连接

文章目录 一. 前言二. 解决方案 方案1方案2 三. 总结 一. 前言 在《数据库原理》这门课的实验上&#xff0c;需要使用SQL Server&#xff0c;然后使用jdbc连接sql server突然报错为&#xff1a;SQLServerException: “Encrypt”属性设置为“true”且 “trustServerCertific…

从 Spring Boot 2 升级到 Spring Boot 3 的终极指南

一、升级前的核心准备 1. JDK 版本升级 Spring Boot 3 强制要求 Java 17 及以上版本。若当前项目使用 Java 8 或 11&#xff0c;需按以下步骤操作&#xff1a; 安装 JDK 17&#xff1a;从 Oracle 或 OpenJDK 官网下载&#xff0c;配置环境变量&#xff08;如 JAVA_HOME&…