Java(二十七)---二叉搜索树以及Map和Set

news2024/11/14 12:18:08

文章目录

  • 前言
  • 1.二叉搜索树
    • 1.1.概念
    • 1.2.操作--- 插入
    • 1.3.操作---搜索
    • 1.4.操作---删除
    • 1.6.性能分析
    • 1.7 和 java 类集的关系
  • 2.搜索
    • 2.1.概念和场景
    • 2.2.模型
  • 3.Map的使用
    • 3.1.关于Map的说明
    • 3.2.Map.Entry<K,V>的说明
    • 3.3.Map中常用的方法
    • 3.4.TreeMap的使用案例
  • 4.Set的使用
    • 4.1 常见方法说明
  • 5.哈希
    • 5.1.概念
    • 5.2 冲突-概念
    • 5.3 冲突-避免
    • 5.4 冲突-避免-哈希函数设计
    • 5.5 冲突-避免-负载因子调节(重点掌握)
    • 5.6 冲突-解决
    • 5.7 冲突-解决-闭散列
    • 5.8 冲突-解决-开散列/哈希桶(重点掌握)
    • 5.9 冲突严重时的解决办法


前言

前几篇博客中,我们学习了有关二叉树、优先级队列等知识点,今天我们学习新的知识点—二叉搜索树,Map以及Set


1.二叉搜索树

1.1.概念

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

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

在这里插入图片描述

1.2.操作— 插入

第一步:判断root是否为空,为空的话,就把新节点定义为头结点,不然的话,就要进行遍历。
第二步:创建三个节点,cur(用于遍历),parent(用于记录cur前一个节点),node(新的节点),cur进行遍历,遍历完后找到相应的位置,让parent的left或者right节点跟node建立联系。


public class BinarySearchTree {
    static class TreeNode{
        public int val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int val) {
            this.val = val;
        }
    }
    public TreeNode root;
    public void insert(int key){
        if (root == null){
            root = new TreeNode(key);
            return;
        }
        TreeNode parnet = null;
        TreeNode cur = root;
        TreeNode node = new TreeNode(key);
        while (cur != null){
            if (cur.val > key){
                parnet = cur;
                cur = cur.left;
            }else if (cur.val < key){
                parnet = cur;
                cur = cur.right;
            }else {
                return;
            }
        }
        if (parnet.val > key){
            parnet.left = node;
        }else {
           parnet.right = node;
       }
    }
}

1.3.操作—搜索

这段代码就不多进行介绍了,相信大家可以看懂

 public TreeNode search(int key){
        TreeNode cur = root;
        while (cur != null){
            if (cur.val > key){
                cur = cur.left;
            }else if (cur.val < key){
                cur = cur.right;
            }else {
                return cur;
            }
        }
        return null;
    }

1.4.操作—删除

这个删除操作不简单,主要是要想好几种情况,我们使用替代法,但是要找到那个需要替代的节点并不容易,现在我们进行分类讨论。
其中cur 中 有一段有空 (cur.left == null 或者是 cur.right == null) ,这两种情况比较大同小异,我已cur.left == null 举例
在这里插入图片描述
cur.right == null 跟 上面情况一模一样,我就不在过多叙述。
但是如果 cur.left != null 并且 cur.right != null 的话,那么就需要使用替代法啦。
在这里插入图片描述
找谁去替代呢?
从 cur 的左右子树开始找,可以找 cur.left后的节点的最大值(cur.left 后面的右节点为空),或者就是cur.right 后的节点的最小值(cur.right后面的左节点为空)。
在这我们选择第二个,找cur.right 后的节点的最小值

 public void remove(int key){
        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null){
            if (cur.val > key){
                parent = cur;
                cur = cur.left;
            }else if (cur.val < key){
                parent = cur;
                cur = cur.right;
            }else {
                removeNode(parent,cur);
                return;
            }
        }
    }

    private void removeNode(TreeNode parent, TreeNode cur) {
        if (cur.left == null){
           if (cur == root){
               root = root.right;
           }else if(cur == parent.left){
               parent.left = cur.right;
           }else if (cur == parent.right){
               parent.right = cur.right;
           }
        } else if (cur.right == null) {
            if (cur == root){
                root = root.right;
            } else if (cur == parent.left) {
                parent.left = cur.left;
            }else if (cur == parent.right){
                parent.right = cur.left;
            }
        } else {
            TreeNode targetParent = cur;
            TreeNode target = cur.right;
            while (target != null){
                targetParent = target;
                target = target.left;
            }
            cur.val = target.val;
            if (targetParent.left == target){
                targetParent.left = target.right;
            }else {
                targetParent.right = target.right;
            }
        }
    }

1.6.性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

在这里插入图片描述
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:logN
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2

1.7 和 java 类集的关系

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


2.搜索

2.1.概念和场景

Map和Set是一种专门用来搜索的容器和数据结构,其搜索效率与其具体实例化的例子有关,以前我们常用的搜索方法:
直接查找,时间复杂度是O(n),随着元素的增加,效率会变慢。
二分查找:时间复杂度是O(logn),前提是:数组必须是有序地
上述的搜索方法比较适合于静态的,即一般不会在区间中进行增加和查改,例如:

  1. 根据姓名查询学生考试成绩
  2. 通讯录,即根据姓名进行查找联系电话
  3. 不重复集合,即需要先搜索关键字是否已经在集合中
    可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,本节介绍的Map和Set是一种适合动态查找的集合容器。

2.2.模型

我们一般吧关键字称为key,把关键字对应的值称为value,有两种存储模型:

  1. 纯k模型,例如:
    快速查找一个单词,是否出现在字典中

  2. 键值对key-value模型:例如:
    统计一句话中,每个单词出现的次数。

而Map中存储的就是key-value的键值对,Set中只存储了Key。

3.Map的使用

在这里插入图片描述
我们再把这个照片拿出来,我们会发现。现在只剩下了Map和Set,下面来介绍一下Map

3.1.关于Map的说明

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

3.2.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的方法

3.3.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>>entry返回所有的key-value 映射关系
boolean containsKey(Object key)判断是否包含key
boolean containsValue(Object value)判断是否包含value

注意:

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

3.4.TreeMap的使用案例

public static void main(String[] args) {
        Map<String ,Integer> map = new TreeMap<>();
        map.put("hello",4);
        map.put("world",20);
        map.put("Tom",5);
        // 获得每个key所对应的value
        Set<Map.Entry<String,Integer>> set =map.entrySet();
        for(Map.Entry<String,Integer>entry:set){
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
        //修改"Tom"的值,对应值加1
        int val = map.get("Tom");
        map.put("Tom",val+1);

        map.getOrDefault("Jerry",20);
        System.out.println(map.containsKey("Tom"));
        System.out.println(map.containsValue(10));
    }

大家可使用TreeMap来实例化Map,看看TreeMap和HashMap的不同。

4.Set的使用

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

4.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中,可以达到去重的效果
注意:
  1. Set是继承自Collection的一个接口类
  2. Set中只存储了key,并且要求key一定要唯一
  3. TreeSet的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的
  4. Set最大的功能就是对集合中的元素进行去重
  5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础
    上维护了一个双向链表来记录元素的插入次序。
  6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
  7. TreeSet中不能插入null的key,HashSet可以。
  8. TreeSet和HashSet的区别【HashSet在课件最后会讲到】
Set底层结构TreeMapHashMap
底层结构红黑树哈希桶
插入/删除/查找的时间复杂度O(logn)O(1)
是否有序关于Key有序不一定有序
线程安全不安全不安全
插入/删除/查找的区别按照红黑树的特性来进行插入和删除1.先计算key的哈希值
比较与复写key必须能够比较,否则会抛出ClassCastException异常自定义类型需要复写equals和hashCode方法
应用场景需要Key有序的场景下Key是否有序不关心,需要更高的时间性能

5.哈希

5.1.概念

顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(logN ),搜索的效率取决于搜索过程中元素的比较次数。
理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素
当向该结构中:
插入元素
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
搜索元素
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(HashTable)(或者称散列表)
例如:
数据集合{1,7,6,4,5,9};
哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小。
在这里插入图片描述
但是如果要存放44 ,该怎么存呢?

5.2 冲突-概念

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

5.3 冲突-避免

首先,我们需要明确一点,由于我们哈希表底层数组的容量往往是小于实际要存储的关键字的数量的,这就导致一个问题,冲突的发生是必然的,但我们能做的应该是尽量的降低冲突率

5.4 冲突-避免-哈希函数设计

引起哈希冲突的一个原因可能是:哈希函数设计不够合理。 哈希函数设计原则
哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间哈希函数计算出来的地址能均匀分布在整个空间中哈希函数应该比较简单
常见哈希函数

  1. 直接定制法–(常用)
    取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B 优点:简单、均匀 缺点:需要事先知道关
    键字的分布情况
    使用场景:适合查找比较小且连续的情况
  2. 除留余数法–(常用)
    设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:
    Hash(key) = key% p(p<=m),将关键码转换成哈希地址
  3. 平方取中法–(了解)
    假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址; 再比如关键字为4321,对它平方就是18671041,抽取中间的3位671(或710)作为哈希地址 平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况
  4. 折叠法–(了解)
    折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和,
    并按散列表表长,取后几位作为散列地址。
    折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况
  5. 随机数法–(了解)
    选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中random为随机数函数。
    通常应用于关键字长度不等时采用此法

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

在这里插入图片描述
负载因子和冲突率的关系粗略演示
在这里插入图片描述
所以当冲突率达到一个无法忍受的程度时,我们需要通过降低负载因子来变相的降低冲突率。
已知哈希表中已有的关键字个数是不可变的,那我们能调整的就只有哈希表中的数组的大小。

5.6 冲突-解决

解决哈希冲突两种常见的方法是:闭散列和开散列

5.7 冲突-解决-闭散列

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。那如何寻找下一个空位置呢?
线性探测
比如上面的场景,现在需要插入元素44,先通过哈希函数计算哈希地址,下标为4,因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。
线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

插入

  • 通过哈希函数获取待插入元素在哈希表中的位置
  • 如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素

在这里插入图片描述

采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标
记的伪删除法来删除一个元素。
二次探测
线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨
着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为: = ( + )% m, 或者:
= ( - )% m。其中:i = 1,2,3…, 是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置,m是表的大小。 对于2.1中如果要插入44,产生冲突。

研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不
会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容。
因此:比散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷。

5.8 冲突-解决-开散列/哈希桶(重点掌握)

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

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

5.9 冲突严重时的解决办法

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

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

大家先把上面的消化一下,下面一篇博客,我们把哈希桶,HashMap的源码,以及有关Map和Set的编程题做一下,下一篇博客不见不散

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

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

相关文章

探索 Milvus 存储系统:如何评估和优化 Milvus 存储性能

欢迎来到探索 Milvus 系列。Milvus 是一款支持水平扩展和具备出色性能的开源向量数据库。Milvus 的核心是其强大的存储系统&#xff0c;是数据持久化和存储的关键基础。该系统包括几个关键组成部分&#xff1a;元数据存储&#xff08;meta storage&#xff09;、消息存储&#…

Vs2022+QT+Opencv 一些需要注意的地方

要在vs2022创建QT项目&#xff0c;先要安装一个插件Qt Visual Studio Tools&#xff0c;根据个人经验选择LEGACY Qt Visual Studio Tools好一些&#xff0c;看以下内容之前建议先在vs2022中配置好opencv&#xff0c;配置方式建议以属性表的形式保存在硬盘上。 设置QT路径 打开v…

数学建模--差值算法

目录 插值方法的种类 应用实例 编程实现 算法实现 拉格朗日插值算法 ​编辑 多项式差值算法 样条插值 牛顿插值算法 插值算法在数据预测中的最新应用和案例研究是什么&#xff1f; 如何比较不同插值方法&#xff08;如线性插值、多项式插值&#xff09;在实际工程问…

bjtu数据库课程设计--基于Spring Boot框架的门店点餐系统

一、安装与配置 1 安装与配置 下载IntelliJ IDEA&#xff0c;需要下载安装jdk1.8.0_152&#xff0c;安装tomcat-9.0.88&#xff0c;安装MySQL8.0数据库。安装成功后打开IntelliJ IDEA&#xff0c;使用 Spring Boot 2.6.13框架&#xff0c;服务器URL窗口使用start.aliyun.com&a…

AI副业玩法:开启你的智能赚钱之路

在这个数码时代&#xff0c;人工智能&#xff08;AI&#xff09;已经不仅仅是科技巨头的专利&#xff0c;它逐渐渗透到我们生活的方方面面。如今&#xff0c;越来越多的人开始利用AI技术进行副业尝试&#xff0c;既拓宽了收入来源&#xff0c;也提升了自我技能。那么&#xff0…

【前端 07】JavaScript中的数组对象

JavaScript中的数组对象 在JavaScript中&#xff0c;数组&#xff08;Array&#xff09;对象是一种非常基础且强大的数据结构&#xff0c;用于在单个变量中存储多个值。这些值可以是任何数据类型&#xff0c;包括数字、字符串、甚至是其他数组&#xff08;多维数组&#xff09…

实验2-4-2 求N分之一序列前N项和**注意小细节

//实验2-4-2 求N分之一序列前N项和//计算序列 1 1/2 1/3 ... 的前N项之和。#include<stdio.h> #include<math.h> int main(){int N;double sum0.0;scanf("%d",&N);for(int a1;a<N;a)sum(1.0/a);//这里必须是1.0 不可以是1&#xff01;&#x…

【管理咨询宝藏150】MBB咨询顾问的结构化PPT训练课程

【管理咨询宝藏150】MBB咨询顾问的结构化PPT训练课程 【格式】PDF版本 【关键词】MBB、麦肯锡、罗兰贝格、咨询顾问 【核心观点】 - 在项目的开始阶段你着手发展有效的金字塔式的演示文稿—我们的重点在最后两个步骤&#xff1b;我们用金字塔结构&#xff1a;通过把核心的信息…

使用nginx解决本地环境访问线上接口跨域问题

前言 前端项目开发过程中&#xff0c;经常会遇到各种各样的跨域问题。 虽然大部分时候&#xff0c;由脚手架自带的proxy功能即可解决问题&#xff0c;如webpack&#xff0c;vite等&#xff1b;但是若没有通过脚手架搭建项目&#xff0c;或者必须使用某些特殊规则转发时&#…

<数据集>手机识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;16172张 标注数量(xml文件个数)&#xff1a;16172 标注数量(txt文件个数)&#xff1a;16172 标注类别数&#xff1a;1 标注类别名称&#xff1a;[Phone] 使用标注工具&#xff1a;labelImg 标注规则&#xff1a;…

【QT】qt 文件操作

qt 文件 qt 文件1. Qt 文件概述2. 输入输出设备类3. 文件读写类4. 文件和目录信息类 qt 文件 1. Qt 文件概述 文件操作是应用程序必不可少的部分。Qt 作为⼀个通用开发库&#xff0c;提供了跨平台的文件操作能力。 Qt 提供了很多关于文件的类&#xff0c;通过这些类能够对文件…

上海有机所化学数据库:一站式科研资源

上海有机化学研究所是中国科学院上海分院的直属机构&#xff0c;主要从事有机化学、材料化学、生命科学等领域的基础研究和应用研究&#xff0c;化学专业数据库是该所承担建设的综合科技信息数据库的重要组成部分&#xff0c;服务于化学化工研究和开发的综合性信息系统&#xf…

Javaweb项目|springboot医院管理系统

收藏点赞不迷路 关注作者有好处 文末获取源码 一、系统展示 二、万字文档展示 基于springboot医院管理系统 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringSpringMVCMyBatisVue 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 编号&#xff1a;…

【海贼王航海日志:前端技术探索】HTML你学会了吗?(一)

目录 1 -> HTML概念 2 -> HTML结构 2.1 -> 认识HTML标签 2.2 -> HTML文件基本结构 2.3 -> 标签层次结构 3 -> 快速生成代码框架 4 -> HTML常见标签 4.1 -> 注释标签 4.2 -> 标题标签 4.3 -> 段落标签 4.4 -> 换行标签 4.5 ->…

多线程-进阶2

博主主页: 码农派大星. 数据结构专栏:Java数据结构 数据库专栏:MySQL数据库 JavaEE专栏:JavaEE 关注博主带你了解更多数据结构知识 1.CAS 1.1CAS全称:Compare and swap 比较内存和cpu寄存器中的内容,如果发现相同,就进行交换(交换的是内存和另一个寄存器的内容) 一个内存的…

《学会 SpringBoot · 参数校验》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

连锁美业门店收银系统Java源码-如何设置门店仓库自提时间?博弈美业实操

1. 门店仓库自提时间&#xff0c;是当客户在小程序上购买实物商品时&#xff0c;预约上门提货的时间 2. 门店仓库自提时间&#xff0c;需要由各门店&#xff08;店主、店长、店员&#xff09;在PAD上进行设置 ▶ 操作路径&#xff1a; • 第一步&#xff1a; 进入【我的】页…

怎么进行图片压缩?对图片文件的大小进行压缩的四个方法介绍

怎么进行图片压缩&#xff1f;图片压缩是一种常见的技术&#xff0c;用于减小图像文件的大小&#xff0c;同时尽可能地保持图像的视觉质量和细节。这一过程不仅适用于个人用户想要节省存储空间或提高网页加载速度&#xff0c;也对于专业摄影师、网站设计师和应用程序开发者来说…

【OceanBase诊断调优】—— clog盘满问题排查

背景 日志盘&#xff0c;即clog盘&#xff0c;是oceanbase中用于记录事务日志信息。在日常运行中其存储量会随着事务处理情况不断变化。在一些特殊场景下会出现clog盘占用量超过阈值的情况。4.x架构下的clog盘&#xff0c;日志盘进行了租户级拆分&#xff0c;意味着无法再以3.…

Redis缓存数据库进阶——Redis缓存数据同步问题(8)

Redis缓存使用问题 数据一致性 只要使用到缓存&#xff0c;无论是本地内存做缓存还是使用 redis 做缓存&#xff0c;那么就会存在数据同步的问题。 我以 Tomcat 向 MySQL 中写入和删改数据为例&#xff0c;来给你解释一下&#xff0c;数据的增删改操作具体是如何进行的。 我…