一.Huffman树

定义:  给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径达到最小,这样的二叉树称为最优二叉树,也称为霍夫曼树(Huffman树).

特点:     Huffman树是带权路径长度最短的树,权值较大的节点离根节点较近

    权值 = 当前节点的值 * 层数,wpl最小的值,就是Huffman树

创建步骤  举例  {13,7,8,3,29,6,1}

    1.从小到大进行排序,将每一个数据视为一个节点,每一个节点都可视为一个二叉树

    2.取出根节点权值两个最小的二叉树

    3.组成一个新的二叉树,新的二叉树根节点的权值是前面两颗二叉树节点权值之和

    4.再将这颗二叉树以根节点的权值大小进行再排序,不断重复1,2,3,4步,直到所有的数据都被处理,就得到一个Huffman树

  1. class Node implements Comparable<Node> {
  2. // 实现Comparable接口,可以使用Collections工具类进行排序
  3. public int value;
  4. public Node left;
  5. public Node right;
  6.  
  7. public Node(int value) {
  8. this.value = value;
  9. }
  10.  
  11. public Node() {
  12. }
  13.  
  14. /*用于测试Huffman树是否正确*/
  15. public void preOrder(){
  16. System.out.println(this);
  17. if (this.left != null){
  18. this.left.preOrder();
  19. }
  20. if (this.right != null){
  21. this.right.preOrder();
  22. }
  23. }
  24.  
  25. @Override
  26. public String toString() {
  27. return "Node{" +
  28. "value=" + value +
  29. '}';
  30. }
  31.  
  32. @Override
  33. public int compareTo(Node o) { // 从小到大进行排序
  34. return this.value - o.value;
  35. }
  36. }
  1. /**
  2. * 创建霍夫曼树
  3. * @param array 原数组
  4. * @return 创建好Huffman树的root节点
  5. */
  6. public static Node createHuffmanTree(int[] array){
  7. if (array.length == 0 || array == null){
  8. System.out.println("数组为空,无法创建");
  9. return null;
  10. }
  11. /*遍历数组中的每一个元素,构造成Node,放入到List中*/
  12. List<Node> nodes = new ArrayList<>();
  13. for (int item : array) {
  14. nodes.add(new Node(item));
  15. }
  16.  
  17. while (nodes.size() > 1){ /*只要List中有元素,就一直进行权值计算*/
  18. /*对Node进行排序*/
  19. Collections.sort(nodes);
  20.  
  21. /*取出根节点两个权值最小的二叉树*/
  22. Node leftNode = nodes.get(0);
  23. Node rightNode = nodes.get(1);
  24.  
  25. /*构建一个新的二叉树*/
  26. Node parent = new Node(leftNode.value + rightNode.value);
  27. parent.left = leftNode;
  28. parent.right = rightNode;
  29.  
  30. /*从List中删除使用过的节点*/
  31. nodes.remove(leftNode);
  32. nodes.remove(rightNode);
  33. /*将新的节点加入到List中*/
  34. nodes.add(parent);
  35. }
  36. /*返回Huffman树的root节点*/
  37. return nodes.get(0);
  38. }
  39. }

测试,如果生成的Huffman树是正确的,那么前序遍历的结果也是正确的

  1. public static void main(String[] args) {
  2. int[] array = {13,7,8,3,29,6,1};
  3. preOrder(createHuffmanTree(array));
  4. }
  5.  
  6. public static void preOrder(Node root){
  7. if (root != null){
  8. root.preOrder();
  9. }else {
  10. System.out.println("该树为空,不能遍历");
  11. return;
  12. }
  13. }

二.Huffman编码

定义:    Huffman编码是一种通信的编码,是在电通信领域的基本编码之一

作用:  Huffman编码广泛的应用于数据文件的压缩,而且它是前缀编码,可以有效的节省传输的带宽

编码的步骤:     举例  String content = 'i like like like java do you like a java oh oh oh';

  1.生成节点   

  1. /*定义节点,data用于存放数据,weight用于存放权值*/
  2. class HuffmanNode implements Comparable<HuffmanNode>{
  3. public Byte data;
  4. public int weight;
  5. public HuffmanNode left;
  6. public HuffmanNode right;
  7.  
  8. public HuffmanNode(Byte data, int weight) {
  9. this.data = data;
  10. this.weight = weight;
  11. }
  12.  
  13. public HuffmanNode() {
  14. }
  15.  
  16. @Override
  17. public int compareTo(HuffmanNode o) {
  18. return this.weight - o.weight;
  19. }
  20.  
  21. }

  2.统计字符串中每一个字符出现的次数

  1. /*统计字符串中每个字符出现的次数,放在List中进行返回*/
  2. /*List存储格式 [Node[date=97 ,weight = 5], Node[date=32,weight = 9]......]*/
  3. public static List<HuffmanNode> getNodes(byte[] bytes){
  4. if (bytes.length == 0 || bytes == null){
  5. System.out.println("字符串为空,无法进行编码");
  6. return null;
  7. }
  8. List<HuffmanNode> nodes = new ArrayList<>();
  9. Map<Byte,Integer> counts = new HashMap<>();
  10. /*遍历bytes ,统计每一个byte出现的次数*/
  11. for (byte item : bytes) {
  12. Integer count = counts.get(item);
  13. if (count == null){ // Map中没有这个字符,说明是第一次
  14. counts.put(item,1);
  15. }else {
  16. counts.put(item,count+1);
  17. }
  18. }
  19. /*遍历Map,将键值对转换为Node对象进行存放到List中*/
  20. for (Map.Entry<Byte,Integer> node:counts.entrySet()){
  21. nodes.add(new HuffmanNode(node.getKey(),node.getValue()));
  22. }
  23. return nodes;
  24. }

  3.根据List集合,创建Huffm树

  1. public static HuffmanNode createHuffmanTree(List<HuffmanNode> nodes){
  2. if (nodes.size() == 0 || nodes == null){
  3. System.out.println("生成的List为空,不能生成霍夫曼树");
  4. return null;
  5. }
  6. while (nodes.size() > 1){
  7. Collections.sort(nodes);
  8.  
  9. HuffmanNode leftNode = nodes.get(0);
  10. HuffmanNode rightNode = nodes.get(1);
  11. HuffmanNode parent = new HuffmanNode(null,leftNode.weight+rightNode.weight);
  12.  
  13. parent.left = leftNode;
  14. parent.right = rightNode;
  15.  
  16. nodes.remove(leftNode);
  17. nodes.remove(rightNode);
  18. nodes.add(parent);
  19.  
  20. }
  21. return nodes.get(0);
  22. }

  4.将传入的Huffman树进行Huffman编码

  1. /*将传入所有节点的Node节点的Huffman编码得到*/
  2. /*node 传入的节点*/
  3. /*code 路径,向左为0,向右为1*/
  4. /*StringBuild 用于拼接路径,生成编码*/
  5. public static void getCode(HuffmanNode node,String code,StringBuilder stringBuilder){
  6. StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
  7. /*将code加入到stringBuilder2 中*/
  8. stringBuilder2.append(code);
  9. if (node!=null){ // 如果node是null,则不进行处理
  10. if (node.data == null){ // 是非叶子节点
  11. //向左递归
  12. getCode(node.left,"0",stringBuilder2);
  13. //向右递归
  14. getCode(node.right,"1",stringBuilder2);
  15. }else { /*此时表明是叶子结点,说明找到了一条路径的最后*/
  16. huffmanCode.put(node.data,stringBuilder2.toString());
  17. }
  18. }
  19. }
  20.  
  21. /*方便调用,重载此方法*/
  22. public static Map<Byte,String> getCode(HuffmanNode root){
  23. if (root == null){
  24. System.out.println("没有生成霍夫曼树");
  25. return null;
  26. }else {
  27. /*处理root左子树*/
  28. getCode(root.left,"0",stringBuilder);
  29. /*处理root右子树*/
  30. getCode(root.right,"1",stringBuilder);
  31. }
  32. return huffmanCode;
  33. }

  5.使用Huffman编码进行压缩

  1. /*将字符串对应的byte数组,通过生成的Huffman编码表,返回一个Huffman编码压缩后的byte数组*/
  2. /*bytes 原始字符串对应的字节数组*/
  3. /*huffmanCode 生成的Huffman编码表*/
  4. /* 返回Huffman编码处理后的字节数组*/
  5. public static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCode){
  6. if (bytes.length == 0 || bytes == null){
  7. System.out.println("字符串为空,无法进行编码");
  8. return null;
  9. }
  10. /*1.根据HuffmanCode获取原始的字节数组的二进制的字符串*/
  11. StringBuilder stb = new StringBuilder();
  12. for (byte b : bytes) {
  13. stb.append(huffmanCode.get(b));
  14. }
  15. /*2.创建存储压缩后的字节数组*/
  16. int index = 0; //记录第几个byte
  17. int len = 0; // 确定霍夫曼编码的长度
  18. if (stb.length() % 8 == 0){
  19. len = stb.length() / 8;
  20. }else {
  21. len = stb.length() / 8 + 1;
  22. }
  23. byte[] huffmanCodeBytes = new byte[len];
  24. /*每8位对应一个byte,所以步长+8*/
  25. for (int i = 0; i < stb.length();i+=8){
  26. String strByte = null;
  27. if (i+8 > stb.length()){ // 不够8位,直接从当前截取到末尾
  28. strByte = stb.substring(i);
  29. }else {
  30. strByte = stb.substring(i,i+8); //否则按照每8位进行拼接
  31. }
  32. /*将strByte 转换成一个个byte,放在要返回的字节数组中,进行返回*/
  33. huffmanCodeBytes[index++] = (byte)Integer.parseInt(strByte,2);
  34. }
  35. return huffmanCodeBytes;
  36. }

查看编码的结果:   压缩率   (49-21) / 49 = 57.14%

压缩之后的字节数组是:

将上述Huffman编码的步骤封装

  1. public static byte[] getZip(byte[] bytes){
  2. List<HuffmanNode> nodes = getNodes(bytes);
  3. // 根据nodes创建赫夫曼树
  4. HuffmanNode root = createHuffmanTree(nodes);
  5. // 根据root节点生成霍夫曼编码
  6. huffmanCode = getCode(root);
  7. // 根据霍夫曼编码,对数据进行压缩,得到字节数组
  8. byte[] huffmanCodeBytes = zip(bytes,huffmanCode);
  9. // System.out.println(Arrays.toString(huffmanCodeBytes));
  10. return huffmanCodeBytes;
  11. }

三.使用Huffman进行解码

  1.将一个二进制的byte,装换为二进制的字符串

  1. /**
  2. * 将一个byte转换成二进制的字符串
  3. * @param flag 表示是否要进行补高位,如果是true则需要补高位,false则不需要补位,如果是最后一个字节不需要补高位
  4. * @param b
  5. * @return 是该byte对应的二进制字符串(补码返回)
  6. */
  7. public static String byteToBitString(boolean flag,byte b){
  8. int temp = b; /* 使用临时变量,将byte转换为int*/
  9. if (flag){ /*如果是一个正数,需要进行补位操作*/
  10. temp |= 256; /*按位与操作*/
  11. }
  12. String str = Integer.toBinaryString(temp); /*返回temp对应的二进制补码*/
  13. if (flag){ // 如果有8位,则按照8位来返回,否则直接返回字符串
  14. return str.substring(str.length()-8);
  15. }else {
  16. return str;
  17. }
  18. }

  2.解码操作

  1. /**
  2. *
  3. * @param huffmanCode 对应霍夫曼编码表
  4. * @param huffmanBytes 霍夫曼编码得到的字节数组
  5. * @return 原先字符串对应的字节数组
  6. */
  7. public static byte[] decode(Map<Byte,String> huffmanCode,byte[] huffmanBytes){
  8. /*1.先得到HuffmanBytes对应的二进制的字符串*/
  9. StringBuilder sbt = new StringBuilder();
  10. //将byte字节转换为二进制字符串
  11. for (int i = 0; i < huffmanBytes.length; i++) {
  12. byte b = huffmanBytes[i];
  13. // 判断是否是最后一个字节
  14. boolean flag = (i == huffmanBytes.length-1);
  15. sbt.append(byteToBitString(!flag,b));
  16. }
  17.  
  18. /*2.把字符串按照指定的方式进行霍夫曼解码*/
  19. /*把Huffman码表进行调换,因为是反向查询*/
  20. Map<String,Byte> map = new HashMap<>();
  21. for (Map.Entry<Byte,String> entry:huffmanCode.entrySet()){
  22. map.put(entry.getValue(),entry.getKey());
  23. }
  24.  
  25. /*3.创建集合,存放解码后的byte*/
  26. List<Byte> byteList = new ArrayList<>();
  27. /*使用索引不停的扫描stb*/
  28. for (int k = 0; k < sbt.length();){
  29. int count = 1; /*小的计数器,用于判断是否字符串是否在Huffman的码标中*/
  30. Byte b = null; /*用于存放编码后的字节*/
  31. boolean loop = true;
  32. while (loop){
  33. /*k不动,让count进行移动,指定匹配到一个字符*/
  34. String key = sbt.substring(k,k+count);
  35. b = map.get(key);
  36. if (b == null){ //没有匹配到
  37. count++;
  38. }else {
  39. //匹配到就退出循环
  40. loop = false;
  41. }
  42. }
  43. byteList.add(b);
  44. k += count; //k直接移动到count在进行下一次遍历
  45. }
  46.  
  47. /*4.当for循环结束后,将list中存放的数据放入到byte数组中返回即可*/
  48. byte[] decodeByteCodes = new byte[byteList.size()];
  49. for (int j = 0; j < decodeByteCodes.length; j++) {
  50. decodeByteCodes[j] = byteList.get(j);
  51. }
  52. return decodeByteCodes;
  53. }

查看解码后的结果:

完整代码

  1. package data.structer.tree;
  2.  
  3. import java.util.*;
  4.  
  5. public class HuffmanCodeDemo {
  6.  
  7. static Map<Byte,String> huffmanCode = new HashMap<>();
  8. static StringBuilder stringBuilder = new StringBuilder();
  9.  
  10. public static void main(String[] args) {
  11. String content = "i like like like java do you like a java oh oh oh";
  12. //System.out.println("原始的长度是:"+content.length());
  13.  
  14. byte[] bytes = getZip(content.getBytes());
  15. // System.out.println("Huffman编码后的字符串长度是:"+bytes.length);
  16. System.out.println("解码后的字符串是:"+new String(decode(huffmanCode,bytes)));
  17. }
  18.  
  19. // 解码
  20.  
  21. /**
  22. *
  23. * @param huffmanCode 对应霍夫曼编码表
  24. * @param huffmanBytes 霍夫曼编码得到的字节数组
  25. * @return
  26. */
  27. public static byte[] decode(Map<Byte,String> huffmanCode,byte[] huffmanBytes){
  28. StringBuilder sbt = new StringBuilder();
  29. //将byte字节转换为二进制字符串
  30. for (int i = 0; i < huffmanBytes.length; i++) {
  31. byte b = huffmanBytes[i];
  32. // 判断是否是最后一个字节
  33. boolean flag = (i == huffmanBytes.length-1);
  34. sbt.append(byteToBitString(!flag,b));
  35. }
  36.  
  37. //把字符串按照指定的方式进行霍夫曼解码
  38. Map<String,Byte> map = new HashMap<>();
  39. for (Map.Entry<Byte,String> entry:huffmanCode.entrySet()){
  40. map.put(entry.getValue(),entry.getKey());
  41. }
  42.  
  43. // 创建集合,存放byte
  44. List<Byte> byteList = new ArrayList<>();
  45. for (int k = 0; k < sbt.length();){
  46. int count = 1;
  47. Byte b = null;
  48. boolean loop = true;
  49. while (loop){
  50. String key = sbt.substring(k,k+count);
  51. b = map.get(key);
  52. if (b == null){ //没有匹配到
  53. count++;
  54. }else {
  55. loop = false;
  56. }
  57. }
  58. byteList.add(b);
  59. k += count;
  60. }
  61.  
  62. byte[] decodeByteCodes = new byte[byteList.size()];
  63. for (int j = 0; j < decodeByteCodes.length; j++) {
  64. decodeByteCodes[j] = byteList.get(j);
  65. }
  66. return decodeByteCodes;
  67. }
  68.  
  69. /**
  70. * 将一个byte转换成二进制的字符串
  71. * @param flag 表示是否要进行补高位,如果是true则需要补高位,false则不需要补位,如果是最后一个字节不需要补高位
  72. * @param b
  73. * @return 是该byte对应的二进制字符串(补码返回)
  74. */
  75. public static String byteToBitString(boolean flag,byte b){
  76. int temp = b;
  77. if (flag){
  78. temp |= 256;
  79. }
  80. String str = Integer.toBinaryString(temp);
  81. if (flag){
  82. return str.substring(str.length()-8);
  83. }else {
  84. return str;
  85. }
  86. }
  87.  
  88. public static byte[] getZip(byte[] bytes){
  89. List<HuffmanNode> nodes = getNodes(bytes);
  90. // 根据nodes创建赫夫曼树
  91. HuffmanNode root = createHuffmanTree(nodes);
  92. // 根据root节点生成霍夫曼编码
  93. huffmanCode = getCode(root);
  94. // 根据霍夫曼编码,对数据进行压缩,得到字节数组
  95. byte[] huffmanCodeBytes = zip(bytes,huffmanCode);
  96. // System.out.println(Arrays.toString(huffmanCodeBytes));
  97. return huffmanCodeBytes;
  98. }
  99.  
  100. /**
  101. * 统计字符串中每个字符出现的次数,添加到List中进行返回
  102. * @param bytes
  103. * @return
  104. */
  105. public static List<HuffmanNode> getNodes(byte[] bytes){
  106. if (bytes.length == 0 || bytes == null){
  107. System.out.println("字符串为空,无法进行编码");
  108. return null;
  109. }
  110. List<HuffmanNode> nodes = new ArrayList<>();
  111. Map<Byte,Integer> counts = new HashMap<>();
  112.  
  113. for (byte item : bytes) {
  114. Integer count = counts.get(item);
  115. if (count == null){ // 说明是第一次
  116. counts.put(item,1);
  117. }else {
  118. counts.put(item,count+1);
  119. }
  120. }
  121.  
  122. for (Map.Entry<Byte,Integer> node:counts.entrySet()){
  123. nodes.add(new HuffmanNode(node.getKey(),node.getValue()));
  124. }
  125. return nodes;
  126. }
  127.  
  128. /**
  129. * 使用霍夫曼编码进行压缩
  130. * @param bytes
  131. * @param huffmanCode
  132. * @return
  133. */
  134. public static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCode){
  135. if (bytes.length == 0 || bytes == null){
  136. System.out.println("字符串为空,无法进行编码");
  137. return null;
  138. }
  139. StringBuilder stb = new StringBuilder();
  140. for (byte b : bytes) {
  141. stb.append(huffmanCode.get(b));
  142. }
  143. int index = 0;
  144. int len = 0; // 确定霍夫曼编码的长度
  145. if (stb.length() % 8 == 0){
  146. len = stb.length() / 8;
  147. }else {
  148. len = stb.length() / 8 + 1;
  149. }
  150. byte[] huffmanCodeBytes = new byte[len];
  151.  
  152. for (int i = 0; i < stb.length();i+=8){
  153. String strByte = null;
  154. if (i+8 > stb.length()){ // 不够8位
  155. strByte = stb.substring(i);
  156. }else {
  157. strByte = stb.substring(i,i+8);
  158. }
  159. huffmanCodeBytes[index] = (byte)Integer.parseInt(strByte,2);
  160. index++;
  161. }
  162. return huffmanCodeBytes;
  163. }
  164.  
  165. public static void getCode(HuffmanNode node,String code,StringBuilder stringBuilder){
  166. StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
  167. stringBuilder2.append(code);
  168. if (node!=null){
  169. if (node.data == null){ // 是非叶子节点
  170. //向左递归
  171. getCode(node.left,"0",stringBuilder2);
  172. //向右递归
  173. getCode(node.right,"1",stringBuilder2);
  174. }else {
  175. huffmanCode.put(node.data,stringBuilder2.toString());
  176. }
  177. }
  178. }
  179.  
  180. public static Map<Byte,String> getCode(HuffmanNode root){
  181. if (root == null){
  182. System.out.println("没有生成霍夫曼树");
  183. return null;
  184. }else {
  185. getCode(root.left,"0",stringBuilder);
  186. getCode(root.right,"1",stringBuilder);
  187. }
  188. return huffmanCode;
  189. }
  190.  
  191. /**
  192. * 生成霍夫曼树
  193. * @param nodes
  194. * @return
  195. */
  196. public static HuffmanNode createHuffmanTree(List<HuffmanNode> nodes){
  197. if (nodes.size() == 0 || nodes == null){
  198. System.out.println("生成的List为空,不能生成霍夫曼树");
  199. return null;
  200. }
  201. while (nodes.size() > 1){
  202. Collections.sort(nodes);
  203.  
  204. HuffmanNode leftNode = nodes.get(0);
  205. HuffmanNode rightNode = nodes.get(1);
  206. HuffmanNode parent = new HuffmanNode(null,leftNode.weight+rightNode.weight);
  207.  
  208. parent.left = leftNode;
  209. parent.right = rightNode;
  210.  
  211. nodes.remove(leftNode);
  212. nodes.remove(rightNode);
  213. nodes.add(parent);
  214.  
  215. }
  216. return nodes.get(0);
  217. }
  218. }
  219.  
  220. class HuffmanNode implements Comparable<HuffmanNode>{
  221. public Byte data;
  222. public int weight;
  223. public HuffmanNode left;
  224. public HuffmanNode right;
  225.  
  226. public HuffmanNode(Byte data, int weight) {
  227. this.data = data;
  228. this.weight = weight;
  229. }
  230.  
  231. public HuffmanNode() {
  232. }
  233.  
  234. @Override
  235. public String toString() {
  236. return "HuffmanNode{" +
  237. "data=" + data +
  238. ", weight=" + weight +
  239. '}';
  240. }
  241.  
  242. @Override
  243. public int compareTo(HuffmanNode o) {
  244. return this.weight - o.weight;
  245. }
  246.  
  247. }

Huffman编码和解码的更多相关文章

  1. DS二叉树--Huffman编码与解码

    题目描述 1.问题描述 给定n个字符及其对应的权值,构造Huffman树,并进行huffman编码和译(解)码. 构造Huffman树时,要求左子树根的权值小于.等于右子树根的权值. 进行Huffma ...

  2. 用C++实现Huffman文件编码和解码(2 总结)

    这个是代码是昨天写完的,一开始的时候还出了点小bug,这个bug在晚上去吃饭的路上想明白的,回来更改之后运行立刻完成最后一步,大获成功. 简单说下huffman编码和文件压缩主要的技术. Huffma ...

  3. Huffman 编码压缩算法

    前两天发布那个rsync算法后,想看看数据压缩的算法,知道一个经典的压缩算法Huffman算法.相信大家应该听说过 David Huffman 和他的压缩算法—— Huffman Code,一种通过字 ...

  4. [转载]Huffman编码压缩算法

    转自http://coolshell.cn/articles/7459.html 前两天发布那个rsync算法后,想看看数据压缩的算法,知道一个经典的压缩算法Huffman算法.相信大家应该听说过 D ...

  5. [老文章搬家] 关于 Huffman 编码

    按:去年接手一个项目,涉及到一个一个叫做Mxpeg的非主流视频编码格式,编解码器是厂商以源代码形式提供的,但是可能代码写的不算健壮,以至于我们tcp直连设备很正常,但是经过一个UDP数据分发服务器之后 ...

  6. Jcompress: 一款基于huffman编码和最小堆的压缩、解压缩小程序

    前言 最近基于huffman编码和最小堆排序算法实现了一个压缩.解压缩的小程序.其源代码已经上传到github上面: Jcompress下载地址 .在本人的github上面有一个叫Utility的re ...

  7. Huffman编码实现文件的压缩与解压缩。

    以前没事的时候写的,c++写的,原理很简单,代码如下: #include <cstdio> #include <cstdlib> #include <iostream&g ...

  8. Huffman编码实现压缩解压缩

    这是我们的课程中布置的作业.找一些资料将作业完毕,顺便将其写到博客,以后看起来也方便. 原理介绍 什么是Huffman压缩 Huffman( 哈夫曼 ) 算法在上世纪五十年代初提出来了,它是一种无损压 ...

  9. java编码原理,java编码和解码问题

    java的编码方式原理 java的JVM的缺省编码方式由系统的“本地语言环境”设置确定,和操作系统的类型无关 . 在JAVA源文件-->JAVAC-->Class-->Java--& ...

随机推荐

  1. 「Vijos 1282」「OIBH杯NOIP2006第二次模拟赛」佳佳的魔法照片

    佳佳的魔法照片 背景 佳佳的魔法照片(Magic Photo):如果你看过<哈利·波特>,你就会知道魔法世界里的照片是很神奇的.也许是因为小魔法师佳佳长的太帅,很多人都找他要那种神奇的魔法 ...

  2. docker-网桥

    使用桥接网络 在网络方面,桥接网络是链路层设备,它在网络段之间转发流量. 网桥可以是硬件设备或在主机内核中运行的软件设备. Docker而言,桥接网络使用软件桥接器,该软件桥接器允许连接到同一桥接网络 ...

  3. bootstrap:图片轮播

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name ...

  4. 为什么双击打开py文件时窗口瞬间关闭了?

    当前理解,py文件里没有input() 等暂停程序运行的函数,程序运行速度太快,运行完就立马关闭了. input()调用后,程序会立即暂停,等待用户输入

  5. 78.纯 CSS 创作 Windows 启动界面

    原文地址:https://segmentfault.com/a/1190000015632759 学习后效果地址:https://scrimba.com/c/cPgWmZCg HTML code: & ...

  6. webpack 三

    之前06年写了两篇自己研究webpack的文章,由于webpack已经升到了4.x,今天用起来发现有点小变化,重新自己建一个简单的项目,这里记录一下过程 1.安装webpack和webpack-cli ...

  7. Spring Boot2 系列教程 (十) | 实现声明式事务

    前言 如题,今天介绍 SpringBoot 的 声明式事务. Spring 的事务机制 所有的数据访问技术都有事务处理机制,这些技术提供了 API 用于开启事务.提交事务来完成数据操作,或者在发生错误 ...

  8. 矩形内的递推dp

    链接:https://www.nowcoder.com/acm/contest/130/B来源:牛客网 黑妹和黑弟又聚在一起玩游戏了,这次他们选择在一个n*m的棋盘上玩游戏,棋盘上的每个方格都有一个非 ...

  9. RabbitMQ入门(二)工作队列

      在文章RabbitMQ入门(一)之Hello World,我们编写程序通过指定的队列来发送和接受消息.在本文中,我们将会创建工作队列(Work Queue),通过多个workers来分配耗时任务. ...

  10. Java.前端模板.Thymleaf

    1. Input 日期格式化 <input id="renewalDate" name="renewalDate" th:value="${#d ...