数据结构实验哈夫曼编码算法的实现_哈夫曼编码算法的实现

数据结构实验哈夫曼编码算法的实现_哈夫曼编码算法的实现一、什么是赫夫曼编码哈夫曼编码(HuffmanCoding),又称霍夫曼编码,是一种编码方式,可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、什么是赫夫曼编码

哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,

使用赫夫曼编码可以有效的压缩数据,通常可以节省20%~90%的空间。

在理解赫夫曼编码前,我们需要对通讯领域的两种编码方式有个粗略的了解。

假设我们需要传输 I'm a jvav programmer and I love programming这样一句话,我们有两种传输方式:

  1. 定长编码

    直接转换为对应长度的二进制格式

    01101111 00100000 00100000 00100000 00100111 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100111 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000 00100000
    

    总长度为296个字符

  2. 变长编码

    按照各个字符出现的次数进行编码,按出现次数编码,出现次数越多的,则编码越小:

    比如空格出现最多次,然后是a,以此类推……

    0=  ,1=a,10=i,11=o......
    

    当传输的信息越多的时候,变长编码实际传输的长度相对定长编码就越小

另外,我们还需要了解一下什么是补码:

计算机里面只有加法器,没有减法器,所以减法必须用加法来完成。
对于 100 以内的十进制数,“-1”就可以用”+99″代替。比如 25 - 1 = 24,可以写成 25 + 99 = (1)24。
如果限定了两位数,那“-1”和“+99”就是等效的。同样,“-2”可以用“+98”代替。

它们之间,称为补数,而100就称为

对于8位二进制数:0000 0000~1111 1111(255),模为256
-1,可以用 +255(1111 1111)代替。
-2,可以用 +254(1111 1110)代替

这些二进制数,就称为负数的补码

二、赫夫曼编码思路

同样举个例子,我们要传输 i like like like java do you like a java这段话

  1. 统计各字符的出现次数

    d:1 y:1 u:1 j:2 v:2 o:2 l:4 k:4 e:4 i:5 a:5 :9
    
  2. 将字符出现次数作为节点的权,构建一个赫夫曼树(这里步骤同上一篇文章

    image-20200717213740072

  3. 我们使用0和1来描述某个节点在树中往左或往右的路径,比如j,从根节点出发抵达j的路径就是0000,抵达i的路径就是101

    于是现在对所有字符的路径进行统计,就有:

    o: 1000     u: 10010     d: 100110     y: 100111    i: 101    a : 110
    k: 1110     e: 1111      j: 0000       v: 0001      l: 001      : 01
    
  4. 按照上面的路径,我们将其转为二进制

    1010100110111101111010011011110111101001101111011110100001100001110011001111000011001111000100100100110111101111011100100001100001110
    

    直接转为二进制长度为359,而经过赫夫曼编码长度则是133,与直接转为二进制相比,缩短了62.9%

三、代码实现

1.创建节点类

/**
 * @Author:CreateSequence
 * @Date:2020-07-18 13:28
 * @Description:赫夫曼编码用节点
 */
public class HuffmanCodeNode implements Comparable<HuffmanCodeNode>{

    //字符
    Byte data;
    //权值
    int weight;
    HuffmanCodeNode left;
    HuffmanCodeNode right;

    public HuffmanCodeNode(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }

    public HuffmanCodeNode(HuffmanCodeNode left, HuffmanCodeNode right) {
        //计算子节点权值之和
        this.weight = left.weight + right.weight;
        this.left = left;
        this.right = right;
    }

    @Override
    public int compareTo(HuffmanCodeNode o) {
        //从小到大排序
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
        return "{" +
            "data=" + data +
            ", weight=" + weight +
            '}';
    }
    
}

2.统计字符出现次数,并组装节点列表

对应思路中的第一步:

/**
 * 统计字符在字符串中的出现次数,并组装节点列表
 * @param str 字符串
 * @return
 */
private List<HuffmanCodeNode> getNodes(String str) {
    //将字符串转为字符串数组
    byte[] strBytes = str.getBytes();

    //遍历字节数组,并且统计某一字符出现次数
    Map<Byte, Integer> counts = new HashMap<>(24);
    for (byte b : bytes) {
        Integer count = counts.get(b);
        //判断某一字符是否第一次出现
        if (count == null) {
            counts.put(b, 1);
        }else {
            //不是就让出现次数+1
            counts.put(b, count ++);
        }
    }

    //将map转为节点集合
    List<HuffmanCodeNode> nodes = new ArrayList<>();
    for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
        nodes.add(new HuffmanCodeNode(entry.getKey(), entry.getValue()));
    }

    return nodes;
}

3.生成赫夫曼树

对应思路中的第二步:

/**
 * 构建赫夫曼树
 * @param nodes 节点集合
 * @return 最终生成的树的根节点
 */
private HuffmanCodeNode createTree(List<HuffmanCodeNode> nodes) {
    //构建树
    while (nodes.size() > 1) {
        //从小到大排序
        Collections.sort(nodes);
        //取出最小的两个数构建树
        HuffmanCodeNode left = nodes.get(0);
        HuffmanCodeNode right = nodes.get(1);
        HuffmanCodeNode parant = new HuffmanCodeNode(left, right);
        //删除两个节点
        nodes.remove(left);
        nodes.remove(right);
        //将根节点添加至集合
        nodes.add(parant);
    }
    //返回树的根节点
    return nodes.get(0);
}

当然,这个时候可以通过前序遍历来检查是否构建成功

/**
 * 前序遍历
 * @param node 树的根节点
 */
private void preOrder(HuffmanCodeNode node) {
    //递归
    System.out.println(node.toString());
    if (node.left != null) {
        preOrder(node.left);
    }
    if (node.right != null) {
        preOrder(node.right);
    }
}

4.得到赫夫曼编码

对应思路中的第三步:

我们已经得到了赫夫曼树,现在我们需要获得从根节点到各个叶子结点的路径,也就是赫夫曼编码

/**
 * 生成赫夫曼树对应的赫夫曼编码集合
 */
private Map<Byte, String> huffmanCodes = new HashMap<>();
/**
 * 储存某个叶子节点的拼接路径
 */
private StringBuilder stringBuilder = new StringBuilder();

/**
 * 将传入的节点作为树的根节点,找到其所有的叶子结点的赫夫曼编码,并放入赫夫曼编码集合
 * @param node 节点
 * @param way 叶子结点的路径,左为0,右为1
 * @param builder 用于拼接路径
 */
private Map<Byte, String> getCodes(HuffmanCodeNode node, String way, StringBuilder builder) {
    StringBuilder stringBuilder = new StringBuilder(builder);
    //建路径拼接至上一路径
    stringBuilder.append(way);
    if (node != null) {
        //判断当前是否为叶子节点
        if (node.data == null) {
            //向左右递归直到找到叶子结点
            getCodes(node.left, "0", stringBuilder);
            getCodes(node.right, "1", stringBuilder);
        }else {
            //已经是叶子结点,将路径存入集合
            huffmanCodes.put(node.data, stringBuilder.toString());
        }
    }
    return huffmanCodes;
}

public Map<Byte, String> getCodes() {
    //构建赫夫曼树
    HuffmanCodeNode root = createTree();
    //处理左右子树
    getCodes(root.left, "0", stringBuilder);
    getCodes(root.right, "1", stringBuilder);
    //返回赫夫曼编码
    return huffmanCodes;
}

5.将得到的赫夫曼编码转回字节数组

对应思路中的第四步,也就是最后一步:

我们得到了赫夫曼编码表,也就是这玩意: Map<Byte, String> huffmanCodes,每串赫夫曼编码字符串都对应一个字符,我们需要处理赫夫曼编码的每一个字符,将其转为二进制后再转为byte,最后处理完得到一队字节数组。

/**
 * 将字符串对应的byte数组,转换为经过赫夫曼编码压缩后的byte数组
 * @param bytes
 * @param huffmanCodes
 * @return
 */
private byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
    //获取赫夫曼编码
    StringBuilder stringBuilder = new StringBuilder();
    //遍历byte数组,一个byte表示一个字符
    for (Byte b : bytes) {
        //将字符转为赫夫曼编码格式,一个字符对应8位编码
        stringBuilder.append(huffmanCodes.get(b));
    }

    //一个字符对应对应的8位的赫夫曼编码,如果赫夫曼编码无法被8整除,就直接补齐赫夫曼编码不足八位的那一个字符
    int len = stringBuilder.length() % 8 == 0 ? stringBuilder.length() / 8 : stringBuilder.length() / 8 + 1;
    //System.out.println("有几个字符:"+len);

    //将压缩后的赫夫曼编码按字符分开存储
    byte[] huffmanCodeBytes = new byte[len];
    //计录已处理几个字符
    int index = 0;
    //每8位编码对应一个byte,所以步长为8
    //每循环一次处理一个byte,也就是一个字符
    for (int i = 0; i < stringBuilder.length(); i += 8) {
        String strBytes;
        //判断编码长度是否超过8位
        if (i + 8 < stringBuilder.length()) {
            //超过8位就从赫夫曼编码截取八位(也就是一个字符)
            strBytes = stringBuilder.substring(i, i + 8);
        }else {
            //否则就有多少截多少
            strBytes = stringBuilder.substring(i);
        }
        //将赫夫曼编码转为二进制,存入byte数组
        huffmanCodeBytes[index] = (byte) Integer.parseInt(strBytes, 2);

        //位已处理字符数+1
        index++;
    }

    //循环结束后,返回赫夫曼编码按字符转换得到的字节数组
    return huffmanCodeBytes;
}

public byte[] zip() {
    byte[] bytes = str.getBytes();
    Map<Byte, String> huffmanCodes = getCodes();
    return zip(bytes, huffmanCodes);
}

6.解码

信息被赫夫曼编码处理后我们会得到一队字节数组,如果要解码,我们需要先把字节数组按字符一个字节一个字节的转为二进制,然后通过赫夫曼编码表把二进制和字符字节一一找出:

/**
 * 将byte转成二进制字符串
 * @param isComple 是否需要补高位。最后一个字节无需补位
 * @param b 要转换的字节
 * @return
 */
private String byteToString(boolean isComplate, byte b) {
    int temp = b;
    //判断是否需要补齐高位
    if (isComplate) {
        temp |= 256;
    }
    //返回temp对应的二进制补码
    String str = Integer.toBinaryString(temp);
    return isComplate ? str.substring(str.length() - 8) : str;
}

/**
 * 解码
 * @param huffmanCodes 赫夫曼编码表
 * @param huffmanBytes 赫夫曼编码处理过的字节数组
 * @return 原来未被转为赫夫曼编码的的字符串字节素组
 */
private byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {

    //将赫夫曼编码处理过byte数组转为二进制字符串
    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < huffmanBytes.length; i++) {

        boolean isComplate = true;
        //如果是最后一个字节就不用补高位了
        if (i == huffmanBytes.length - 1) {
            isComplate = false;
        }
        //拼接字节转的二进制字符串
        stringBuilder.append(byteToString(isComplate, huffmanBytes[i]));
    }

    //把字符串按照指定赫夫曼编码进行解码
    //原本赫夫曼编码表是<字节,二进制字符串>,现在要转为<二进制字符串,字节>以通过转换得到的二进制字符串取出对应的字节
    Map<String, Byte> reHuffmanCodes = new HashMap<>();
    for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
        reHuffmanCodes.put(entry.getValue(), entry.getKey());
    }

    List<Byte> bytes = new ArrayList<>();
    //由于无法确认拼接后的二进制字符串每八位一定就能和某个字节对应,所以需要进行字符串匹配
    //这里可以简单理解为双指针,一号指针从i开始,二号指针从i+1开始
    //一号指针先指向字符串第i字符,然后二号指针从i+1个字符开始不断后移,然后进行进行匹配
    //比如:i=0,j=1,第一次截取并匹配的字符就是[0,1),也就是0;第二次是[0,2),也就是01;然后是[0,3).....以此类推
    //直到找到以后,比如[2,7),就移动一号指针到7,二号指针移动到8
    for (int i = 0, j = 1; i < stringBuilder.length(); i = --j) {
        String key = "";
        while (!reHuffmanCodes.containsKey(key)) {
            key = stringBuilder.substring(i, j);
            j++;
        }
        bytes.add(reHuffmanCodes.get(key));
    }

    //由集合转为字节数组
    byte b[] = new byte[bytes.size()];
    for (int i = 0; i < b.length; i++) {
        b[i] = bytes.get(i);
    }

    return b;
}

四、完整代码

具体代码和测试用例可以去GitHub上看,这里就放实现类:

import java.util.*;

/**
 * @Author:CreateSequence
 * @Date:2020-07-18 13:26
 * @Description:赫夫曼编码
 */
public class HuffmanCode {

    private String str;

    public String getStr() {
        return str;
    }

    public HuffmanCode(String code) {
        if (code.length() == 0 || code == null) {
            throw new RuntimeException("字符串必须有至少一个字符!");
        }
        this.str = code;
    }

    /**
     * 统计字符在字符串中的出现次数,并组装节点列表
     * @return
     */
    public List<HuffmanCodeNode> getNodes() {
        //将字符串转为字符串数组
        byte[] bytes = str.getBytes();

        //遍历字节数组,并且统计某一字符出现次数
        Map<Byte, Integer> counts = new HashMap<>(24);
        for (byte b : bytes) {
            Integer count = counts.get(b);
            //判断某一字符是否第一次出现
            if (count == null) {
                counts.put(b, 1);
            }else {
                //不是就让出现次数+1
                counts.put(b, count ++);
            }
        }

        //将map转为节点集合
        List<HuffmanCodeNode> nodes = new ArrayList<>();
        for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
            nodes.add(new HuffmanCodeNode(entry.getKey(), entry.getValue()));
        }

        return nodes;
    }

    /**
     * 构建赫夫曼树
     * @param nodes
     * @return
     */
    private HuffmanCodeNode createTree(List<HuffmanCodeNode> nodes) {
        //构建树
        while (nodes.size() > 1) {
            //从小到大排序
            Collections.sort(nodes);
            //取出最小的两个数构建树
            HuffmanCodeNode left = nodes.get(0);
            HuffmanCodeNode right = nodes.get(1);
            HuffmanCodeNode parant = new HuffmanCodeNode(left, right);
            //删除两个节点
            nodes.remove(left);
            nodes.remove(right);
            //将根节点添加至集合
            nodes.add(parant);
        }
        //返回树的根节点
        return nodes.get(0);
    }
    public HuffmanCodeNode createTree(){
        return createTree(getNodes());
    }

    /**
     * 前序遍历
     * @param node 树的根节点
     */
    private void preOrder(HuffmanCodeNode node) {
        //递归
        System.out.println(node.toString());
        if (node.left != null) {
            preOrder(node.left);
        }
        if (node.right != null) {
            preOrder(node.right);
        }
    }
    public void preOrder(){
        HuffmanCodeNode root = createTree(getNodes());
        preOrder(root);
    }

    /**
     * 生成赫夫曼树对应的赫夫曼编码集合
     */
    private Map<Byte, String> huffmanCodes = new HashMap<>();
    /**
     * 储存某个叶子节点的拼接路径
     */
    private StringBuilder stringBuilder = new StringBuilder();

    /**
     * 将传入的节点作为树的根节点,找到其所有的叶子结点的赫夫曼编码,并放入赫夫曼编码集合
     * @param node 节点
     * @param way 叶子结点的路径,左为0,右为1
     * @param builder 用于拼接路径
     */
    private Map<Byte, String> getCodes(HuffmanCodeNode node, String way, StringBuilder builder) {
        StringBuilder stringBuilder = new StringBuilder(builder);
        //建路径拼接至上一路径
        stringBuilder.append(way);
        if (node != null) {
            //判断当前是否为叶子节点
            if (node.data == null) {
                //向左右递归直到找到叶子结点
                getCodes(node.left, "0", stringBuilder);
                getCodes(node.right, "1", stringBuilder);
            }else {
                //已经是叶子结点,将路径存入集合
                huffmanCodes.put(node.data, stringBuilder.toString());
            }
        }
        //返回赫夫曼编码
        return huffmanCodes;
    }

    public Map<Byte, String> getCodes() {
        //构建赫夫曼树
        HuffmanCodeNode root = createTree();
        //处理左右子树
        getCodes(root.left, "0", stringBuilder);
        getCodes(root.right, "1", stringBuilder);
        //返回赫夫曼编码
        return huffmanCodes;
    }

    /**
     * 将字符串对应的byte数组,转换为经过赫夫曼编码压缩后的byte数组
     * @param bytes
     * @param huffmanCodes
     * @return
     */
    private byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        //获取赫夫曼编码
        StringBuilder stringBuilder = new StringBuilder();
        //遍历byte数组,一个byte表示一个字符
        for (Byte b : bytes) {
            //将字符转为赫夫曼编码格式,一个字符对应8位编码
            stringBuilder.append(huffmanCodes.get(b));
        }

        //一个字符对应对应的8位的赫夫曼编码,如果赫夫曼编码无法被8整除,就直接补齐赫夫曼编码不足八位的那一个字符
        int len = stringBuilder.length() % 8 == 0 ? stringBuilder.length() / 8 : stringBuilder.length() / 8 + 1;
        //System.out.println("有几个字符:"+len);

        //将压缩后的赫夫曼编码按字符分开存储
        byte[] huffmanCodeBytes = new byte[len];
        //计录已处理几个字符
        int index = 0;
        //每8位编码对应一个byte,所以步长为8
        //每循环一次处理一个byte,也就是一个字符
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            String strBytes;
            //判断编码长度是否超过8位
            if (i + 8 < stringBuilder.length()) {
                //超过8位就从赫夫曼编码截取八位(也就是一个字符)
                strBytes = stringBuilder.substring(i, i + 8);
            }else {
                //否则就有多少截多少
                strBytes = stringBuilder.substring(i);
            }
            //将赫夫曼编码转为二进制,存入byte数组
            huffmanCodeBytes[index] = (byte) Integer.parseInt(strBytes, 2);

            //位已处理字符数+1
            index++;
        }

        //循环结束后,返回赫夫曼编码按字符转换得到的字节数组
        return huffmanCodeBytes;
    }
    public byte[] zip() {
        byte[] bytes = str.getBytes();
        Map<Byte, String> huffmanCodes = getCodes();
        return zip(bytes, huffmanCodes);
    }

    /**
     * 将byte转成二进制字符串
     * @param isComple 是否需要补高位。最后一个字节无需补位
     * @param b 要转换的字节
     * @return
     */
    private String byteToString(boolean isComplate, byte b) {
        int temp = b;
        //判断是否需要补齐高位
        if (isComplate) {
            temp |= 256;
        }
        //返回temp对应的二进制补码
        String str = Integer.toBinaryString(temp);
        return isComplate ? str.substring(str.length() - 8) : str;
    }

    /**
     * 解码
     * @param huffmanCodes 赫夫曼编码表
     * @param huffmanBytes 赫夫曼编码处理过的字节数组
     * @return 原来未被转为赫夫曼编码的的字符串字节素组
     */
    private byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {

        //将赫夫曼编码处理过byte数组转为二进制字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < huffmanBytes.length; i++) {

            boolean isComplate = true;
            //如果是最后一个字节就不用补高位了
            if (i == huffmanBytes.length - 1) {
                isComplate = false;
            }
            //拼接字节转的二进制字符串
            stringBuilder.append(byteToString(isComplate, huffmanBytes[i]));
        }

        //把字符串按照指定赫夫曼编码进行解码
        //原本赫夫曼编码表是<字节,二进制字符串>,现在要转为<二进制字符串,字节>以通过转换得到的二进制字符串取出对应的字节
        Map<String, Byte> reHuffmanCodes = new HashMap<>();
        for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
            reHuffmanCodes.put(entry.getValue(), entry.getKey());
        }

        List<Byte> bytes = new ArrayList<>();
        //由于无法确认拼接后的二进制字符串每八位一定就能和某个字节对应,所以需要进行字符串匹配
        //这里可以简单理解为双指针,一号指针从i开始,二号指针从i+1开始
        //一号指针先指向字符串第i字符,然后二号指针从i+1个字符开始不断后移,然后进行进行匹配
        //比如:i=0,j=1,第一次截取并匹配的字符就是[0,1),也就是0;第二次是[0,2),也就是01;然后是[0,3).....以此类推
        //直到找到以后,比如[2,7),就移动一号指针到7,二号指针移动到8
        for (int i = 0, j = 1; i < stringBuilder.length(); i = --j) {
            String key = "";
            while (!reHuffmanCodes.containsKey(key)) {
                key = stringBuilder.substring(i, j);
                j++;
            }
            bytes.add(reHuffmanCodes.get(key));
        }

        //由集合转为字节数组
        byte b[] = new byte[bytes.size()];
        for (int i = 0; i < b.length; i++) {
            b[i] = bytes.get(i);
        }

        return b;
    }

    public byte[] decode(byte[] huffmanBytes) {
        return decode(huffmanCodes, huffmanBytes);
    }

}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/170803.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • ERP和MES、QAS以及APS在制造企业信息化的了解

    ERP和MES、QAS以及APS在制造企业信息化的了解ERP系统企业ERP系统标准的定义来自其英文原意,即企业资源规划(EnterpiseResourcePlanning)。企业ERP系统是一个对企业资源进行有效共享与利用的系统,通过信息系统对信息进行充分整理、有效传递、使企业的资源在购、存、产、销、人、财、物等各个方面能够得到合理地配置与利用,从而实现企业经营效率地提高。从本质上讲,企业ERP系统时一套信息系统,是一种工具。系统设计中可集成某些管理思想与内容,可帮助企业提升管理水平。另外一种说法认为企业ERP系统是将企业所有资源进行整合集成管理,简单

    2022年6月28日
    31
  • socket常用函数_socket是可重入函数吗

    socket常用函数_socket是可重入函数吗前言socketpair是Linux下的函数,其主要作用是创建一对套节字来进行进程间通信,其与匿名管道(PIPE)的作用相似,这两个套节字均可读可写.具体介绍见本博客另一篇文章:https://blog.csdn.net/wufuhuai/article/details/79747912实现我们都知道socket不仅能够进行跨进程通信,而且socket是可以双向通信的,即是…

    2022年10月14日
    2
  • JAVA基本框架搭建(Maven,jetty,Joda-time,junit)

    JAVA基本框架搭建(Maven,jetty,Joda-time,junit)

    2021年5月11日
    122
  • AD域介绍

    AD域介绍域的背景介绍为什么要使用域?假设你是公司的系统管理员,你们公司有一千台电脑。如果你要为每台电脑设置登录帐户,设置权限(比如是否允许登录帐户安装软件),那你要分别坐在这一千台电脑前工作。如果你要做一些改变,你也要分别在这一千台电脑上修改。相信没有哪个管理员想要用这种不吃不喝不睡觉的方式来工作,所以就应运而生了域的概念。域(Domain):概念域模型就是针对大型网络的管理需求而设计的,域就是共享用户账号,计算机账号和安全策略的计算机集合。域的管理优点因为所有的用户信息都被集中存储,所以,域提供了集

    2022年5月17日
    106
  • 悲观锁、乐观锁的区别及使用场景

    悲观锁、乐观锁的区别及使用场景定义:悲观锁(Pessimistic Lock): 每次获取数据的时候,都会担心数据被修改,所以每次获取数据的时候都会进行加锁,确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁。由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待。乐观锁(Optimistic Lock): 每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候…

    2022年6月13日
    33
  • LeeCode(Database)-Employees Earning More Than Their Managers

    LeeCode(Database)-Employees Earning More Than Their Managers

    2021年9月8日
    65

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号