数据结构——B树

文章目录

  • B树
    • 1. 概念
    • 2. B树插入分析
    • 3.插入过程
    • 4. B树插入实现
    • 5.B树验证
    • 6. B树性能分析
    • 7.B+树&B*树
    • 8. 小结
    • 9. B树的运用
      • MyISAM
      • InnoDB
    • 10. 总结


B树

可以用于查询的数据结构非常的多,比如说二插搜索树、平衡树、哈希表、位图、布隆过滤器,但如果需要存储一些数据量极大的数据,上述的数据结构就很不适用了,内存是不一定放的下的,比如用平衡树搜索一个贼大的文件。

因为数据量非常大,一次性无法加载到内存中,那么就可以在平衡树中保存数据项需要查找的部分,以及指向该数据在磁盘中的地址的指针。

虽然在上诉方法在内存中存放的是数据的地址,真实的数据是在磁盘上,解决了内存占用的问题。但是也有一定的缺陷:

  • 当数据量比较大的时候,比如使用的是红黑树,虽然时间复杂度是 O ( l o g n ) O(logn) O(logn),但数据量比较大的时候,树的高度是比较高的。
  • 当数据量特别大时,树中的节点可能无法一次性加载到内存中,这就需要增加磁盘的I/O次数

解决办法:就是减少I/O的次数,降低树的高度,引入多叉平衡树(B树)

1. 概念

B树是一棵M阶(M>2)的B树,是一棵平衡的M路平衡搜索树,可以是空树或者满足一下性
质:

  1. 根节点至少有两个孩子
  2. 每个非根节点至少有 M / 2 − 1 M/2-1 M/21(上取整)个关键字,至多有 M − 1 M-1 M1个关键字,并且以升序排列
    例如:当M=3的时候,至少有3/2=1.5,向上取整等于2,2-1=1个关键字,最多是2个关键字
  3. 每个非根节点至少有 M / 2 M/2 M/2(上取整)个孩子,至多有M个孩子
    例如:当M=3的时候,至少有3/2=1.5,向上取整等于2个孩子。最多有3个孩子。
  4. key[i]和key[i+1]之间的孩子节点的值介于key[i]、key[i+1]之间
  5. 所有的叶子节点都在同一层

2. B树插入分析

为了简单起见,假设M = 3. 即三叉树,每个节点中存储两个数据,两个数据可以将区间分割成三个部分,因此节点应该有三个孩子,为了后续实现简单期间,节点的结构如下:

在这里插入图片描述

注意:孩子永远比数据多一个
插入过程当中,有可能需要分裂,分裂的
前提是:
假设,当前是要组成一个M路查找树,关键字数必须<=M-1**(这里关键字数**>M-1**就要进行节点拆分)**规则是:
把中间的元素,提取出来,放到父亲节点上,左边的单独构成一个节点,右边的单独构成一个节点。

假设用 53 , 139 , 75 , 49 , 145 , 36 , 101 {53,139,75,49,145,36,101} 53,139,75,49,145,36,101构造B树的过程如下:

  1. 先插入53和139

在这里插入图片描述

  1. 再插入75,插入75后按照插入排序的思想对元素进行排序

    在这里插入图片描述

  2. 因为该节点的元素已经满了,就需要进行分裂

    节点分裂的步骤:

    • 找到节点数据域的中间位置
    • 给一个新节点,将中间位置的数据移动到新创建的节点中
    • 将中间位置的那个数据移动到父节点中
    • 将节点的关系连接好

    在这里插入图片描述

  3. 插入49和145

    • 找到该元素的插入位置的位置
    • 按照插入排序的思想将节点插入到该节点的合适位置
    • 再检测是否满足B数的性质
    • 满足插入结束,不满足对节点进行分裂

    在这里插入图片描述

  4. 插入36

    插入后违法了B树的性质,需要进行分裂

    在这里插入图片描述

    对cur节点进行分裂:

    • 找到中间位置
    • 将中间位置右侧元素移动到新节点中
    • 将中间位置元素49移动到父节点parent中
    • 将新节点的父节点修改成parent

    在这里插入图片描述

  5. 插入数字101

    • 在B树中找到该节点的插入位置
    • 按照插入排序思想将该节点插入到指定位置
    • 检测插入后的节点元素个数是否小于M
    • 如果小于M插入完成,等于M则需要对该节点进行分裂

在这里插入图片描述

在这里插入图片描述

分裂完后,发现根节点也需要进行分裂,根节点分裂需要进行特殊处理,因为它会增加树的高度。

在这里插入图片描述

3.插入过程

  1. 如果树为空,直接插入新节点中,该节点为树的根节点

  2. 树非空,找待插入元素在树中的插入位置(注意:找到的插入节点位置一定在叶子节点中)

  3. 检测是否找到插入位置(假设树中的key唯一,即该元素已经存在时则不插入)

  4. 按照插入排序的思想将该元素插入到找到的节点中

  5. 检测该节点是否满足B-树的性质:即该节点中的元素个数是否等于M,如果小于则满足

  6. 如果插入后节点不满足B树的性质,需要对该节点进行分裂:

    • 申请新节点
    • 找到该节点的中间位置
    • 将该节点中间位置右侧的元素以及其孩子搬移到新节点中
    • 将中间位置元素以及新节点往该节点的双亲节点中插入,即继续4
  7. 如果向上已经分裂到根节点的位置,插入结束

4. B树插入实现

B树的节点定义

// M叉树
private static final int M = 3;
static class BTreeNode {
    // 节点关键字
    int[] keys;
    // 孩子
    BTreeNode[] subs;
    // 关键字个数
    int keySize;
    // 父节点
    BTreeNode parent;

    public BTreeNode() {
        keys = new int[M];
        // 多给一个方便分裂
        subs = new BTreeNode[M+1];
    }
}
    

定义一个类方便查找指定节点

public class Pair<K,V> {
    K key;
    V val;

    public Pair(K key, V val) {
        this.key = key;
        this.val = val;
    }
}

插入代码

/**
 * B树
 */
public class BTree {
    // B树根节点
    BTreeNode root;

    public boolean insert(int key) {
        if (root == null) {
            root = new BTreeNode();
            root.keys[0] = key;
            root.keySize++;
            return true;
        }
        // 查找关键字是否已经存在
        Pair<BTreeNode,Integer> cur = find(key);
        if (cur.val != -1) {
            // 说明key已经存在,插入失败
            return false;
        }
        // 开始插入(插入排序)
        BTreeNode parent = cur.key;
        int i = 0;
        for (i = parent.keySize-1; i >= 0; i--) {
            if (parent.keys[i] >= key) {
                parent.keys[i+1] = parent.keys[i];
            } else {
                break;
            }
        }
        parent.keys[i+1] = key;
        parent.keySize++;

        // 判断是否需要分裂
        if (parent.keySize >= M) {
            split(parent);
        }
        return true;
    }

    /**
     * 对cur节点进行分裂
     * @param cur
     */
    private void split(BTreeNode cur) {
        BTreeNode newNode = new BTreeNode();
        BTreeNode parent= cur.parent;
        // 中间位置
        int mid = cur.keySize>>1;
        // 把mid往后的元素移动到新节点
        int j = 0;
        int i = mid+1;
        for (; i < cur.keySize; i++) {
            newNode.keys[j] = cur.keys[i];
            // 子节点引用
            newNode.subs[j] = cur.subs[i];
            // 修改子节点的parent引用
            if (newNode.subs[j] != null) {
                newNode.subs[j].parent = newNode;
            }
            newNode.keySize++;
            j++;
        }
        // 多拷贝一次最后的子节点
        newNode.subs[j] = cur.subs[i];
        if (newNode.subs[j] != null) {
            newNode.subs[j].parent = newNode;
        }
        //更新分裂节点关键字个数,-1指的是mid位置的节点要向上提
        cur.keySize = cur.keySize-mid-1;

        // 处理根节点分裂情况!
        if (cur == root) {
            root = new BTreeNode();
            root.keys[0] = cur.keys[mid];
            root.keySize++;
            root.subs[0] = cur;
            root.subs[1] = newNode;
            cur.parent = root;
            newNode.parent = root;
            return;
        }
        // 更新新节点的父亲节点
        newNode.parent = parent;

        // 将mid位置的元素向上提
        int midVal = cur.keys[mid];
        i = parent.keySize-1;
        for (; i >= 0; i--) {
            if (parent.keys[i] >= midVal) {
                parent.keys[i+1] = parent.keys[i];
                parent.subs[i+2] = parent.subs[i+1];
            } else {
                break;
            }
        }
        parent.keys[i+1] = midVal;
        parent.keySize++;
        // 将当前父节点的孩子节点设置为newNode
        parent.subs[i+2] = newNode;
        // 如果个数超过M递归分裂
        if (parent.keySize >= M) {
            split(parent);
        }
    }

    /**
     * 在B树中查找关键字是否已经存在
     * 找到返回节点和起下标,否则返回其父节点和-1
     * @param key
     * @return
     */
    private Pair<BTreeNode, Integer> find(int key) {
        BTreeNode cur = root;
        BTreeNode parent = root;
        int index = 0;
        while (cur != null) {
            while (index < cur.keySize) {
                if (cur.keys[index] == key) {
                    return new Pair<>(cur,index);
                } else if (cur.keys[index] < key) {
                    index++;
                } else {
                    break;
                }
            }
            parent = cur;
            // 向子节点找
            cur = cur.subs[index];
            index = 0;
        }
        return new Pair<>(parent,-1);
    }
}

5.B树验证

对B树进行中序遍历,只要打印出来的数据是有序说明插入正确

/**
     * 验证B树
     * @param root
     */
    private void inorder(BTreeNode root){
        if(root == null)
            return;
        for(int i = 0; i < root.keySize; ++i){
            inorder(root.subs[i]);
            System.out.println(root.keys[i]);
        }
        inorder(root.subs[root.keySize]);
    }

6. B树性能分析

对于一棵节点为N度的B树,查找和插入需要 l o g M − 1 N log_{M-1}N logM1N~ l o g M / 2 N log_{M/2}N logM/2N次比较。对于度为M的B树,每一个节点的子节点个数为 M / 2 M/2 M/2~ ( M − 1 ) (M-1) (M1)之间,因此树的高度应该要在 l o g M − 1 N log_{M-1}N logM1N l o g M / 2 N log_{M/2}N logM/2N之间,在定位到该节点后,再采用二分查找的方式可以很快的定位到该元素。

B树的效率是很高的对于 N = 62 ∗ 1000000000 N=62*1000000000 N=621000000000个节点,如果M为1024,则 l o g M / 2 N < = 4 log_{M/2}N<=4 logM/2N<=4,既在620亿个元素中,如果这棵树的度为1024,则需要小于4次即可定位到该节点,然后利用二分查找,可以快速定位到该元素,大大减少了读取磁盘的次数。

总结:

  1. B树实际上是一棵M叉平衡树
  2. 非根节点的关键字数量[M/2-1,M-1],因为每次满了之后,会拷走一半
  3. 非根节点的孩子数量[M/2,M]
  4. 所有的叶子节点都在同一层,所以B树是天然平衡的
  5. M越大效率越高,但M越大也会有空间浪费的问题,节点分裂会拷走一半就会存在空间浪费的问题

7.B+树&B*树

B+树是B树的变形,也是一种多路搜索树:

其定义基本与B树相同,除了:

  1. B+树非叶子节点的子树指针与关键字个数相同,而B树的孩子数量比关键字多一个
  2. B+树非叶子节点的子树指针指向的子树都属于它的右子树,也就是大于当前节点
  3. 为所有叶子节点增加一个链指针,也就是叶子节点是一个链表
  4. B+数的叶子节点存储了数据,而非叶子节点只是存储了关键字。

在这里插入图片描述

对于B+树来说,如果要查找数据,那么一定得遍历整个树的高度,因为数据在叶子节点,但B树的每个节点都存储了数据,就不一定要遍历到叶子节点。

B+树的分裂:当一个结点满时,分配一个新的结点,
并将原结点中1/2的数据
复制到新结点,最后在父结点中增加新结点的指针,B+树的分裂只影响原结点和父
结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;

B*树

B*树是B+树的变形,在B+树的非根和非叶子节点再增加指向兄弟节点的指针。

在这里插入图片描述

B树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了),如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;所以,B*树分配新结点的概率比B+树要低,空间使用率更高;

8. 小结

  • B树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键
    字范围的子结点;所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;
  • B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引,B+树总是到叶子结点才命中;
  • B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;

9. B树的运用

B-树最常见的应用就是用来做索引。索引通俗的说就是为了方便用户快速找到所寻之物,比如:书籍目录可以让读
者快速找到相关信息,hao123网页导航网站,为了让用户能够快速的找到有价值的分类网站,本质上就是互联网
页面中的索引结构。
MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构,简单来说:索引就是数据结
构。

注意:索引是基于表的,而不是基于数据库的

MyISAM

MyISAM引擎是MySQL5.5.8版本之前默认的存储引擎,不支持事物,支持全文检索,使用B+Tree作为索引结构,
叶节点的data域存放的是数据记录的地址,其结构如下:

在这里插入图片描述

上图是以以Col1为主键,MyISAM的示意图,可以看出MyISAM的索引文件仅仅保存数据记录的地址**。MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key**可以重复。如果想在Col2上建立一个辅助索引,则此索引的结构如下图所示 :

在这里插入图片描述

同样也是一棵B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算
法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
MyISAM的索引方式也叫做**“非聚集索引”**的

InnoDB

InnoDB存储引擎支持事务,其设计目标主要面向在线事务处理的应用,从MySQL数据库5.5.8版本开始,InnoDB存储引擎是默认的存储引擎。InnoDB支持B+树索引、全文索引、哈希索引。但InnoDB使用B+Tree作为索引结构
时,具体实现方式却与MyISAM截然不同。
第一个区别是InnoDB的数据文件本身就是索引文件MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而InnoDB索引,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保
存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引

在这里插入图片描述

上图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录,这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形
第二个区别是InnoDB的辅助索引data域存储相应记录主键的值而不是地址,所有辅助索引都引用主键作为data
域。

**聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录 **

10. 总结

  1. 像链表,顺序表这样的是否可以?

    时间复杂度太高,不行

  2. AVL树&红黑树是否可以?
    数据量大时树的高度较高,增加了I/O次数

  3. 哈希表是否可以?
    哈希表存在哈希冲突哈希只能支持 是或者不是。比如: where id =? 或者 where id =? 做不到范围查找: where id >10

  4. 为什么使用B+树,不适用B树?

    • 节点点不存储data,这样一个节点就可以存储更多的key。可以使得树更矮,所以I/O操作次数更少。
    • 叶子节点相连,更便于进行范围查找

聚簇索引和非聚簇索引的区别

  • 聚簇索引将数据行存储在与索引相同的 B+ 树结构中,而非聚簇索引是将索引和主键 ID 存储在 B+ 树结构中;
  • 数量限制不同:一张表只能有一个聚簇索引,但可以有多个非聚簇索引;
  • 范围查询不同:聚簇索引中的数据行与索引行是一一对应的,因此聚簇索引通常比非聚簇索引更适合范围查询,而非聚簇索引需要进行两次查找:首先查找索引,然后查找数据行
  • 非聚簇索引最大的缺点。 当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询,首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录
  • 在Innodb下主键索引是聚集索引,在Myisam下主键索引是非聚集索引
  • MyISAM 不提供事务支持。InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别。
  • MyISAM 不支持外键,而 InnoDB 支持。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Oracle(10)Managing Undo Data

目录 一、基础知识 1、AUM :Init Parameters AUM:初始化参数 2、AUM:Other Parameters AUM:其他参数 3、AUM:Sizing an UNDO TS AUM:调整UNDOTS的大小 4、AUM :Undo Quota AUM:撤消配额 5、Get Undo Segment Info 获取撤消段信息 二、基础操作 1、AUM:UNDO Tablespace …

京东大数据平台(京东数据分析):9月京东牛奶乳品排行榜

鲸参谋监测的京东平台9月份牛奶乳品市场销售数据已出炉&#xff01; 9月份&#xff0c;牛奶乳品市场销售呈大幅上涨。鲸参谋数据显示&#xff0c;今年9月&#xff0c;京东平台牛奶乳品市场的销量为2000万&#xff0c;环比增长约65%&#xff0c;同比增长约3%&#xff1b;销售额为…

SSL数字证书服务

SSL/TLS 证书允许Web浏览器使用安全套接字层/传输层安全 (SSL/TLS) 协议识别并建立与网站的加密网络连接。 SSL数字证书主要功能 SSL证书在浏览器或用户计算机与服务器或网站之间建立加密连接。这种连接可以保护传输中的敏感数据免遭非授权方的拦截&#xff0c;从而使在线交易…

【软件测试】其实远远不止需求文档这么简单

我们都知道&#xff0c;软件测试是一门依赖性很强的综合技术&#xff0c;软件测试工程师在施行自己的工作时&#xff0c;总是要依赖其他团队的产出。 比如&#xff0c;我们要依赖着需求团队给出的需求分析说明书来确定测试的方向&#xff0c;又要依赖开发团队产出的实际代码产品…

从UEFI如何启动到系统

从UEFI如何启动到系统 文章目录 从UEFI如何启动到系统UEFI须知1. 进入UEFI setup界面2. Setup界面3. BootManager界面4. Shell下操作4.1. 显示启动设备4.2. 进入设备及查看文件4.3. UEFI下的其他操作4.4. UEFI下的一些Shell命令 5. UEFI下更新固件方法GRUBGRUB界面1. 编辑GRUB选…

【unity小技巧】实现由滑动条控制音量的大小

文章目录 前言开始1.配置BGM2.滑动条3.文本组件4.新增音量控制脚本 完结 前言 这期来一个比较基础的课程&#xff0c;也是比较常用的&#xff0c;unity使用滑动条控制音量的大小 开始 1.配置BGM 2.滑动条 3.文本组件 4.新增音量控制脚本 public class VolumeController : M…

数据结构-邻接表广度优先搜索(C语言版)

对于一个有向图无向图&#xff0c;我们下面介绍第二种遍历方式。 广度优先搜索&#xff0c;即优先对同一层的顶点进行遍历。 如下图所示&#xff1a; 该例子&#xff0c;我们有六个顶点&#xff0c; 十条边。 对于广度优先搜索&#xff0c;我们先搜索a&#xff0c;再搜索abc…

人工智能-深度学习计算:层和块

我们关注的是具有单一输出的线性模型。 在这里&#xff0c;整个模型只有一个输出。 注意&#xff0c;单个神经网络 &#xff08;1&#xff09;接受一些输入&#xff1b; &#xff08;2&#xff09;生成相应的标量输出&#xff1b; &#xff08;3&#xff09;具有一组相关 参数…

【Unity之UI编程】如何用UGUI搭建一个登录注册面板

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;UI_…

15 _ 二分查找(上):如何用最省内存的方式实现快速查找功能?

今天我们讲一种针对有序数据集合的查找算法:二分查找(Binary Search)算法,也叫折半查找算法。二分查找的思想非常简单,很多非计算机专业的同学很容易就能理解,但是看似越简单的东西往往越难掌握好,想要灵活应用就更加困难。 老规矩,我们还是来看一道思考题。 假设我们…

深度学习之基于Yolov5人体姿态摔倒识别分析报警系统(GUI界面)

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 系统设计概述&#xff1a; 传感器采集&#xff1a;通过在场景中布置摄像头或红外传感器等设备&#xff0c;采集人体…

聊一聊关于手机Charge IC的电流流向

关于手机Charge&#xff0c;小白在以前的文章很少讲&#xff0c;一是这部分东西太多&#xff0c;过于复杂。二是总感觉写起来欠缺点什么。但后来想一想&#xff0c;本是抱着互相学习来写文章的心理态度&#xff0c;还是决定尝试写一些。 关于今天要讲的关于手机Charge的内容&a…

2023 electron最新最简版打包、自动升级详解

这里我将讲解一下从0搭建一个electron最简版架子&#xff0c;以及如何实现打包自动化更新 之前我有写过两篇文章关于electron框架概述以及 常用api的使用&#xff0c;感兴趣的同学可以看看 Electron桌面应用开发 Electron桌面应用开发2 搭建electron 官方文档&#xff1a;ht…

使用Ansible中的playbook

目录 1.Playbook的功能 2.YAML 3.YAML列表 4.YAML的字典 5.playbook执行命令 6.playbook的核心组件 7.vim 设定技巧 示例 1.Playbook的功能 playbook 是由一个或多个play组成的列表 Playboot 文件使用YAML来写的 2.YAML #简介# 是一种表达资料序列的格式,类似XML #特…

康耐视VisionPro+C#程序编写

添加引用&#xff0c;用什么就添加什么 康耐视控件名 代码实现 引用命名空间 using Cognex.VisionPro.PMAlign; 实例化工具及训练区域设置 CogPMAlignTool cogPMAlignTool new CogPMAlignTool(); cogPMAlignTool.InputImage cogImageFileTool.OutputImage as CogImage8…

MATLAB_双馈风力发电机-900V直流混合储能并网系统MATLAB仿真

仿真平台为&#xff1a;matlab2016b 双馈感应风机模块、采用真实风速数据。混合储能模块、逆变器模块、转子过电流保护模块、整流器控制模块、逆变器控制模块等模块。 下图分别为母线直流电压以及有功无功输出&#xff1a; 混合储能系统——蓄电池以及超级电容的SOC为&#x…

【Linux系统编程】系统用户和权限的操作

目录 一&#xff0c;Linux的用户 1&#xff0c;用户之间的切换 2&#xff0c;超级用户权限的使用 二&#xff0c;Linux的文件权限 1&#xff0c;文件信息的介绍 2&#xff0c;文件权限的修改 3&#xff0c;用户的修改 3-1&#xff0c;拥有者的更改 3-2&#xff0c;所属…

国际物流常见风险如何规避_箱讯科技

外贸物流是国际贸易的重要环节&#xff0c;其管理和效率的高低直接影响着贸易的成本和效益。因此&#xff0c;外贸企业应该重视物流的组织和管理&#xff0c;提高物流运作的效率。 国际物流基础知识 01什么是“双清包税”和“双清不包税” 双清包税上门又叫双清包税到门&…

vue3+element Plus实现弹框的拖拽、可点击底层页面功能

1、template部分 <el-dialog:modal"false"v-model"dialogVisible"title""width"30%"draggable:close-on-click-modal"false"class"message-dialog"> </el-dialog> 必须加的属性 modal:是否去掉遮罩层…