数据结构与算法-18算法专向(hash)

news2024/9/21 20:15:18

话题引入:

给你N(1<N<10)个自然数,每个数的范围为(1~10000000000)。现在让你以最快的速度判断某一个数是否在这N个数内,不得使用已经封装好的类,该如何实现。 A[] = new int[N+1]?

散列表

散列表(Hash table),也被称为哈希表,是一种根据键(Key)而直接访问在内存存储位置的数据结构。它通过计算一个关于键值的函数(即散列函数),将所需查询的数据映射到表中一个位置来访问记录,从而加快查找速度。

1 简介

  • 定义:散列表是一种通过键来直接访问内存存储位置的数据结构,它利用散列函数将键映射到表中一个位置,以实现快速查找。
  • 核心组件
    • 散列函数:一个用于计算散列值的函数,通常表示为hash(key),其中key是元素的键值,hash(key)的值是经过散列函数计算得到的散列值。
    • 散列表:存放记录的数组,也称为哈希表。
  • 特性
    • 确定性:如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。
    • 散列碰撞(Collision):散列函数的输入和输出不是唯一对应关系的。如果两个散列值相同,两个输入值很可能是相同的,但也可能不同。这种情况称为散列碰撞。
    • 不可逆性:一个哈希值对应无数个明文,理论上无法直接通过哈希值反推出原始输入。
    • 混淆特性:输入一些数据计算出散列值后,部分改变输入值,一个具有强混淆特性的散列函数会产生一个完全不同的散列值。

2 散列冲突解决方法

2.1 开放寻址法

开放寻址法(Open Addressing):通过探查序列来寻找空闲位置。

2.1.1 常见的开放寻址法:
  1. 线性探测法(Linear Probing)
    • 当发生冲突时,从冲突的地址开始,线性地向后探测,直到找到第一个空闲的地址。
    • 公式:𝐻(𝑘,𝑖)=(ℎ(𝑘)+𝑖)mod  𝑚H(k,i)=(h(k)+i)modm
    • 其中,ℎ(𝑘)h(k) 是原始哈希函数,𝑚m 是哈希表的大小,𝑖i 是探测序列中的步长,通常从1开始。
  2. 二次探测法(Quadratic Probing)
    • 与线性探测类似,但探测的步长是二次方的序列,即1, 4, 9, 16, …。
    • 公式:𝐻(𝑘,𝑖)=(ℎ(𝑘)+𝑐𝑖)mod  𝑚H(k,i)=(h(k)+c**i)modm
    • 其中,𝑐𝑖=𝑖2c**i=i2。
  3. 双哈希法(Double Hashing)
    • 使用两个哈希函数,当发生冲突时,使用第二个哈希函数计算下一个探测地址。
    • 公式:𝐻(𝑘,𝑖)=(ℎ1(𝑘)+𝑖⋅ℎ2(𝑘))mod  𝑚H(k,i)=(h1(k)+ih2(k))modm
    • 其中,ℎ1(𝑘)h1(k) 和 ℎ2(𝑘)h2(k) 是两个不同的哈希函数。
2.2.2 优缺点

优点

  • 空间利用率高:不需要额外的存储空间来存储冲突的元素。
  • 简单易实现:算法实现相对简单。

缺点:

  • 聚集问题:特别是线性探测法,容易形成聚集,影响查找效率。
  • 删除操作复杂:删除元素时需要标记删除,而不是真正删除,以避免影响探测序列。
2.2.3 示例说明
  • 线性探测法的工作原理

    • 线性探测法的基本思想是,当一个元素通过哈希函数计算得到的地址已被占用时,它会从这个地址开始,线性地向后(或向前,这取决于实现方式)探测,直到找到一个空闲的地址。探测的步长通常是1。
  • 线性探测法的步骤

    • 计算哈希地址:使用哈希函数 ℎ(𝑘)h(k) 计算键 𝑘k 的哈希地址。
    • 探测冲突:如果该地址已被占用,就从该地址开始,线性地向后探测。
    • 找到空闲地址:探测到第一个空闲地址,将元素插入该地址。
    • 插入元素:将元素插入到找到的空闲地址中。
  • 示例

    • 假设我们有一个哈希表大小为6,哈希函数定义为 ℎ(𝑘)=𝑘 mod  6 ,现在我们要插入以下元素:
      • 元素1:ℎ(1)= 1 mod  6=1 h(1)=1mod 6=1
      • 元素2:ℎ(2)=2 mod  6=2 h(2)=2mod 6=2
      • 元素3:ℎ(3)=3 mod  6=3 h(3)=3mod 6=3
      • 元素4:ℎ(4)=4 mod  6=4 h(4)=4mod 6=4
      • 元素6:ℎ(7)=7 mod  6=1 h(6)=6mod 6=1(与元素1冲突)
    • 下面是插入元素6的步骤:
      • 计算元素7的哈希地址:ℎ(7)=1。
      • 地址1已被元素1占用,发生冲突。
      • 从地址1开始线性探测,往下寻址 【 2(不为空) -> 3(不为空) -> 4(不为空) -> 5(空) 】
      • 将元素7插入到地址5。
    • 此时,哈希表的状态如下:
      • 地址0:空闲
      • 地址1:元素1
      • 地址2:元素2
      • 地址3:元素3
      • 地址4:元素4
      • 地址5:元素6

2.2 链表法

链表法(Chaining):在散列表中,每个位置对应一条链表,所有散列值相同的元素都放到相同位置对应的链表中。这种方法更加常用且简单。

2.2.1 工作原理
  1. 哈希函数:首先使用哈希函数 ℎ(𝑘)h(k) 计算键 𝑘k 的哈希地址。
  2. 链表存储:在哈希表的对应地址处,维护一个链表,所有哈希值相同的键都将被存储在这个链表中。
  3. 插入操作:当插入一个新元素时,根据其哈希值找到对应的链表,然后在链表的末尾添加新元素。
  4. 查找操作:当查找一个元素时,根据其哈希值找到对应的链表,然后在链表中顺序查找该元素。
  5. 删除操作:当删除一个元素时,同样需要根据其哈希值找到对应的链表,然后在链表中找到该元素并删除。
2.2.2 优缺点

优点

  • 冲突易于处理:冲突的元素可以简单地存储在同一哈希值的链表中。
  • 动态扩展:链表的长度可以根据需要动态变化,适应不同数量的冲突。
  • 删除方便:删除元素时不需要考虑其他元素的探测或重新散列。

缺点

  • 空间开销:每个槽都需要额外的空间来存储链表的节点。
  • 性能问题:如果哈希表的负载因子(即元素数量与槽数量的比值)过高,链表可能会变得很长,导致查找效率下降。
2.2.3 示例说明

假设我们有一个哈希表大小为3,哈希函数定义为 ℎ(𝑘)=𝑘mod  3h(k)=kmod3,现在我们要插入以下元素:

  • 元素1:ℎ(1)=1mod  3=1h(1)=1mod3=1
  • 元素4:ℎ(4)=4mod  3=1h(4)=4mod3=1(与元素1冲突)
  • 元素7:ℎ(7)=7mod  3=1h(7)=7mod3=1(与元素1和4冲突)

插入过程如下:

  1. 元素1插入到哈希表的地址1的链表中。
  2. 元素4也插入到地址1的链表中,因为它与元素1冲突。
  3. 元素7同样插入到地址1的链表中。

此时,哈希表的状态如下:

  • 地址0:空闲
  • 地址1:元素1 -> 元素4 -> 元素7
  • 地址2:空闲

3 散列表&hashMap

散列表与hashMap

HashMap 是 Java 中的一个非常重要的集合类,它基于散列表(Hash Table)实现,用于存储键值对(key-value pairs)。散列表是一种使用哈希函数组织数据,以支持快速插入和搜索的数据结构。在 HashMap 中,散列表的使用是其高效性的关键所在。

3.1 什么是hashmap

Java中的HashMap是Java集合框架中的一个非常重要的类,它实现了Map接口。HashMap存储键值对(key-value pairs),其中每个键都映射到最多一个值。一个键可以映射到最多一个值。HashMap不保证映射的顺序;特别是,它不保证该顺序会随着时间的推移保持不变(扩容)。

特点:

  1. 基于哈希表实现HashMap内部通过一个哈希表来存储键值对,这使得它在查找、插入和删除操作方面具有较高的效率,平均时间复杂度为O(1)。
  2. 允许null键和null值:与Hashtable不同,HashMap允许使用null键和null值。但最多只能有一个null键,因为键是唯一的。
  3. 不保证顺序HashMap不保证映射的顺序;特别是它不保证该顺序会随着时间的推移保持不变。
  4. 初始容量和负载因子HashMap的默认初始容量是16,默认负载因子是0.75。当HashMap中的元素数量超过容量乘以负载因子时,哈希表会进行扩容,即创建一个新的哈希表,并将原哈希表中的所有元素重新哈希后存储到新表中。扩容操作是代价较高的。

hashmap使用示例

import java.util.HashMap;  
import java.util.Map;  
  
public class HashMapExample {  
    public static void main(String[] args) {  
        // 创建一个HashMap实例  
        Map<String, Integer> ageMap = new HashMap<>();  
  
        // 向HashMap中添加键值对  
        ageMap.put("Alice", 30);  
        ageMap.put("Bob", 25);  
        ageMap.put("Charlie", 35);  
  
        // 访问HashMap中的值  
        System.out.println("Alice's age: " + ageMap.get("Alice")); // 输出: Alice's age: 30  
  
        // 检查HashMap中是否包含某个键  
        if (ageMap.containsKey("Bob")) {  
            System.out.println("Bob is in the map.");  
        }  
  
        // 检查HashMap中是否包含某个值(注意,这可能会返回true,因为可能有多个键映射到相同的值)  
        if (ageMap.containsValue(30)) {  
            System.out.println("There is someone who is 30 years old.");  
        }  
  
        // 修改HashMap中已存在的键对应的值  
        ageMap.put("Alice", 31); // 将Alice的年龄改为31  
  
        // 移除HashMap中的键值对  
        ageMap.remove("Charlie");  
  
        // 遍历HashMap(使用entrySet()遍历键和值)  
        for (Map.Entry<String, Integer> entry : ageMap.entrySet()) {  
            System.out.println(entry.getKey() + ": " + entry.getValue());  
        }  
  
        // 或者,如果你只对键或值感兴趣,可以使用keySet()或values()  
        // 遍历HashMap中的所有键  
        for (String key : ageMap.keySet()) {  
            System.out.println(key);  
        }  
  
        // 遍历HashMap中的所有值  
        for (Integer value : ageMap.values()) {  
            System.out.println(value);  
        }  
  
        // 使用Java 8的forEach()方法和Lambda表达式遍历  
        ageMap.forEach((key, value) -> System.out.println(key + ": " + value));  
    }  
}
3.2 hashmap数据结构

jdk1.8之前(数组+链表)

在JDK 1.8之前,HashMap采用了数组+链表的方式去保存数据。通过计算Hash值将元素放到数组上的指定位置上。如果出现了Hash相同的情况(即哈希冲突),就会形成单向的链表,并且链表使用头插法,将最新插入的元素放到链表的头结点位置。

jdk1.8以后(数组+链表+红黑树)

从JDK 1.8开始,HashMap的数据结构进行了优化,引入了红黑树来解决链表过长导致的查找效率下降问题。当链表的长度超过8并且数组长度大于64时,为了避免查找搜索性能下降,该链表会转换成一个红黑树。红黑树是一种自平衡的二叉搜索树,它的插入、删除和查找的时间复杂度都是O(log n),相比于链表的线性时间复杂度O(n),可以大大提高操作的效率。
在这里插入图片描述

3.3 数据存储原理
  • 根据key计算hash值。
  • 判断数组是否存在,如果不存在则用resize方法创建默认长度为16的数组。
  • 确定要存入的Node在数组中的位置,根据hash值与数组最大索引进行按位与运算得到索引位置。
  • 判断该位置是否有元素,如果没有则直接创建一个Node存入;如果有元素,则进一步判断key是否相同,如果相同则覆盖,并将原来的值返回;如果key不相同,则在原Node基础上添加新的Node,并判断该位置是链表还是红黑树,进行相应的插入操作。

在这里插入图片描述

3.4 最后读读源码

备注很多认真看

3.4.1 get
public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;//先计算hashcode
}
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && //  数组查找!!!!!!!
            ((k = first.key) == key || (key != null && key.equals(k)))) // hash值相同 且 key相等
            return first;//返回查找的数据
        if ((e = first.next) != null) {  //桶中不止一个节点
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);//红黑查找!!!!!
            do {//链表查找!!!!!
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;//否则返回null
}
3.4.2 put
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);//调用Map的putVal方法
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {//onlyIfAbsent:true不更改现有值;evict:false表示table为创建状态
    Node<K,V>[] tab; Node<K,V> p; int n, i;//临时变量
    if ((tab = table) == null || (n = tab.length) == 0)//数组是否null或者==0,第1次put为空
        n = (tab = resize()).length;//初始化数组(or扩容)!!!!!!!!!!!!!
    if ((p = tab[i = (n - 1) & hash]) == null)//寻址:(n - 1) & hash重要,16-1 按位与hash,为null表示没有值
        tab[i] = newNode(hash, key, value, null);//等空,直接插入
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&//key相等!!!!!!!!
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;//将第一个元素赋值给e,用e来记录;跳到646Line
        else if (p instanceof TreeNode)//判断是否红黑树!!!!!!!!!!!!!!!!
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else { //生成链表(操作链表),开始遍历链表
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {//p.next为空表明处于链表的尾部,1、生成链表  2、已经是链表!!!!  存储位置相等
                    p.next = newNode(hash, key, value, null);// 直接创建
                    if (binCount >= TREEIFY_THRESHOLD - 1) //链表长度如果>8转红黑树(or 扩容),-1是因为binCount从0开始
                        treeifyBin(tab, hash);//树化;还需要判断是否大于64,否则扩容
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))//对链表节点中数据进行覆盖判断
                    break;// 如果key相同,break跳出for循环,执行后面的逻辑
                p = e;
            }
        }
        if (e != null) { // key已经存在
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;// 用新的value值去覆盖老的value值
            afterNodeAccess(e);
            return oldValue;// 返回覆盖前的value值,put的返回值
        }
    }
    ++modCount;//用来记录HashMap的修改次数
    if (++size > threshold)//扩容
        resize();//如果size大于threshold,就需要进行扩容
    afterNodeInsertion(evict);
    return null;
}

4 hash函数的应用场景

  1. 加密与哈希算法
  • MD5 哈希算法
    • 广泛用于生成数据的128位二进制哈希值(指纹)。
    • 尽管理论上存在冲突可能(因为输出空间有限),但实际应用中冲突概率极低。
    • 用途:密码存储(注意:MD5已不推荐用于安全敏感场景,因其易受碰撞攻击)。
    • 示例md5(md5("password") + "salt") 用于密码加盐哈希。
    • 不可逆性:MD5生成的哈希值无法直接还原为原始数据。
  1. 视频重复检测
  • 方法:使用MD5算法对视频文件内容进行哈希,得到其唯一标识。
  • 优点:快速检测视频文件是否重复,只需比较哈希值。
  • 注意:大文件哈希计算可能耗时较长,且对于微小差异的视频文件可能无法区分。
  1. 相似性检测(如论文查重)
  • 指纹算法:将论文内容转化为特征向量(指纹),通常基于文本特征提取。
  • 汉明距离:用于衡量两个指纹之间的相似度,汉明距离越小表示相似度越高。
  • 应用:在学术查重系统中广泛使用,以检测论文的抄袭情况。
  1. 负载均衡(如Nginx配置)
  • 场景:在拥有多台服务器的环境中,合理分配请求以平衡负载。
  • 方法:根据请求的某些特征(如IP地址)计算哈希值,然后对服务器数量取模,决定请求发往哪台服务器。
  • 优点:简单有效,能快速实现请求的均衡分配。
  1. 分布式系统数据分库分表
  • 目的:处理大规模数据,将数据存储到多个数据库或表中以提高性能和可扩展性。
  • 方法:使用哈希函数对数据的唯一标识(如ID)进行哈希,然后对表数量取模,决定数据存储位置。
  • 示例id % 10 用于决定将ID分配到10个表中的哪一个。
  • 扩展性:当需要增加表数量时,需重新分配数据,可能导致数据迁移和性能影响。
  1. 分布式存储表扩展问题

可以了解了解(redis cluster集群的一致性哈希

  • 挑战:在增加新表后,需要重新计算并迁移旧数据到新结构,可能导致大量数据移动和服务中断。
  • 解决方案
    • 增量迁移:逐步迁移数据,减少一次性迁移压力。
    • 使用更灵活的哈希算法:如一致性哈希,减少数据迁移量。
    • 动态扩容策略:设计能够动态调整数据分布的机制,减少手动干预。
  1. 查找算法:HashMap
  • 核心:基于哈希表的键值对存储结构,提供快速的查找、插入和删除操作。
  • 原理:通过哈希函数将键转换为数组索引,实现快速定位。
  • 冲突解决:通过链表、红黑树等方式处理哈希冲突。
  • 应用:广泛应用于缓存、数据库索引、编程语言数据结构等场景。

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

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

相关文章

快来尝尝,超赞的食家巷一窝丝

一窝丝&#xff0c;这个名字听起来就充满了诗意和神秘。当你第一次见到它时&#xff0c;定会被它那精致的外形所吸引。纤细如丝&#xff0c;盘绕在一起&#xff0c;宛如一个精美的艺术品。那丝丝缕缕&#xff0c;散发着淡淡的麦香味&#xff0c;仿佛在诉说着古老的故事。 制作食…

解读 Java 经典巨著《Effective Java》90条编程法则,第5条:优先考虑依赖注入来引用资源

【前言】欢迎订阅【解读《Effective Java》】系列专栏 《Effective Java》是 Java 开发领域的经典著作&#xff0c;作者 Joshua Bloch 以丰富的经验和深入的知识&#xff0c;全面探讨了 Java 编程中的最佳实践。这本书被公认为 Java 开发者的必读经典&#xff0c;对提升编码技…

Java 中常用的排序算法

Java 中常用的排序算法有很多&#xff0c;每种算法的时间复杂度和适用场景都不同。以下是几种常见的排序算法及其 Java 实现和讲解&#xff1a; 1. 冒泡排序 (Bubble Sort) 算法思路&#xff1a; 重复地遍历数组&#xff0c;每次比较相邻两个元素。如果前一个比后一个大&…

Web接入Sonic平台之安装

问题及解决方案 1.安装python的airtest-bdd依赖时报错&#xff0c;显示无法编译psutil note: This error originates from a subprocess, and is likely not a problem with pip. ERROR: Failed building wheel for psutil Failed to build psutil ERROR: ERROR: Failed to b…

【2025】基于 SpringBoot 的电影购票系统、电影购票系统、智能电影购票系统、电影购票平台、电影购票管理、微服务电影购票系统(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

传输层协议(TCP和UDP)

目录 一、UDP 1、UDPAPI 2、UDPAPI的使用 二、TCP 1、TCPAPI 2、TCP的相关特性 2.1 确认应答 2.2 超时重传 2.3 连接管理&#xff08;三次握手&#xff0c;四次挥手&#xff09; 2.4 滑动窗口 2.5 流量控制 2.6 拥塞控制 2.7 延时应答 2.8 捎带应答 2.9 面向字节…

【赵渝强老师】基于ZooKeeper实现Hadoop HA

由于在HA架构中包含的节点比较多&#xff0c;在进行实际部署的时候需要做好集群的规划。图14.9一共使用了4个节点来部署HDFS HA&#xff0c;它们分别是&#xff1a;bigdata112、bigdata113、bigdata114和bigdata115。由于Hadoop默认包含了HDFS和Yarn&#xff0c;因此在部署HDFS…

构建 LLM 应用程序时经常遇到的高级概念的快速指南

使用案例 数据支持的 LLM 应用程序有无数的用例&#xff0c;但大致可以分为四类&#xff1a; 结构化数据提取 Pydantic 提取器允许您指定要从数据中提取的精确数据结构&#xff0c;并使用 LLM 以类型安全的方式填充缺失的部分。这对于从 PDF、网站等非结构化来源中提取结构化…

阿里国际、eBay、乐天等跨境电商如何搭建测评系统给自己店铺测评

要实现自己养号给自己店铺进行测评&#xff0c;确实需要一系列周密的准备和规划&#xff0c;以确保整个过程既稳定安全又有效。以下是详细补充和强化建议&#xff1a; 1. 稳定的测评环境系统 选择高级防关联技术&#xff1a;除了使用国外的服务器、纯净的国外IP和防关联浏览器…

mysql怎样优化count(*) from 表名 where …… or ……这种慢sql

一 问题描述 线上发现一条类似这样的慢sql&#xff08;查询时长8s&#xff09;&#xff1a; select id,name,(select count(*) from t14 where t14.idt15.id or t14.id2t15.id) as cnt from t15 ; t14的id和id2字段上都有索引&#xff0c;但是因为条件里有or&#xff0c;导致…

Kubernetes调度基础

一、RC 和 RS 1. Replication Controller Replication Controller&#xff0c;简称 RC&#xff0c;复制控制器&#xff0c;可确保Pod 副本数达到期望值&#xff0c;也就是 RC 可确保一个 Pod 总是可用&#xff0c;或一组 Pod 的数量永远处于一个定值。 如果存在的 Pod 大于设…

杭州等保测评揭秘:数据安全如何成为企业的“一道锁”

在数字化时代&#xff0c;数据安全已成为企业和机构面临的重要挑战。杭州作为科技创新的前沿城市&#xff0c;积极推进信息安全建设&#xff0c;其中等保测评&#xff08;等级保护测评&#xff09;成为保障数据安全的重要手段。 等保测评是依据《信息安全等级保护管理办法》对…

代码随想录训练营第36天|二维背包

1049. 最后一块石头的重量 II class Solution { public:int lastStoneWeightII(vector<int>& stones) {int sumaccumulate(stones.begin(),stones.end(),0);int targetsum/2;vector<int> dp(target1,0);for(auto& stone: stones){for(int itarget; i>s…

如何快速学习拼音打字?

拼音打字是很多人学会使用电脑或手机的第一步&#xff0c;尤其是对于需要经常输入中文的人来说&#xff0c;熟练掌握拼音打字可以大大提升效率。下面分享一些快速学习拼音打字的方法和技巧&#xff0c;帮助你尽快掌握这项技能。 1. 了解拼音的基本规则 学习拼音打字之前&…

代码随想录算法训练营第五十八天 | 拓扑排序精讲-软件构建

目录 软件构建 思路 拓扑排序的背景 拓扑排序的思路 模拟过程 判断有环 写代码 方法一&#xff1a; 拓扑排序 软件构建 题目链接&#xff1a;卡码网&#xff1a;117. 软件构建 文章讲解&#xff1a;代码随想录 某个大型软件项目的构建系统拥有 N 个文件&#xff0c;文…

Vue|mixin混入

目录 mixin混入什么是mixin代码准备如何混入?全局混合 在 Vue 中&#xff0c;混入&#xff08;Mixins&#xff09;是一种灵活的分发功能&#xff0c;可以将可复用的代码片段和功能注入到多个组件中。简单来说&#xff0c;混入允许你在多个组件之间共享逻辑&#xff0c;而无需重…

QT<24> Qt和windows中获取CPU序列号号以及主板序列号

前言&#xff1a;在qt中获取CPU和主板唯一序列号&#xff0c;可以在程序构造函数中判断是否与windows中一致&#xff0c;不一致可以直接退出程序&#xff0c;防止程序daoyong。 一、获取电脑CPU唯一序列号 QString MainPage::get_cpu() {QString cmd"wmic cpu get proc…

DNS和ICMP

DNS DNS&#xff08;Domain Name System &#xff09; DNS 是一整套从域名映射到 IP 的系统 关于DNS背景 TCP/IP 中使用 IP 地址和端口号来确定网络上的一台主机的一个程序 . 但是 IP 地址不 方便记忆 . 于是人们发明了一种叫主机名的东西 , 是一个字符串 , 并且…

computed计算属性与watch侦听器

1.computed计算属性的写法有两种&#xff0c;一种是只读的&#xff0c;只负责展示&#xff0c;另一种可以进行修改&#xff0c;利用get来获取值&#xff0c;利用set来进行修改 2.watch侦听器的写法也有两种&#xff0c;可以直接写成函数&#xff0c;也可以写成对象&#xff0c;…

浅显易懂的Git教程

Git概述 SVN与Git的对比 SVN&#xff08;Subversion&#xff09; 类型&#xff1a;集中式版本控制系统 工作流程&#xff1a; 从中央服务器下载最新版本到本地。在本地进行开发。提交更改回中央服务器。 优点&#xff1a; 简单易用&#xff0c;适合小型团队。版本历史清…