【188】Java8利用AVL树实现Map
AVL树又被叫做平衡二叉搜索树、平衡二叉树。AVL是其发明者的首字母缩写。
这篇文章中,AVLTreeMap 类集成了 java.util.Map 接口,并利用 AVL 树结构实现了 Map 接口的所有方法。本文还给出了测试代码。
为什么要发明AVL树?
当我按照从小到大或者从大到小的顺序向二叉查找树插入节点,二叉查找树就会退化成一个链表。这是二叉查找树的最差情况。搜索、插入、删除的最差效率都是 O(N)。这样就失去了用二叉查找树优化查找方法的意义。
就算不是最坏情况,也会出现非常不平衡的树,造成查找效率大于 O(logN) 小于 O(N) 。注意这里 logN 是以2为底N的对数。
AVL树的时间复杂度
因为含有数学公式,就使用图片了。
代码实现
AVLTreeMap.java
package zhangchao.avl;import java.util.*;/*** 利用AVL树,也就是平衡二叉树来实现map* @author zhangchao* @param <K> 键* @param <V> 值*/
public class AVLTreeMap<K,V> implements Map<K, V>{// 根节点private Node<K, V> root = null;private Comparator<K> comparator;public AVLTreeMap(Comparator<K> comparator) {this.comparator = comparator;}@Overridepublic int size() {if (null == root) {return 0;}int size = 0;// 当前层的节点列表List<Node<K, V>> currentLevel = new ArrayList<>();currentLevel.add(root);while (!currentLevel.isEmpty()) {size += currentLevel.size();// 下一层的节点列表List<Node<K, V>> nextLevel = new ArrayList<>();for (Node<K, V> tmpNode : currentLevel) {if (null != tmpNode.leftChild) {nextLevel.add(tmpNode.leftChild);}if (null != tmpNode.rightChild) {nextLevel.add(tmpNode.rightChild);}}currentLevel.clear();currentLevel.addAll(nextLevel);}return size;}@Overridepublic boolean isEmpty() {return (null == root);}@Overridepublic boolean containsKey(Object keyObj) {if (null == root) {return false;}K key = (K)keyObj;Node<K, V> current = this.root;while(null != current) {int compareResult = this.comparator.compare(key, current.key);if (compareResult < 0) {current = current.leftChild;} else if (compareResult > 0) {current = current.rightChild;} else {return true;}}return false;}@Overridepublic boolean containsValue(Object value) {if (null == this.root) {return false;}List<Node<K, V>> nodeList = this.nodeList();for (Node<K, V> node : nodeList) {if (null == value && null == node.value) {return true;}if (null != value && value.equals(node.value)) {return true;}}return false;}@Overridepublic V get(Object keyObj) {if (null == this.root) {return null;}K key = (K)keyObj;Node<K, V> current = this.root;while(null != current) {int compareResult = this.comparator.compare(key, current.key);if (compareResult < 0) {current = current.leftChild;} else if (compareResult > 0) {current = current.rightChild;} else {return current.value;}}return null;}/*** 右旋操作* @param parent 爷爷节点* @param y 父级节点* @param x 子级节点*/private void rotateRight(Node<K, V> parent, Node<K, V> y, Node<K, V> x) {y.leftChild = x.rightChild;x.rightChild = y;if (null == parent) {this.root = x;} else {// 判断原来的y是parent的左子节点还是右子节点。if (null != parent.leftChild && 0 == this.comparator.compare(parent.leftChild.key, y.key)) {parent.leftChild = x;} else if (null != parent.rightChild && 0 == this.comparator.compare(parent.rightChild.key, y.key)) {parent.rightChild = x;}}y.height = this.calHeight(y);x.height = this.calHeight(x);if (null != parent) {parent.height = this.calHeight(parent);}}/*** 左旋操作* @param parent 爷爷节点* @param y 父级节点* @param x 子级节点*/private void rotateLeft(Node<K, V> parent, Node<K, V> y, Node<K, V> x) {y.rightChild = x.leftChild;x.leftChild = y;if (null == parent) {this.root = x;} else {// 判断原来的y是parent的左子节点还是右子节点。if (null != parent.leftChild && 0 == this.comparator.compare(parent.leftChild.key, y.key)) {parent.leftChild = x;} else if (null != parent.rightChild && 0 == this.comparator.compare(parent.rightChild.key, y.key)) {parent.rightChild = x;}}y.height = this.calHeight(y);x.height = this.calHeight(x);if (null != parent) {parent.height = this.calHeight(parent);}}@Overridepublic V put(K key, V value) {if (null == this.root) {this.root = new Node<>();this.root.key = key;this.root.value = value;this.root.height = 1;return null;}// 如果key是全新的,保存所有的父亲节点。List<Node<K, V>> linkList = new ArrayList<>();// 如果key是全新的,这个变量是新节点的父亲节点。Node<K, V> parent = null;Node<K, V> current = root;int compareResult = 0;while (null != current) {compareResult = this.comparator.compare(key, current.key);if (compareResult < 0) {parent = current;linkList.add(parent);current = current.leftChild;} else if (compareResult > 0) {parent = current;linkList.add(parent);current = current.rightChild;} else {// 有相等的key,直接设置值就可以了。V oldValue = current.value;current.value = value;return oldValue;}}Node<K, V> newItem = new Node<K, V>();newItem.key = key;newItem.value = value;newItem.height = 1;if (compareResult < 0) {parent.leftChild = newItem;} else if (compareResult > 0) {parent.rightChild = newItem;}// 更新祖先节点的高度final int size = linkList.size();for (int i = size - 1; i >= 0; i--) {Node<K, V> item = linkList.get(i);item.height = calHeight(item);}linkList.add(newItem);int parentSize = linkList.size();for (int i = parentSize - 1; i >= 0; i--) {// 当前节点Node<K, V> z = linkList.get(i);// z的父节点,如果z是根节点,那么就是null。Node<K, V> z_parent = null;if (i > 0) {z_parent = linkList.get(i - 1);}int leftHeight = this.calHeight(z.leftChild);int rightHeight = this.calHeight(z.rightChild);int balance = leftHeight - rightHeight;if (balance > 1) { // LL 或 LRNode<K, V> y = z.leftChild;Node<K, V> x = linkList.get(i + 2);boolean isLL = (null != y.leftChild && this.comparator.compare(y.leftChild.key, x.key) == 0);boolean isLR = (null != y.rightChild && this.comparator.compare(y.rightChild.key, x.key) == 0);if (isLL) { // LL 右旋this.rotateRight(z_parent, z, y);}else if (isLR) { // LR// y和x之间左旋this.rotateLeft(z, y, x);// z和x之间右旋this.rotateRight(z_parent, z, x);}break; // 停止for循环} else if (balance < -1) { // RR 或 RLNode<K, V> y = z.rightChild;Node<K, V> x = linkList.get(i + 2);boolean isRR = (null != y.rightChild && this.comparator.compare(y.rightChild.key, x.key) == 0);boolean isRL = (null != y.leftChild && this.comparator.compare(y.leftChild.key, x.key) == 0);if (isRR) {this.rotateLeft(z_parent, z, y);} else if (isRL) {// y和x之间右旋this.rotateRight(z, y, x);// z和x之间左旋this.rotateLeft(z_parent, z, x);}break; // 停止for循环}}// 更新祖先节点高度for (int i = parentSize - 1; i >= 0; i--) {Node<K, V> item = linkList.get(i);item.height = calHeight(item);}return null;}private List<Node<K,V>> getNodeAndParent(K key, List<Node<K, V>> parents) {if (null == this.root) {return null;}Node<K, V> parent = null;Node<K, V> current = this.root;while(null != current) {int compareResult = this.comparator.compare(key, current.key);if (compareResult < 0) {parent = current;if (null != parents) {parents.add(parent);}current = current.leftChild;} else if (compareResult > 0) {parent = current;if (null != parents) {parents.add(parent);}current = current.rightChild;} else {List<Node<K, V>> result = new ArrayList<>();result.add(current);result.add(parent);return result;}}return null;}private K deleteAsBST(Node<K, V> node, Node<K, V> parent) {K endKey = null;// 叶子节点if (null == node.leftChild && null == node.rightChild) {if (node == parent.leftChild) {parent.leftChild = null;} else {parent.rightChild = null;}return parent.key;}// 左子节点为空,只有右子节点else if (null == node.leftChild && null != node.rightChild) {if (node == this.root) {this.root = node.rightChild;} else if (node == parent.leftChild) {parent.leftChild = node.rightChild;} else if (node == parent.rightChild) {parent.rightChild = node.rightChild;}endKey = node.rightChild.key;node.rightChild = null;return endKey;}// else 包含两种情况:// 1.左子节点不为空,右子为空// 2.左子节点不为空,右子不为空// 要删除的节点的左子树中,找出最大节点。Node<K, V> current = node.leftChild;Node<K, V> currentParent = node;while (null != current.rightChild) {currentParent = current;current = current.rightChild;}// 把current从原位置删除if (current == currentParent.leftChild) {currentParent.leftChild = current.leftChild;} else if (current == currentParent.rightChild) {currentParent.rightChild = current.leftChild;}// 让current取代node的位置if (node == this.root) {this.root = current;} else if (node == parent.leftChild) {parent.leftChild = current;} else {parent.rightChild = current;}current.leftChild = node.leftChild;current.rightChild = node.rightChild;node.leftChild = null;node.rightChild = null;if (null == current.leftChild) {return current.key;} else {Node<K, V> p1 = current.leftChild;while (null != p1.rightChild) {p1 = p1.rightChild;}return p1.key;}}@Overridepublic V remove(Object keyObj) {// 空map,不执行删除操作。if (null == this.root) {return null;}K key = (K)keyObj;// 只有根节点的情况if (null == this.root.leftChild && null == this.root.rightChild) {if (this.comparator.compare(key ,this.root.key) == 0) {V v = this.root.value;this.root = null;return v;} else {return null;}}// 不包含key就返回nullList<Node<K, V>> nodeAndParent = this.getNodeAndParent(key, new ArrayList<>());// map中没有对应的key,不执行删除操作。if (null == nodeAndParent || nodeAndParent.isEmpty()) {return null;}Node<K, V> node = nodeAndParent.get(0); // 要删除的节点V result = node.value;Node<K, V> parent = nodeAndParent.get(1); // 要删除的节点的父亲节点// 按照二叉搜索树(BST)的方式删除节点。返回结束节点的键。K endKey = this.deleteAsBST(node, parent);// 包含所有可能改动过高度的节点的列表。// 顺序是从根节点向下。// 替换了已删除节点位置的节点称为替换节点。// pathList的内容有以下三种情况:// 1. 叶子节点,pathList包含根节点到父节点。// 2. 没有左子节点,只有右子节点,pathList包含根节点到替换节点。// 3. 有左子节点,pathList包含根节点到替换节点,再加上替换节点到替换节点左子树最大节点。List<Node<K, V>> pathList = new ArrayList<>();List<Node<K,V>> endKeyResult = this.getNodeAndParent(endKey, pathList);pathList.add(endKeyResult.get(0));// 因为可能加入了节点,所以要重新计算 parents 的长度int size = pathList.size();for (int i = size - 1; i >= 0; i--) {Node<K, V> z_parent = i > 0 ? pathList.get(i - 1) : null;Node<K, V> z = pathList.get(i);// 更新高度z.height = this.calHeight(z);if (null != z_parent) {z_parent.height = this.calHeight(z_parent);}int leftHeight = calHeight(z.leftChild);int rightHeight = calHeight(z.rightChild);int balance = leftHeight - rightHeight;if (balance > 1) {Node<K, V> y = z.leftChild;Node<K, V> x = null;int y_leftHeight = calHeight(y.leftChild);int y_rightHeight = calHeight(y.rightChild);if (y_leftHeight >= y_rightHeight) {// LLx = y.leftChild;// z和y之间右旋this.rotateRight(z_parent, z, y);} else {// LRx = y.rightChild;// y和x之间左旋this.rotateLeft(z, y, x);// z和x之间右旋this.rotateRight(z_parent, z, x);}} else if (balance < -1) {Node<K, V> y = z.rightChild;Node<K, V> x = null;int y_leftHeight = calHeight(y.leftChild);int y_rightHeight = calHeight(y.rightChild);if (y_leftHeight >= y_rightHeight) {// RLx = y.leftChild;// y和x之间右旋this.rotateRight(z, y, x);// z和x之间左旋this.rotateLeft(z_parent, z, x);} else {// RRx = y.rightChild;// z和y之间左旋this.rotateLeft(z_parent, z, y);}}}return result;}// end public V remove(Object keyObj)@Overridepublic void putAll(Map<? extends K, ? extends V> m) {if (null == m) {return;}Set<? extends K> keySet = m.keySet();for (K key : keySet) {this.put(key, m.get(key));}}@Overridepublic void clear() {this.root = null;}private List<Node<K, V>> nodeList() {if (null == this.root) {return new ArrayList<Node<K, V>>();}List<Node<K, V>> result = new ArrayList<>();Stack<Node<K, V>> stack = new Stack<>();Node<K, V> current = this.root;while(null != current || !stack.isEmpty()) {while (null != current) {stack.push(current);current = current.leftChild;}current = stack.pop();// 放入结果列表中result.add(current);current = current.rightChild;}return result;}@Overridepublic Set<K> keySet() {List<Node<K, V>> nodeList = nodeList();Set<K> set = new TreeSet<>(this.comparator);for (Node<K, V> node : nodeList) {set.add(node.key);}return set;}@Overridepublic Collection<V> values() {List<Node<K, V>> nodeList = nodeList();List<V> result = new ArrayList<>();for (Node<K,V> node : nodeList) {result.add(node.value);}return result;}@Overridepublic Set<Entry<K, V>> entrySet() {List<Node<K, V>> nodeList = this.nodeList();Set<Entry<K, V>> set = new TreeSet<Entry<K, V>>((o1, o2) -> {Node<K, V> n1 = (Node<K, V>) o1;Node<K, V> n2 = (Node<K, V>) o2;return comparator.compare(n1.key, n2.key);});for (Node<K,V> node : nodeList) {set.add(node);}return set;}private int calHeightForCheck(Node<K,V> node) {if (null == node) {return 0;}int height = 0;List<Node<K,V>> currentLevel = new ArrayList<>();currentLevel.add(node);while (!currentLevel.isEmpty()) {height ++;List<Node<K,V>> nextLevel = new ArrayList<>();for (Node<K,V> tmpNode : currentLevel) {if (null != tmpNode.leftChild) {nextLevel.add(tmpNode.leftChild);}if (null != tmpNode.rightChild) {nextLevel.add(tmpNode.rightChild);}}currentLevel = nextLevel;}return height;}private void showTree(Node node, Node parent, int level, String prefix) {if (null == node) {return;}StringBuilder sb = new StringBuilder();for (int i = 0; i < level; i++) {sb.append(" ");}sb.append(prefix);sb.append(node.key).append(" ");if (parent != null) {sb.append(parent.key);}int balance = calHeightForCheck(node.leftChild) - calHeightForCheck(node.rightChild);sb.append(" ").append(balance);System.out.println(sb);level++;showTree(node.leftChild, node, level, "left : ");showTree(node.rightChild, node, level, "right: ");}/*** 打印树形结构。*/public void showTree() {if (null == root) {System.out.println("null");}showTree(root, null, 0, "root: ");}private void checkTree(Node node, Node parent, int level) {if (null == node) {return;}int balance = calHeightForCheck(node.leftChild) - calHeightForCheck(node.rightChild);if (balance < -1 || balance > 1) {throw new RuntimeException("balance < -1 || balance > 1");}level++;checkTree(node.leftChild, node, level);checkTree(node.rightChild, node, level);}/*** 检查树是不是符合AVL树的要求*/public void checkTree() {if (null == root) {return;}checkTree(root, null, 0);}/*** 以node为根节点,计算树的高度* @param node 根节点* @return 树的高度*/private int calHeight(Node<K,V> node) {if (null == node) {return 0;}int leftHeight = (null == node.leftChild) ? 0 : node.leftChild.height;int rightHeight = (null == node.rightChild) ? 0 : node.rightChild.height;return Math.max(leftHeight, rightHeight) + 1;}class Node<K,V> implements Entry<K,V> {K key = null;V value = null;int height;Node<K, V> leftChild;Node<K, V> rightChild;@Overridepublic K getKey() {return key;}@Overridepublic V getValue() {return value;}@Overridepublic V setValue(V tmpValue) {V oldValue = value;value = tmpValue;return oldValue;}public int getHeight() {return height;}}
}
测试代码 TestAVLTreeMap.java
这里面有和二叉查找树Map的对比。
二叉查找树Map的实现文章:https://blog.csdn.net/zhangchao19890805/article/details/128609922?spm=1001.2014.3001.5502
package zhangchao.avl.test;import zhangchao.avl.AVLTreeMap;import zhangchao.bst.BstTreeMap;import java.util.*;public class TestAVLTreeMap {public static void main(String[] args) {t7();}public static void t7() {int a[] = {20, 10, 21, 22, 5, 15, 1};Comparator<Integer> comparator = (o1, o2) ->{if (null == o1 && null == o2) {return 0;}if (null == o1 && null != o2) {return -1;}if (null != o1 && null == o2) {return 1;}return o1 - o2;};AVLTreeMap<Integer, String> avlTreeMap = new AVLTreeMap<>(comparator );for (int key : a) {avlTreeMap.put(key, "__" + key);}avlTreeMap.showTree();avlTreeMap.remove(20);System.out.println("\n");avlTreeMap.showTree();avlTreeMap.checkTree();}public static void t6() {Comparator<Integer> comparator = (o1, o2) ->{if (null == o1 && null == o2) {return 0;}if (null == o1 && null != o2) {return -1;}if (null != o1 && null == o2) {return 1;}return o1 - o2;};AVLTreeMap<Integer, String> avlTreeMap = new AVLTreeMap<>(comparator );BstTreeMap<Integer, String> bstTreeMap = new BstTreeMap<>(comparator);long t1;long t2;// 比对插入System.out.println("insert");Random r = new Random();final int MAX = 100000;List<Integer> list = new ArrayList<>();for (int i = 0; i < MAX; i++) {int key = r.nextInt(MAX);list.add(i);}t1 = System.currentTimeMillis();for (int key : list) {avlTreeMap.put(key, "__" + key);}t2 = System.currentTimeMillis();System.out.println("AVL:" + (t2 - t1));t1 = System.currentTimeMillis();for (int key : list) {bstTreeMap.put(key, "__" + key);}t2 = System.currentTimeMillis();System.out.println("BST:" + (t2 - t1));// 比对查询System.out.println("\nsearch");t1 = System.currentTimeMillis();for (int i = 0; i < MAX; i++) {avlTreeMap.get(i);}t2 = System.currentTimeMillis();System.out.println("AVL:" + (t2 - t1));t1 = System.currentTimeMillis();for (int i = 0; i < MAX; i++) {bstTreeMap.get(i);}t2 = System.currentTimeMillis();System.out.println("BST:" + (t2 - t1));
// avlTreeMap.showTree();// 比对删除System.out.println("\nremove");t1 = System.currentTimeMillis();Collections.shuffle(list);for (int key : list) {avlTreeMap.remove(key);}t2 = System.currentTimeMillis();System.out.println("AVL:" + (t2 - t1));t1 = System.currentTimeMillis();for (int key : list) {bstTreeMap.remove(key);}t2 = System.currentTimeMillis();System.out.println("BST:" + (t2 - t1));avlTreeMap.checkTree();}public static void t3() {Map<Integer, String> map = new AVLTreeMap<>( (o1, o2) ->{if (null == o1 && null == o2) {return 0;}if (null == o1 && null != o2) {return -1;}if (null != o1 && null == o2) {return 1;}return o1 - o2;});int[] arr = new int[]{20,10,21,5,15,22,13,16};for (int i : arr) {map.put(i, "__" + String.valueOf(i));}AVLTreeMap avlTreeMap = (AVLTreeMap) map;avlTreeMap.showTree();avlTreeMap.remove(10);System.out.println("\n");avlTreeMap.showTree();avlTreeMap.checkTree();}public static void t2() {Map<Integer, String> map = new AVLTreeMap<>( (o1, o2) ->{if (null == o1 && null == o2) {return 0;}if (null == o1 && null != o2) {return -1;}if (null != o1 && null == o2) {return 1;}return o1 - o2;});int[] arr = new int[]{8,3,6,1,2,98,2,6,150,170,160,7,52,28,75,14,40,86,10,21,46,25};for (int i : arr) {map.put(i, "__" + String.valueOf(i));}AVLTreeMap avlTreeMap = (AVLTreeMap) map;avlTreeMap.showTree();avlTreeMap.remove(7);System.out.println("\n\n\n");avlTreeMap.showTree();avlTreeMap.checkTree();}public static void t1() {Map<Integer, String> map = new AVLTreeMap<>( (o1, o2) ->{if (null == o1 && null == o2) {return 0;}if (null == o1 && null != o2) {return -1;}if (null != o1 && null == o2) {return 1;}return o1 - o2;});int[] arr = new int[]{8,3,6,1,2,98,2,6,150,170,160,7,52,28,75,14,40,86,10,21,46,25};for (int i : arr) {map.put(i, "__" + String.valueOf(i));}AVLTreeMap avlTreeMap = (AVLTreeMap) map;avlTreeMap.showTree();System.out.println(map.get(3));System.out.println(map.get(6));System.out.println(map.get(98));System.out.println(map.get(null));Set<Integer> set = avlTreeMap.keySet();for (Integer i : set) {System.out.println(i);}System.out.println();HashSet<Integer> hashSet = new HashSet<>();for (int i : arr) {hashSet.add(i);}for (int i : hashSet) {if (!set.contains(i)) {System.out.println(false);}}System.out.println(set.size() + " " + hashSet.size());System.out.println("containsKey 3: " + avlTreeMap.containsKey(3));System.out.println("containsKey 4: " + avlTreeMap.containsKey(4));System.out.println("containsValue __3: " + avlTreeMap.containsValue("__3"));System.out.println("containsValue __4: " + avlTreeMap.containsValue("__4"));System.out.println();Set<Map.Entry<Integer, String>> entrySet = avlTreeMap.entrySet();for (Map.Entry<Integer, String> item : entrySet) {System.out.println(item.getKey() + ": " + item.getValue());}avlTreeMap.checkTree();}
}
相关文章:

【188】Java8利用AVL树实现Map
AVL树又被叫做平衡二叉搜索树、平衡二叉树。AVL是其发明者的首字母缩写。 这篇文章中,AVLTreeMap 类集成了 java.util.Map 接口,并利用 AVL 树结构实现了 Map 接口的所有方法。本文还给出了测试代码。 为什么要发明AVL树? 当我按照从小到大…...
[SQL挖掘机] - 右连接: right join
介绍: 右连接是一种多表连接方式,它以右侧的表为基础,并返回满足连接条件的匹配行以及右侧表中的所有行,即使左侧的表中没有匹配的行。右连接将右表的每一行与左表进行比较,并根据连接条件返回结果集。其实, 左连接和右连接原理一…...
bug篇之基于docker安装nacos(2.1.1)使用dubbo连接不上的问题
说明:首先我的nacos安装是2.1.1版本,请注意版本问题。另外启动时用dubbo的话必须先启动服务提供者再启动服务使用者,否则会报错,同时也必须开放三个端口:8848,9848,9849 java.lang.IllegalStat…...
【Python入门系列】第二十一篇:Python物联网和传感器应用
文章目录 前言一、Python在物联网和传感器应用中的优势二、连接传感器和设备三、读取传感器数据四、示例代码和讲解五、进一步处理和分析传感器数据六、更多应用示例1、温湿度监测系统2、智能家居系统 - 灯光控制 总结 前言 物联网和传感器在现代科技中扮演着重要的角色。物联…...

Python爬虫的urlib的学习(学习于b站尚硅谷)
目录 一、页面结构的介绍 1.学习目标 2.为什么要了解页面(html) 3. html中的标签(仅介绍了含表格、无序列表、有序列表、超链接) 4.本节的演示 二、Urllib 1.什么是互联网爬虫? 2.爬虫核心 3.爬虫…...
【MongoDB】--MongoDB聚合Aggregation
目录 一、前言二、聚合管道操作2.1、实际案例1(1)、案例--根据学生no,找到对应班级名称(2)、案例--这个班级有哪些学生和哪些老师在任课 2.2、实际案例2(1)、案例--主表和关联表都有条件限制,且分页返回 一、前言 聚合操作组值来自多个文档,…...

Hadoop学习指南:探索大数据时代的重要组成——Hadoop概述
前言 在当今大数据时代,处理海量数据成为了一项关键任务。Hadoop作为一种开源的分布式计算框架,为大规模数据处理和存储提供了强大的解决方案。本文将介绍Hadoop的组成和其在大数据处理中的重要作用,让我们一同踏上学习Hadoop的旅程。 Hado…...

Java实现简单小画板
Java制作简单画板,包括两个类,一个主要画板类Drawpad,一个画板监听器DrawListener类。 1、Drawpad类,包括画板,画板功能设计,保存图片等 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 2…...

B078-项目实战--支付模块 领养订单支付流程
目录 支付模块需求分析表设计支付单表支付宝账号信息表-商家账号微信支付账号信息表-商家账号银行账号表-商家资金账号表支付流水表 流程分析支付基础模块继承加密算法沙箱环境准备支付宝支付-流程分析根据demo封装工具类导入依赖AlipayConfigAlipayInfoAlipayUtil 内网穿透 领…...

[css]margin-top不起作用问题(外边距合并)
在初学css时,会遇到突然间margin-top不起作用的情况。如下面: 情况一: 代码: <html> <head><style type"text/css"> * {margin:0;padding:0;border:0; }#outer {width:300px;height:300px;backgroun…...

Vue2基础八、插槽
零、文章目录 Vue2基础八、插槽 1、插槽 (1)默认插槽 作用:让组件内部的一些 结构 支持 自定义需求: 将需要多次显示的对话框, 封装成一个组件问题:组件的内容部分,不希望写死,希望能使用的时候自定义。…...
自然语言处理从入门到应用——LangChain:提示(Prompts)-[提示模板:连接到特征存储]
分类目录:《自然语言处理从入门到应用》总目录 特征存储是传统机器学习中的一个概念,它确保输入模型的数据是最新和相关的。在考虑将LLM应用程序投入生产时,这个概念非常重要。为了个性化LLM应用程序,我们可能希望将LLM与特定用户…...

jenkins自定义邮件发送人姓名
jenkins发送邮件的时候发送人姓名默认的,如果要自定义发件人姓名,只需要修改如下信息即可: 系统管理-system-Jenkins Location下的系统管理员邮件地址 格式为:自定义姓名<邮件地址>...

SolidWorks二次开发---简单的连接solidworks
创建一个.net Framework的应用,正常4.0以上就可以了。 打开nuget包管理 在里面搜索paine 在版中选择对应的solidworks年份开头的,进行安装。 安装完之后 : 同时选中下面两个dll,把嵌入操作类型改为false 然后在按钮的单击事件中输入: Connect.Crea…...
docker 安装 active Mq
在安装完Docker的机器上,安装activeMQ。 拉取镜像: docker pull webcenter/activemq 查看镜像: docker images Docker运行ActiveMQ镜像 docker run --name activemq -d -p 8161:8161 -p 61616:61616 --privilegedtrue --restartalways …...

【Linux】TCP协议
🌠 作者:阿亮joy. 🎆专栏:《学会Linux》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 目录 👉TCP协议&…...

DevOps系列文章之 自动化测试大全(单测和集成测试)
自动化测试业界主流工具 核心目标: 主要是功能测试和覆盖率测试 业界常用主流工具 GoogleTest GoogleTest是一个跨平台的(Liunx、Mac OS X、Windows 、Cygwin 、Windows CE and Symbian ) C单元测试框架,由google公司发布,为在不同平台上为编…...

Android启动速度优化
本节主要内容:了解APP启动流程、启动状态、查看启动时间、CPU Profile定位启动耗时代码、StrictMode严苛模式检测不合理写法、解决启动黑白屏问题。 一、APP启动流程 ①用户点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startAc…...
linux 日志 系统安全日志 web日志
web日志 LINUX日志系统之WEB日志(一)_dracut.log_麻子来了的博客-CSDN博客 系统安全日志 Linux系统安全日志详解_sinolover的博客-CSDN博客 wtmp和utmp文件都是二进制文件,需使用who、w、users、last和ac来操作这两个文件。 who /var/lo…...
SpringBoot 整合 MongoDB 连接 阿里云MongoDB
注:spring-boot-starter-data-mongodb 2.7.5;jdk 1.8 阿里云MongoDB是副本集实例的 在网上查找了一番,大多数都是教连接本地mongodb或者linux上的mongodb 阿里云上有java版连接教程,但它不是SpringBoot方法配置的,是手…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...

【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...