一、概念

1. 为什么需要树这种数据结构

① 数组存储方式
优点:通过下标方式访问元素,速度快。对于有序数组可以通过二分查找提高检测速度。
缺点:如果要通过内容来查找元素的位置,或者插入删除值时,效率较低。
② 链式存储方式
优点:插入和删除操作效率较好。
缺点:查找效率较低,需要从头开始依次访问链表中的每个数据项。
③ 哈希表
优点:插入删除和查询效率都非常高
缺点:空间利用率不高,底层使用的是数组,并且某些单元没有被利用。哈希表中元素是无序的,不能按照固定的顺序来遍历哈希表中的元素。不能快速找出哈希表中的最大值和最小值。
④ 树
能提高数据存储,读取效率。比如利用二叉排序树,即可以保证数据的检索速度,同时也可以保证数据的插入,删除,修改的速度。

2.常用术语

叶子节点:没有子节点的节点
路径:从root节点找到该节点的路线
路径长度: 若规定根节点的层数为1,则从更节点到第L层节点的路径长度为L-1
:根节点所在第一层
树的高度:最大层数
二叉树:每个节点最多只能有两个子节点的一种形式称为二叉树
满二叉树:如果该二叉树所有叶子节点都在最后一层,并且节点总数为2的n此方-1,n为层数,则我们称为满二叉树。
完全二叉树:如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树。
:某个节点的子节点或直接后继节点的个数。1度就代表只有一个子节点。
总节点数:除了根节点其余节点都有一根线指向它,所以总节点树为(树的边数+1)。
叶子节点数: 总结点数-度数非零的节点数。对于二叉树,总结点树为 2n2 + n1 + 1,叶子节点树为 2n2 + n1 + 1 - n2 - n1为n2 + 1。
前驱节点:中序遍历下,一个节点的前一个节点
后继节点:中序遍历下,一个节点的后一个节点

3.遍历

前序遍历:先输出父节点,再遍历左子树和右子树。
中序遍历: 先遍历左子树,再输出父节点,再遍历右子树。
后序遍历:先遍历左子树,再遍历右子树,最后输出父节点。

4.顺序存储二叉树

数组存储方式和树存储方式可以相互转换,即数组可以转为树,树也可以转为数组。以下规律可以堆数组看成树进行前序,中序,后序遍历。用于堆排序。
① 顺序二叉树通常只考虑完全二叉树
② 第n个元素的左子节点为2n+1(n表示二叉树中的第几个元素,从0开始编号)
③ 第n个元素的右子节点为2
n+2
④ 第n个元素的父节点为(n-1)/2

5.线索化二叉树

(1)n个节点的二叉链表中含有n+1个空指针域(每多一个节点就会多两个指针域,少一个指针域。同时根节点并不会少一个指针域,所以为2n-n+1为n+1)。利用二叉链表中的空指针域,存放指向节点在某种遍历次序下的前驱和后继节点的指针(这种附加的指针称为“线索”)。
(2)这种加上了线索的二叉链表称为线索链表,对应的二叉树称为线索二叉树。根据线索性质的不同,线索二叉树可分为前序线索二叉树,中序线索二叉树和后序线索二叉树三种。
(3)当线索话二叉树后,node节点的属性left和righ有多种情况。left指向的可能是左子树,也可能是指向前驱节点。


二、堆排序

1.简介

(1) 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序;它的最坏,最好,平均时间复杂度均为O(nlogn);它是不稳定排序;
(2) 每个结点的值都大于或等于其左右子结点的值,称为大顶堆。

对堆中结点按顺序编号,映射到数组中就是如下图样子:

根据之前的顺序存储二叉树可以知道,大顶堆的特点为arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2];
(3) 每个结点的值都小于等于其左右子结点的值,称为小顶堆。

2.构造大顶堆思路

(1) 找到最后一个非叶子节点为(n-1)/2,然后比较该非叶子节点是否小于它的左右两个子节点,小于就进行交换。
(2) 进行循环,如果没交换就直接退出循环,如果交换了就用交换的子节点元素比较是否小于它的左右子节点,然后重复(2)的操作。
(3) 依次递归,找倒数第二个非叶子节点重复(1)(2)的操作,直到根节点。

3.堆排序基本思路

(1) 将待排序序列构造成一个大顶堆(根据需求如果是升序就构造为大顶堆,如果是降序就构造为小顶堆)。
(2) 此时,整个序列的最大值就是堆顶的根节点。
(3) 将其于末尾元素进行交换,此时末尾就为最大值。
(4) 然后将剩余n-1个元素重新构造称为一个大顶堆,然后交换根节点和末尾节点。如此反复执行,便得到一个有序序列了。

4.代码

    public static void main(String[] args) {int[] arr = new int[800];for (int i =0;i<800;i++){arr[i] = (int)(Math.random()*1000);}heapSort(arr);System.out.println(Arrays.toString(arr));}// 堆排序public static void heapSort(int arr[]) {// 把数组构建为大顶堆for (int i = arr.length/2-1; i >= 0; i--) {adjustHeap(arr,i,arr.length);}// 交换头尾节点值,然后将交换后的数组长度减一调整为大顶堆。for (int i = arr.length - 1;i >= 0;i--) {int temp = arr[0];arr[0] = arr[i];arr[i] = temp;adjustHeap(arr,0,i);}}/*** 将一个节点调整为大顶堆,前提是以该节点的左右子节点的子树必须是大顶堆* @param arr 数组* @param i 非叶子节点的索引* @param length 数组长度*/public static void adjustHeap(int[] arr,int i,int length) {int temp = arr[i];for (int k = 2*i + 1;k < length;k = 2*k + 1) {if (k+1 < length && arr[k+1] > arr[k]) {k++;}if (temp < arr[k]) {arr[i] = arr[k];i = k;} else {break;}}arr[i] = temp;}

三、HuffmanTree(霍夫曼树)

1.介绍

节点的权: 若将树中节点赋给一个有着某种含义的数值,则这个数值称为该节点的权。
节点的带权路径长度: 从根节点到该节点之间的路径长度与该节点的权的乘积。
树的带权路径长度: 树的带权路径长度规定为所有叶子节点的带权路径长度之和,记为WPL(weighted path length),权值越大的节点离根节点越近的二叉树才是最优二叉树。
HuffmanTree: 给定N个权值作为N个叶子结点,构造一棵二叉树,WPL最小的树就是霍夫曼树。

2.数组转赫夫曼树思路

(1) 从小到大进行排序,将每一个数据看成一个节点,每个节点可以看成是一颗最简单的二叉树。每个节点都是赫夫曼树的叶子节点。
(2) 取出根节点权值最小的两颗二叉树。
(3) 组成一颗新的二叉树,该节点是前面两颗二叉树根节点权值的和。
(4) 再将 这颗新的二叉树,以根节点的权值大小再次和数组其他元素排序,不断重复1-2-3-4的步骤,直到数据都被处理,得到一颗赫夫曼树。

3.数组转赫夫曼树代码实现

4.赫夫曼编码

赫夫曼编码是赫夫曼树在电讯通信中的经典应用之一。广泛用于数据文件压缩,压缩率通常在20%~90%之间。赫夫曼码是可变字长编码(VLC)的一种,与1952年提出,称之为最佳编码。
定长编码
字符 -> ascii码 -> 二进制
每一位字符转换成的二进制长度都是一样的,称为定长。
前缀编码
即字符的编码都不能是其他字符编码的前缀,不会造成匹配的多义性。
原理
① 将要传输的字符串各个字符对应的个数统计出来
② 按照每个字符出现的次数构建一颗赫夫曼树,次数作为权值
③ 根据赫夫曼树,从根节点开始,向左路径为0,向右路径为1。给各个字符规定编码。
④ 根据③得到的赫夫曼编码,将字符串转化为对应编码。
注意:赫夫曼树根据排序方法的不同,可能不太一样。但是wpl是一样的,都是最小的,最后生成的赫夫曼编码的长度是一样的。

字符串->字节数组->赫夫曼编码表->转为赫夫曼编码的二进制字符串->二进制字符串转字节

注意事项
① 如果文件本身就是经过压缩处理的,那么使用赫夫曼编码再压缩效率不会有明显变化,比如视频,ppt等文件。
② 赫夫曼编码是按字节来处理的,因此可以处理所有的文件(二进制文件,文本文件)。
③ 如果一个文件中的内容,重复的数据不多,压缩效果也不会很明显。


四、二叉排序树(BST二叉搜索树)

1.简介

BST(Binary Sort(Search) Tree),对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。(如果有相同的值,可以将该节点放在左子节点或右子节点)。它既有数组快速查找的优势,又有链表的快速插入与删除操作的特点。

2.创建和遍历代码

public class BinarySortTree {public Node root;public void add(Node node) {if (root == null) {root = node;} else {root.add(node);}}public void infixOrder() {if (root == null) {System.out.println("BST为空,不能遍历");} else {root.infixOrder();}}static class Node {public int value;public Node left;public Node right;public Node(int value) {this.value = value;}public void add(Node node) {if (node == null) {return;}if (this.value > node.value) {if (this.left != null) {this.left.add(node);} else {this.left = node;}} else {if (this.right != null) {this.right.add(node);} else {this.right = node;}}}public void infixOrder() {if (this.left != null) {this.left.infixOrder();}System.out.println(this);if (this.right != null) {this.right.infixOrder();}}@Overridepublic String toString() {return "Node{" +"value=" + value +'}';}}
}class BinarySortTreeDemo{public static void main(String[] args) {int[] arr = {7,3,10,12,5,1,9};BinarySortTree binarySortTree = new BinarySortTree();for (int i = 0;i < arr.length;i++) {binarySortTree.add(new BinarySortTree.Node(arr[i]));}binarySortTree.infixOrder();}
}

3. 删除节点原理

① 删除的是叶子节点
(1) 先找到要删除的结点 targetNode
(2) 找到 targetNode的父结点parent
(3) 确定targetNode是parent的左子节点,还是右子节点
(4) 根据前面的情况来对应删除,左子节点parent.left=null,右子节点parent.right=null
② 删除只有一颗子树的节点
(1) 先找到要删除的节点 targetNode
(2) 找到targetNode的父节点parent
(3) 确定targetNode的子节点是左子节点还是右子节点
(4) targetNode是parent的左子节点还是右子节点
(5) 如果targetNode有左子节点。如果targetNode是parent的左子节点,parent.left = targetNode.left;如果targetNode是parent的右子节点,parent.right = targetNode.left;
(6) 如果targetNode有右子节点。如果targetNode是parent的左子节点,parent.left = targetNode.right;如果targetNode是parent的右子节点,parent.right = targetNode.right;
③删除有两颗子树的节点
(1) 先找到要删除的节点 targetNode
(2) 找到targetNode的父节点parent
(3) 从targetNode的右子树找到最小的节点
(4) 用一个临时变量,将最小节点的值保存temp
(5) 删除该最小节点
(6) targetNode.value = temp

4. 删除节点代码

public class BinarySortTree {public Node root;public void add(Node node) {if (root == null) {root = node;} else {root.add(node);}}public void remove(int value){if (root == null) {return;}Node current = search(value);if (current == null) {return;}// 根节点是叶子节点的情况if (root.right == null && root.left == null) {root = null;return;}Node parent = searchParent(value);if (current.left == null && current.right == null) {if (parent.left != null && parent.left.value == value) {parent.left = null;} else if (parent.right != null && parent.right.value == value){parent.right = null;}} else if (current.left != null && current.right != null) {Node minNode = findMinNode(current.right);minNode.left = current.left;minNode.right = current.right;if (parent != null) {if (parent.left != null && parent.left.value == value) {parent.left = minNode;} else {parent.right = minNode;}} else {root = minNode;}} else {if (current.left != null) {if (parent != null) {if (parent.left != null && parent.left.value == value) {parent.left = current.left;} else {parent.right = current.left;}} else {root = current.left;}} else {if (parent != null) {if (parent.left != null && parent.left.value == value) {parent.left = current.right;} else {parent.right = current.right;}} else {root = current.right;}}}}public Node findMinNode(Node node) {Node target = node;while(target.left != null) {target = target.left;}remove(target.value);return target;}public void infixOrder() {if (root == null) {System.out.println("BST为空,不能遍历");} else {root.infixOrder();}}public Node search(int value) {Node node = root;while (node != null) {if (node.value == value) {return node;} else if (node.value > value) {node = node.left;} else {node = node.right;}}return null;}public Node searchParent(int value) {Node node = root;Node parent = null;while (node != null) {if (node.value == value) {return parent;} else if (node.value > value) {parent = node;node = node.left;} else {parent = node;node = node.right;}}return null;}static class Node {public int value;public Node left;public Node right;public Node(int value) {this.value = value;}public void add(Node node) {if (node == null) {return;}if (this.value > node.value) {if (this.left != null) {this.left.add(node);} else {this.left = node;}} else {if (this.right != null) {this.right.add(node);} else {this.right = node;}}}public void infixOrder() {if (this.left != null) {this.left.infixOrder();}System.out.println(this);if (this.right != null) {this.right.infixOrder();}}@Overridepublic String toString() {return "Node{" +"value=" + value +'}';}}
}class BinarySortTreeDemo{public static void main(String[] args) {int[] arr = {7,3,10,12,5,1,9,2};BinarySortTree binarySortTree = new BinarySortTree();for (int i = 0;i < arr.length;i++) {binarySortTree.add(new BinarySortTree.Node(arr[i]));}binarySortTree.infixOrder();binarySortTree.remove(1);binarySortTree.remove(10);binarySortTree.remove(9);binarySortTree.remove(12);binarySortTree.remove(3);binarySortTree.remove(7);binarySortTree.remove(5);binarySortTree.remove(2);System.out.println("-----------");binarySortTree.infixOrder();}
}

五、平衡二叉树(AVL)

1.介绍

AVL树是最早被发明的自平衡二叉查找树。在AVL树中,任一节点对应的两棵子树的最大高度差为1,因此它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是{O(log {n})}。增加和删除元素的操作则可能需要借由一次或多次树旋转,以实现树的重新平衡。
为什么要有avl
二叉搜索树一定程度上可以提高搜索效率,但是当原序列有序时,例如序列 A = {1,2,3,4,5,6},构造二叉搜索树如下图。依据此序列构造的二叉搜索树为右斜树,同时二叉树退化成单链表,搜索效率降低为 O(n)。

在此二叉搜索树中查找元素 6 需要查找 6 次。
二叉搜索树的查找效率取决于树的高度,因此保持树的高度最小,即可保证树的查找效率。同样的序列 A,将其改为下图的方式存储,查找元素 6 时只需比较 3 次,查找效率提升一倍。

可以看出当节点数目一定,保持树的左右两端保持平衡,树的查找效率最高。
这种左右子树的高度相差不超过 1 的树为平衡二叉树。

2.单旋转(左旋转)

原理

创建一个和当前根节点值相同的节点
把新节点的左子树设置为当前根节点的左子树
把新节点的右子树设置为当前当前根节点的右子树的左子树
把当前根节点的值设置为当前根节点右子节点的值
把当前根节点的右子树设置为当前根节点的右子树的右子树
把当前根节点的左子树设置为新节点

代码

        // 左旋转public void leftRotate() {AVLNode avlNode = new AVLNode(value);avlNode.left = left;avlNode.right = right.left;value = right.value;right = right.right;left = avlNode;}

3.单旋转(右旋转)

原理

创建一个和当前根节点值相同的新节点
把新节点的右子节点设置为当前节点的右子树
把新节点的左子节点设置 为当前节点的左子树的右子树
把当前根节点的值设置为当前根节点左子节点的值
把当前根节点的左子树设置为当前根节点左子树的左子树
把当前根节点的右子树设置为新节点

代码

    // 右旋转public void rightRotate() {AVLNode avlNode = new AVLNode(root.value);avlNode.right = root.right;avlNode.left = root.left.right;root.value = root.left.value;root.left = root.left.left;root.right = avlNode;}

4.双旋转

如下图树经过单旋转并不能完成平衡二叉树的转换,需要双旋转。

原理





代码

package com.ljf.system.tree;public class AVLTree {public AVLTree.AVLNode root;public void add(AVLTree.AVLNode node) {if (root == null) {root = node;} else {root.add(node);}if ( root.rightHeight() - root.leftHeight() > 1) {if (root.right != null && root.right.leftHeight() > root.right.rightHeight()) {root.right.rightRotate();root.leftRotate();} else {root.leftRotate();}return;}if (root.leftHeight() - root.rightHeight() > 1) {if (root.left != null && root.left.rightHeight() > root.left.leftHeight()) {root.left.leftRotate();root.rightRotate();} else {root.rightRotate();}}}public void infixOrder() {if (root == null) {System.out.println("BST为空,不能遍历");} else {root.infixOrder();}}static class AVLNode {public int value;public AVLTree.AVLNode left;public AVLTree.AVLNode right;public AVLNode(int value) {this.value = value;}public void add(AVLTree.AVLNode node) {if (node == null) {return;}if (this.value > node.value) {if (this.left != null) {this.left.add(node);} else {this.left = node;}} else {if (this.right != null) {this.right.add(node);} else {this.right = node;}}}public void infixOrder() {if (this.left != null) {this.left.infixOrder();}System.out.println(this);if (this.right != null) {this.right.infixOrder();}}// 左旋转public void leftRotate() {AVLNode avlNode = new AVLNode(value);avlNode.left = left;avlNode.right = right.left;value = right.value;right = right.right;left = avlNode;}// 右旋转public void rightRotate() {AVLNode avlNode = new AVLNode(value);avlNode.right = right;avlNode.left = left.right;value = left.value;left = left.left;right = avlNode;}/*** 返回以该节点为根节点的树的高度*/public int height() {return Math.max(left == null ? 0 : left.height(),right == null ? 0 : right.height()) + 1;}public int rightHeight() {if (right == null) {return 0;}return right.height();}public int leftHeight() {if (left == null) {return 0;}return left.height();}@Overridepublic String toString() {return "Node{" +"value=" + value +'}';}}
}class AVLTreeDemo{public static void main(String[] args) {int[] arr = {10,11,7,6,8,9};AVLTree avlTree = new AVLTree();for (int i = 0;i < arr.length;i++) {avlTree.add(new AVLTree.AVLNode(arr[i]));}avlTree.infixOrder();System.out.println("-----------");System.out.println(avlTree.root.height());System.out.println(avlTree.root.leftHeight());System.out.println(avlTree.root.rightHeight());}
}

六、多叉树

1.介绍

如果允许每个节点可以有更多的数据项和更多的子节点,就是多叉树。
2-3树,2-3-4树就是多叉树,多叉树通过重新组织节点,减少树的高度,能对二叉树进行优化。

2.2-3树介绍

2-3树是最简单的b-树结构,具有一下特点
2-3树所有叶子节点都在同一层(只要是b树都满足这个条件)。
有两个子节点的节点叫做二节点,二节点要么没有子节点,那么有两个子节点。
有三个子节点的节点 叫三节点,三节点要么没有子节点,要么有三个子节点。
2-3树是由二节点和三节点构成的树。
当按照规则插入一个数到某个节点时,不能满足上面要求时,就需要拆,先向上拆,如果上层满,则拆本层,拆后仍然要满足上面条件。
对于三节点的子树的值大小仍然遵守BST的规则。

3.b树(B-tree,B-树)介绍

特点

B树的阶:节点的最多子节点个数。比如2-3的阶是3,2-3-4树的阶是4。
B树的搜索,从根节点开始,对节点内的关键字序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子节点;重复直到所对应的儿子指针为空,或已经是叶子节点。
每个中间节点包含k-1个元素和k个孩子,其中m/2 <= k < = m;每个叶子节点都包含k个元素,其中m/2 <= k < = m。m/2向上取整。
搜索可能在非叶子节点结束。
所有叶子节点再同一层,叶子节点中不包含关键字信息,实际上这些节点不存在,指向这些节点的指针都为null;

4.b+树

特点

B+树所有关键字都出现在叶子节点的链表中。叶子节点的链表有序。
所有的非叶子节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素
有k个子树的非叶子节点包含k个元素(b树是k-1个元素),每个元素不保存数据,只用来做索引(稀疏索引)

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

5.b*树

特点

B*树定义了非叶子节点关键字个数至少为(2/3)M,即块的最低使用率为2/3,而B+树的块的最低使用率为1/2。
B
树分配新节点的概率比B+树要低,空间使用率更高。

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

数据结构和算法基础(4)——树相关推荐

  1. 【数据结构与算法基础】树与二叉树的互化

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

  2. 【数据结构与算法基础】树的应用

    写在前面 树这一数据结构学的差不多了,该拉出来练练了.本节学习几个树的应用,包括优先队列.Huffman编码等. 1.优先队列(Priority Queue) 优先队列是特殊的"队列&quo ...

  3. 【数据结构与算法基础】哈夫曼树与哈夫曼编码(C++)

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

  4. python数据结构与算法知识点_数据结构和算法基础知识点(示例代码)

    数据结构和算法基础知识点 链表 1.链表是一种由节点组成的线性数据集合,每个节点通过指针指向下一个节点.它是 一种由节点组成,并能用于表示序列的数据结构. 2.单链表:每个节点仅指向下一个节点,最后一 ...

  5. 数据结构与算法基础-青岛大学-王卓

    数据结构与算法基础(青岛大学-王卓)_哔哩哔哩_bilibili 文章目录: 第一章:数据结构的基本概念 1.逻辑结构的种类 2.存储结构的种类 ​3.抽象数据类型的形式定义 4.Complex抽象书 ...

  6. 【数据结构与算法基础】AOE网络与关键路径

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

  7. 【数据结构与算法基础】最短路径问题

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

  8. 数据结构与算法基础——重要知识点截图【青岛大学-王卓版】

    文章目录: 第一章:数据结构的基本概念 1.逻辑结构的种类 2.存储结构的种类 ​3.抽象数据类型的形式定义 4.Complex抽象书籍类型中的基本操作 5.概念小结 6.时间复杂度 7.空间复杂度 ...

  9. 数据结构与算法基础(青岛大学-王卓)(1)

    士别三日当刮目相待,不好意思鸽了好久了,因为学习的时间不连续,所以我一直攒着,我又回来继续更新了 没有继续学习浙大的数据结构了,对比了青岛大学的王老师的这个教程我觉得更适合我一些,更入门,更详细. 课 ...

  10. 数据结构与算法基础(java版)

    目录 数据结构与算法基础(java版) 1.1数据结构概述 1.2算法概述 2.1数组的基本使用 2.2 数组元素的添加 2.3数组元素的删除 2.4面向对象的数组 2.5查找算法之线性查找 2.6查 ...

最新文章

  1. Python机器学习:训练Tesseract
  2. 怎么将string list 转成有特殊字符分开字符串
  3. python-第二块:time模块和datatime模块
  4. 我的世界java版记分板_我的世界计分板教程 计分板指令详解
  5. python文件名匹配
  6. count(*)效率提高_2020年最新整理财务统计函数,学会让你财务统计效率提升95%以上...
  7. linux内核sysfs详解【转】
  8. 将图片变换成3d对象
  9. 小程序场景二维码扫码
  10. 1023_MISRA C规范学习_Rule 2.2
  11. 【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第四章
  12. Android 获取外网IP地址
  13. python windows 下设置文件锁、多线程
  14. 一家有两个孩子,已知至少有一个孩子是在星期二出生的男孩。问:两个孩子都是男孩的概率是多大?
  15. 2022年招投标,最加分的资质证书排行榜!
  16. 好书推荐,电子人的入门好书
  17. vue 使用正则判断邮箱格式是否正确 手机号 证件啥的都可以套用
  18. 风控中的EAD、PD与LGD模型都有啥区别?
  19. 饥荒怎么解锁机器人_【饥荒解锁全人物】饥荒角色怎么解锁_角色解锁_饥荒人物解锁条件【图】_游戏城...
  20. esp32之wifi状态机

热门文章

  1. OpenCv获取图像中椭圆长短轴的点的位置
  2. netstat -ano命令失效怎么办
  3. Aliyun 学习笔记(二)阿里云物联网平台介绍
  4. AE二次开发中几个功能速成归纳(符号设计器、创建要素、图形编辑、属性表编辑、缓冲区分析)...
  5. 在Spyder安装xlrd插件
  6. 顺丰理赔要讲究方法,注意这几个方面能获得满意结果
  7. 获取商品数据 API(商品详情、商品主图)
  8. 231313132131
  9. NAS中的文件共享协议
  10. moviepy音视频剪辑:与大小相关的视频变换函数详解