日撸Java三百行(day25:栈实现二叉树深度遍历之中序遍历)
目录
一、栈实现二叉树遍历的可行性
二、由递归推出栈如何实现中序遍历
1.左子树入栈
2.根结点出栈
3.右子树入栈
4.实例说明
三、代码实现
总结
一、栈实现二叉树遍历的可行性
在日撸Java三百行(day16:递归)中,我们讲过“递归”说白了就是函数自身调用自身,当递归函数调用自身时,我们可以看作是入栈的过程,调用一次就入栈一次;而当递归满足结束条件后,一层一层返回时,又可以看作是出栈的过程,返回一层就出栈一次。这样看来,递归实现和栈实现似乎是可以相互转化的,毕竟它们都满足“先进后出、后进先出”的原则。
在日撸Java三百行(day21:二叉树的深度遍历的递归实现)中,我们是利用递归的方法对二叉树进行的前中后序遍历,那么既然递归实现和栈实现可以相互转化,我们是否可以利用栈的思想来完成二叉树的遍历呢?今天我们就先从用栈实现二叉树的中序遍历开始(因为中序遍历是几种遍历中最简单的)。
二、由递归推出栈如何实现中序遍历
先来回顾一下我们之前是怎么用递归实现二叉树中序遍历的,代码如下:
/************************ In-order visit.**********************/public void inOrderVisit() {if(leftChild != null) {leftChild.inOrderVisit();} // of ifSystem.out.print("" + value + " ");if(rightChild != null) {rightChild.inOrderVisit();} // of if} // of inOrderVisit
中序遍历是按“左子树 根结点 右子树”的顺序进行遍历,上述的递归函数就是先判断当前根结点的左子树是否为空,不空则搁置当前这层函数操作,将该左子树作为新的根结点,再次调用函数;空则直接输出当前根结点,再按照同样的方法判断右子树。这个过程如果用栈来完成,可以简单概括为三步,即左子树入栈、根结点出栈、右子树入栈,下面我们就来对这三步进行分析说明。
1.左子树入栈
由上边递归函数的代码顺序可知,每次调用函数时都会优先进入左子树,如果当前左子树不空,就会进入新一层的递归函数;进入新一层的函数后,再次优先进入左子树,如果该左子树仍不空,则再次进入下一层递归函数……总结一下就是,如果左子树持续不空,那么就会一直朝着左子树的方向行进,直到某个结点的左子树为空。为了便于理解,我们以下图为例:
- 首先a作为根结点调用递归函数
- 进入a的左子树b
- a的左子树b不为空,于是将b作为新的根结点调用递归函数
- 进入b的左子树d
- b的左子树d不为空,于是将d作为新的根结点调用递归函数
- 进入d的左子树h
- d的左子树h不为空,于是将h作为新的根结点调用递归函数
- h没有左子树,停止调用
我们在一开始说过,调用一次递归函数就可以看作是入栈一次,所以上述过程,如果用栈来实现, 就是依次入栈左子树,如果该左子树中还有左子树,则继续入栈左子树,直到某个结点的左子树为空,其实也就是按照上图中红色箭头的方向持续入栈左子树。不过需要注意,每个根结点都必须在其左子树之前入栈,上图的二叉树入栈后结果如下:
2.根结点出栈
根据上面递归函数的代码,可以知道由于上图中h结点的左子树为空,所以不会继续调用函数,而是来到第11行代码直接输出h结点。这个过程反映到栈中,就是将此时的栈顶元素——h结点出栈并访问它。
3.右子树入栈
在上述的递归函数中,输出结点h后,接下来我们就要开始判断其右子树了,如果其右子树不为空,那么就把它的右子树作为新的根结点调用递归函数。用栈的思想考虑,就是如果此时出栈元素的右子树不空,就将它的右子树入栈,然后再从该右子树出发(即把该右子树当作当前根结点),按照“左子树入栈、根结点出栈、右子树入栈”的顺序进行;而如果此时出栈元素的右子树为空,则将当前栈顶元素进行出栈,然后继续判断新出栈元素的右子树。
4.实例说明
我们简单总结一下以上三步,先将左子树依次入栈,然后出栈当前栈顶元素,再判断该出栈元素的右子树,最后根据判断结果执行。
栈实现二叉树遍历用文字语言叙述,真的既拗口又不好理解,下面我们还是用一个具体的例子来说明,这样稍微直观一点。对于下图的二叉树,栈实现中序遍历的具体步骤如下:
- 左子树依次入栈。放在这里就是将a、b、d依次入栈,由于d之后没有左子树了,所以入栈到d这里就暂时停止了。
- 将当前栈顶元素d出栈,并输出d。
- 判断此时出栈元素的右子树。由于此时出栈元素d的右子树为空,所以下一步应该将新的栈顶元素出栈。
- 将新的栈顶元素b出栈,并输出b。
- 判断此时出栈元素的右子树。由于此时出栈元素b的右子树不空,所以下一步应该将它的右子树入栈。
- 将b的右子树e入栈。(一旦右子树入栈,下一步就是该右子树的左子树依次入栈)
- 左子树再次依次入栈。放到这里就是将e之后的左子树依次入栈,由于h之后没有左子树了,所以入栈到h这里就暂时停止了。
- 将当前栈顶元素h出栈,并输出h。
- 判断此时出栈元素的右子树。由于此时出栈元素h的右子树为空,所以下一步应该将新的栈顶元素出栈。
- 将新的栈顶元素e出栈,并输出e。
- 判断此时出栈元素的右子树。由于此时出栈元素e的右子树为空,所以下一步应该将新的栈顶元素出栈。
- 将新的栈顶元素a出栈,并输出a。
- 判断此时出栈元素的右子树。由于此时出栈元素a的右子树不空,所以下一步应该将它的右子树入栈。
- 将a的右子树c入栈。(一旦右子树入栈,下一步就是该右子树的左子树依次入栈)
- 左子树再次依次入栈。放到这里就是将c之后的左子树依次入栈,由于f之后没有左子树了,所以入栈到f这里就暂时停止了。
- 将当前栈顶元素f出栈,并输出f。
- 判断此时出栈元素的右子树。由于此时出栈元素f的右子树不空,所以下一步应该将它的右子树入栈。
- 将f的右子树i入栈。(一旦右子树入栈,下一步就是该右子树的左子树依次入栈)
- 左子树再次依次入栈。但是由于此时i没有左子树了,所以这一步跳过。
- 将当前栈顶元素i出栈,并输出i。
- 判断此时出栈元素的右子树。由于此时出栈元素i的右子树为空,所以下一步应该将新的栈顶元素出栈。
- 将新的栈顶元素c出栈,并输出c。
- 判断此时出栈元素的右子树。由于此时出栈元素c的右子树不空,所以下一步应该将它的右子树入栈。
- 将c的右子树g入栈。(一旦右子树入栈,下一步就是该右子树的左子树依次入栈)
- 左子树再次依次入栈。但是由于此时g没有左子树了,所以这一步跳过。
- 将当前栈顶元素g出栈,并输出g。
- 判断此时出栈元素的右子树。由于此时出栈元素g的右子树为空,所以下一步应该将新的栈顶元素出栈。
- 将新的栈顶元素出栈。但是由于此时栈已空同时当前结点也为空,所以到此就完成了。
由这个例子,我们可以得出以下便于后续编程的结论:
- 出栈后立马输出该出栈元素
- 当栈空且结点也为空时,遍历结束
三、代码实现
大概理解这个过程之后,我们还是先开始代码模拟吧(毕竟用文字解释感觉真的不好说清楚,不过也有可能是我的语言表达水平有限吧…)
为了提高代码的复用性,这里我们重写了一个和通用性队列类似的通用性栈,代码如下:
package datastructure;/***Object stack.**@auther Xin Lin 3101540094@qq.com.*/public class ObjectStack {/*** The depth.*/public static final int MAX_DEPTH = 10;/*** The actual depth.*/int depth;/*** The data.*/Object[] data;/************************ Construct an empty Object stack.**********************/public ObjectStack() {depth = 0;data = new Object[MAX_DEPTH];} // Of the first constructor/************************ Overrides the method claimed in Object, the superclass of any class.**********************/public String toString() {String resultString = "";for (int i = 0; i < depth; i++) {resultString += data[i];} // Of for ireturn resultString;} // Of toString/************************ Push an element.* * @param paraObject The given object.* @return Success or not.**********************/public boolean push(Object paraObject) {if (depth == MAX_DEPTH) {System.out.println("Stack full.");return false;} // Of ifdata[depth] = paraObject;depth++;return true;} // Of push/************************ Pop an element.* * @return The object at the top of the stack.**********************/public Object pop() {if(depth == 0) {System.out.println("Nothing to pop.");return '\0';} // Of ifObject resultObject = data[depth - 1];depth--;return resultObject;} // Of pop/************************ Is the stack empty?* * @return True if empty.**********************/public boolean isEmpty() {if(depth == 0) {return true;} // Of ifreturn false;} // Of isEmpty/************************The entrance of the program.** @param args Not used now.**********************/public static void main(String[] args) {ObjectStack tempStack = new ObjectStack();for(char ch = 'a'; ch < 'm'; ch++) {tempStack.push(new Character(ch));System.out.println("The current stack is: " + tempStack);} // Of for chchar tempChar;for(int i = 0; i < 12; i++) {tempChar = ((Character)tempStack.pop()).charValue();System.out.println("Popped: " + tempChar);System.out.println("The current stack is: " + tempStack);} // Of for i} // Of main
} // Of class ObjectStack
在重写的过程中,一定要注意强制类型转换的使用。比如倒数第6行代码中,由于栈是Object类型的栈,所以得到的出栈元素肯定也是Object类型,因此我们需要使用Character先将其强制转换成Character类型,再利用charValue()方法将Character类型转换为基本数据类型char,最后再赋给同为char类型的变量tempChar。
现在我们开始创建方法,首先创建一个ObjectStack类型的对象栈,以及一个二叉树的结点引用;然后,定义一个while循环,循环条件为栈不空或者结点不空(因为栈空且结点空的时候,遍历就结束了)。
在while循环中,如果当前结点不空,则将其入栈,再利用tempNode = tempNode.leftChild不断往下迭代左子树,具体来说就是不断地将当前结点的左子树作为新的当前结点;如果当前结点为空,则说明当前结点的根结点没有左子树,所以根据中序遍历“左 根 右”的顺序要求,此时就直接输出当前结点的根结点,也就是输出此时的栈顶元素(注意先出栈再打印);然后,将该出栈元素的右子树作为新的当前结点,继续判断。
/************************ In-order visit with stack.**********************/public void inOrderVisitWithStack() {ObjectStack tempStack = new ObjectStack();BinaryCharTree tempNode = this;while(!tempStack.isEmpty() || tempNode != null) {if(tempNode != null) {tempStack.push(tempNode);tempNode = tempNode.leftChild;} else {tempNode = (BinaryCharTree)tempStack.pop();System.out.print("" + tempNode.value + " ");tempNode = tempNode.rightChild;} // Of if} // Of while} // Of inOrderVisitWithStack
最后,我们用昨天创建的二叉树tempTree2来进行数据测试,如下:
System.out.println("\r\nIn-order visit with stack: ");
tempTree2.inOrderVisitWithStack();
完整的程序代码:
package datastructure.tree;import datastructure.*;
import java.util.Arrays;
/*** Binary tree with char type elements.**@auther Xin Lin 3101540094@qq.com.*/public class BinaryCharTree {/*** The value*/char value;/*** The left child*/BinaryCharTree leftChild;/*** The right child*/BinaryCharTree rightChild;/************************ The first constructor.* * @param paraName The value.**********************/public BinaryCharTree(char paraName) {value = paraName;leftChild = null;rightChild = null;} // Of constructor/************************ Manually construct a tree. Only for testing.**********************/public static BinaryCharTree manualConstructTree() {// Step 1. Construct a tree with only one node.BinaryCharTree resultTree = new BinaryCharTree('a');// Step 2. Construct all Nodes. The first node is the root.// BinaryCharTree tempTreeA = resultTree.root;BinaryCharTree tempTreeB = new BinaryCharTree('b');BinaryCharTree tempTreeC = new BinaryCharTree('c');BinaryCharTree tempTreeD = new BinaryCharTree('d');BinaryCharTree tempTreeE = new BinaryCharTree('e');BinaryCharTree tempTreeF = new BinaryCharTree('f');BinaryCharTree tempTreeG = new BinaryCharTree('g');// Step 3. Link all Nodes.resultTree.leftChild = tempTreeB;resultTree.rightChild = tempTreeC;tempTreeB.rightChild = tempTreeD;tempTreeC.leftChild = tempTreeE;tempTreeD.leftChild = tempTreeF;tempTreeD.rightChild = tempTreeG;return resultTree;} // Of manualConstructTree/************************ Pre-order visit.**********************/public void preOrderVisit() {System.out.print("" + value + " ");if(leftChild != null) {leftChild.preOrderVisit();} // Of ifif(rightChild != null) {rightChild.preOrderVisit();} // Of if} // Of preOrderVisit/************************ In-order visit.**********************/public void inOrderVisit() {if(leftChild != null) {leftChild.inOrderVisit();} // Of ifSystem.out.print("" + value + " ");if(rightChild != null) {rightChild.inOrderVisit();} // Of if} // Of inOrderVisit/************************ Post-order visit.**********************/public void postOrderVisit() {if(leftChild != null) {leftChild.postOrderVisit();} // Of ifif(rightChild != null) {rightChild.postOrderVisit();} // Of ifSystem.out.print("" + value + " ");} // Of postOrderVisit/************************ Get the depth of the binary char tree.* * @return The depth.**********************/public int getDepth() {if((leftChild == null) && (rightChild == null)) {return 1;} // Of if// The depth of the left child.int tempLeftDepth = 0;if(leftChild != null) {tempLeftDepth = leftChild.getDepth();} // Of if// The depth of the right child.int tempRightDepth = 0;if(rightChild != null) {tempRightDepth = rightChild.getDepth();} // Of ifif(tempLeftDepth >= tempRightDepth) {return tempLeftDepth + 1;} else {return tempRightDepth + 1;} // Of if} // Of getDepth/************************ Get the number of nodes of the binary char tree.* * @return The number of nodes.**********************/public int getNumNodes() {if((leftChild == null) && (rightChild == null)) {return 1;} // Of if// The number of nodes of the left child.int tempLeftNodes = 0;if(leftChild != null) {tempLeftNodes = leftChild.getNumNodes();} // Of if// The number of nodes of the right child.int tempRightNodes = 0;if(rightChild != null) {tempRightNodes = rightChild.getNumNodes();} // Of if// The total number of nodes.return tempLeftNodes + tempRightNodes + 1;} // Of getNumNodes/*** The values of nodes according to breadth first traversal.*/char[] valuesArray;/*** The indices in the complete binary tree.*/int[] indicesArray;/*********************** Convert the tree to data arrays, including a char array and an int array.* The results are stored in two member variables.* * @see #valuesArray* @see #indicesArray**********************/public void toDataArrays() {//Initialize arrays.int tempLength = getNumNodes();valuesArray = new char[tempLength];indicesArray = new int[tempLength];int i = 0;//Traverse and convert at the same time.CircleObjectQueue tempQueue = new CircleObjectQueue();tempQueue.enqueue(this);CircleIntQueue tempIntQueue = new CircleIntQueue();tempIntQueue.enqueue(0);BinaryCharTree tempTree = (BinaryCharTree) tempQueue.dequeue();int tempIndex = tempIntQueue.dequeue();while (tempTree != null) {valuesArray[i] = tempTree.value;indicesArray[i] = tempIndex;i++;if (tempTree.leftChild != null) {tempQueue.enqueue(tempTree.leftChild);tempIntQueue.enqueue(tempIndex * 2 + 1);} // Of ifif (tempTree.rightChild != null) {tempQueue.enqueue(tempTree.rightChild);tempIntQueue.enqueue(tempIndex * 2 + 2);} // Of itempTree = (BinaryCharTree) tempQueue.dequeue();tempIndex = tempIntQueue.dequeue();} // Of while} // Of toDataArrays/*********************** Convert the tree to data arrays, including a char array and an int array.* The results are stored in two member variables.* * @see #valuesArray* @see #indicesArray**********************/public void toDataArraysObjectQueue() {//Initialize arrays.int tempLength = getNumNodes();valuesArray = new char[tempLength];indicesArray = new int[tempLength];int i = 0;//Traverse and convert at the same time.CircleObjectQueue tempQueue = new CircleObjectQueue();tempQueue.enqueue(this);CircleObjectQueue tempIntQueue = new CircleObjectQueue();Integer tempIndexInteger = Integer.valueOf(0);tempIntQueue.enqueue(tempIndexInteger);BinaryCharTree tempTree = (BinaryCharTree) tempQueue.dequeue();int tempIndex = ((Integer)tempIntQueue.dequeue()).intValue();System.out.println("tempIndex = " + tempIndex);while (tempTree != null) {valuesArray[i] = tempTree.value;indicesArray[i] = tempIndex;i++;if (tempTree.leftChild != null) {tempQueue.enqueue(tempTree.leftChild);tempIntQueue.enqueue(Integer.valueOf(tempIndex * 2 + 1));} // Of ifif (tempTree.leftChild != null) {tempQueue.enqueue(tempTree.leftChild);tempIntQueue.enqueue(Integer.valueOf(tempIndex * 2 + 2));} // Of iftempTree = (BinaryCharTree) tempQueue.dequeue();if (tempTree == null) {break;} // Of iftempIndex = ((Integer)tempIntQueue.dequeue()).intValue();} // Of while} // Of toDataArraysObjectQueue/************************ The second constructor. The parameters must be correct since no validity* check is undertaken.* * @param paraDataArray The array for data.* @param paraIndicesArray The array for indices.**********************/public BinaryCharTree(char[] paraDataArray, int[] paraIndicesArray) {// Step 1. Use a sequential list to store all nodes.int tempNumNodes = paraDataArray.length;BinaryCharTree[] tempAllNodes = new BinaryCharTree[tempNumNodes];for(int i = 0; i < tempNumNodes; i++) {tempAllNodes[i] = new BinaryCharTree(paraDataArray[i]);} // Of for i// Step 2. Link all nodes.for(int i = 1; i < tempNumNodes; i++) {for(int j = 0; j < i; j++) {System.out.println("Indices " + paraIndicesArray[j] + " vs. " + paraIndicesArray[i]);if(paraIndicesArray[i] == paraIndicesArray[j] * 2 + 1) {tempAllNodes[j].leftChild = tempAllNodes[i];System.out.println("Linking " + j + " with " + i);break;} // Of ifif(paraIndicesArray[i] == paraIndicesArray[j] * 2 + 2) {tempAllNodes[j].rightChild = tempAllNodes[i];System.out.println("Linking " + j + " with " + i);break;} // Of if} // Of for j} // Of for i// Step 3. The root is the first node.value = tempAllNodes[0].value;leftChild = tempAllNodes[0].leftChild;rightChild = tempAllNodes[0].rightChild;} // Of the the second constructor/************************ In-order visit with stack.**********************/public void inOrderVisitWithStack() {ObjectStack tempStack = new ObjectStack();BinaryCharTree tempNode = this;while(!tempStack.isEmpty() || tempNode != null) {if(tempNode != null) {tempStack.push(tempNode);tempNode = tempNode.leftChild;} else {tempNode = (BinaryCharTree)tempStack.pop();System.out.print("" + tempNode.value + " ");tempNode = tempNode.rightChild;} // Of if} // Of while} // Of inOrderVisitWithStack/************************ The entrance of the program.* * @param args Not used now.**********************/public static void main(String args[]) {BinaryCharTree tempTree = manualConstructTree();System.out.println("\r\nPreorder visit:");tempTree.preOrderVisit();System.out.println("\r\nIn-order visit:");tempTree.inOrderVisit();System.out.println("\r\nPost-order visit:");tempTree.postOrderVisit();System.out.println("\r\n\r\nThe depth is: " + tempTree.getDepth());System.out.println("The number of nodes is: " + tempTree.getNumNodes());tempTree.toDataArrays();System.out.println("The values are: " + Arrays.toString(tempTree.valuesArray));System.out.println("The indices are: " + Arrays.toString(tempTree.indicesArray));tempTree.toDataArraysObjectQueue();System.out.println("Only object queue.");System.out.println("The values are: " + Arrays.toString(tempTree.valuesArray));System.out.println("The indices are: " + Arrays.toString(tempTree.indicesArray));char[] tempCharArray = {'A', 'B', 'C', 'D', 'E', 'F'};int[] tempIndices = {0, 1, 2, 4, 5, 12};BinaryCharTree tempTree2 = new BinaryCharTree(tempCharArray, tempIndices);System.out.println("\r\nPreorder visit:");tempTree2.preOrderVisit();System.out.println("\r\nIn-order visit:");tempTree2.inOrderVisit();System.out.println("\r\nPost-order visit:");tempTree2.postOrderVisit();System.out.println("\r\nIn-order visit with stack: ");tempTree2.inOrderVisitWithStack();}// Of main
} // Of class BinaryCharTree
运行结果:
可以发现,对于同一棵二叉树tempTree2,我们今天用栈实现的中序遍历和我们昨天用递归实现的中序遍历,其结果是一模一样的,说明代码可行。
总结
今天,我们主要学习的就是如何利用栈来实现二叉树的中序遍历,其本质上就是递归思维和迭代思维的相互转化。单看今天的代码量的话,其实挺少的,但是如果想要说清楚理透彻这两种思维的转化过程,似乎就比较困难了,本文也只是作者个人一些浅薄的理解,如有误,欢迎批评指正。
通过今天的学习,我们可以发现果然还是递归用起来简单,不过我们还是需要像二叉树遍历这种较为复杂的迭代操作,这对于锻炼一个程序员的迭代思维还是非常好的。
相关文章:

日撸Java三百行(day25:栈实现二叉树深度遍历之中序遍历)
目录 一、栈实现二叉树遍历的可行性 二、由递归推出栈如何实现中序遍历 1.左子树入栈 2.根结点出栈 3.右子树入栈 4.实例说明 三、代码实现 总结 一、栈实现二叉树遍历的可行性 在日撸Java三百行(day16:递归)中,我们讲过…...

【vue讲解:ref属性、动态组件、插槽、vue-cli创建项目、vue项目目录介绍、vue项目开发规范、es6导入导出语法】
0 ref属性(组件间通信) # 1 ref属性放在普通标签上<input type"text" v-model"name" ref"myinput">通过 this.$refs[myinput] 拿到的是 原生dom对象操作dom对象:改值,换属性。。。# 2 ref属…...

ubuntu:最新安装使用docker
前言 系统:ubuntu 22.04 desktop 目的:安装使用docker 安装小猫猫 没有安装包的,可以自己去瞅瞅,这里不提供下载方式 sudo dpkg -i ./cat-verge_1.7.5_amd64.deb 在应用里,打开这个软件,并开启系统猫猫 配…...
Linux ssh 免密失效
sudo chmod -R 777 /home/xxx sudo chown -R xxx:xxx /home/xxx 为什么我输入这两条指令后,ssh免密失效了? 当你使用 sudo chmod -R 777 /home/xxx 和 sudo chown -R xxx:xxx /home/xxx 这两条指令后,可能会导致 SSH 免密登录失效的原因有以…...

k8s上部署ingress-controller
一、安装helm仓库 # helm pull ingress-nginx/ingress-nginx 二、修改 三、运行 # kubectl label nodes node01.110111.cn ingresstrue# kubectl label nodes node02.110112.cn ingresstrue# helm upgrade --install ingress-nginx -n ingress-nginx . -f values.yaml 四、检…...
Android 13 about launcher3 (1)
Android 13 Launcher3 android13#launcher3#分屏相关 Launcher3修改 wm density界面布局不改变 /packages/apps/Launcher3/src/com/android/launcher3/InvariantDeviceProfile.java Launcher的默认配置加载类,通过InvariantDeviceProfile方法可以看出,…...

服务器数据恢复—raid5阵列热备盘未全部启用导致阵列崩溃的数据恢复案例
服务器存储数据恢复环境: 一台EMC某型号存储中有一组RAID5磁盘阵列。该raid5阵列中有12块硬盘,其中2块硬盘为热备盘。 服务器存储故障: 该存储raid5阵列中有两块硬盘离线,只有1块热备盘启用替换掉其中一块离线盘,另外…...

HTML—css
css概述 C S S 是 C a s c a d i n g S t y l e S h e e t s ( 级 联 样 式 表 ) 。 C S S 是 一 种 样 式 表 语 言 , 用 于 为 H T M L 文 档 控 制 外 观 , 定 义 布 局 。 例 如 , C S S 涉 及 字 体 、 颜 色 、…...
IO多路复用(Input/Output Multiplexing)
IO多路复用(Input/Output Multiplexing) 是一种在单个线程中管理多个输入/输出通道的技术。它允许一个线程同时监听多个输入流(如网络套接字、文件描述符等),并在有数据可读或可写时进行相应的处理,而不需要为每个通道创建一个独立的线程。这种技术主要用于处理并发连接…...
android与pc 用socket无线通信
今天做一个android与pc通信的小demo(不是wifi,蓝牙)android为客户端,pc为服务器(一对多)。pc代码很简单,android客户端代码也不难,但是有一点不太明白就是在客户端向服务器发送消息时…...

【流程引擎】springboot完美集成activiti工作流方案
前言 activiti工作流引擎项目,企业erp、oa、hr、crm等企事业办公系统轻松落地,一套完整并且实际运用在多套项目中的案例,满足日常业务流程审批需求。 项目源码配套文档获取:本文末个人名片直接获取。 一、项目形式 springboot…...
11、常见API
01、String类概述及构造方法简介 一、字符串 由多个字符组成的一串数据 二、简介 String类代表的是一个字符串。字符串对象在开发中是最常见的。为了方便我们对字符串进行操作,java就把字符串用对象进行了封装,这个封装就是String类 三、String类的构造方…...

渗透第三次作业
目录 第一关Ma Spaghet! 第二关Jefff: 第三关:Ugandan Knuckles 第四关:Ricardo Milos 第五关: Ah Thats Hawt 第一关Ma Spaghet! <h2 id"spaghet"></h2> <script>spaghet.innerHTML (new URL(…...

Python自动化:解锁高效工作与生产力的密钥
在当今快节奏的数字时代,自动化已成为提升工作效率、优化流程、减少人为错误的不可或缺的工具。Python,作为一种功能强大、易于学习且应用广泛的编程语言,在自动化领域扮演着举足轻重的角色。无论是数据处理、Web自动化、软件测试,…...
Sentinel1.8.1 控制台改造
Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效果。 本项目是在Sentinel控制台1.8.1的基础上改造的&…...

设计模式(2)行为型模式和七大原则
1、目标 本文的主要目标是学习设计模式的行为型模式并举例说明 2、行为型模式 2.1 观察者模式(Observer) 观察者模式是对象之间存在一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新&…...
学懂C++(三十一):高级教程——深入详解C++高级多线程编程技术之锁优化与替代
引言 随着多核处理器的普及,多线程编程技术已经成为提高应用程序性能的关键手段。在多线程环境下,如何高效、安全地管理线程之间的共享资源是开发者面临的主要挑战。传统的锁机制,如互斥锁(Mutex)、临界区(…...

Linux - 基础工具使用
文章目录 一、yum1、介绍2、功能3、语法4、使用 二、rzsz1、安装rzsz的指令2、介绍3、使用 三、vim基础使用1、介绍2、基础使用 四、gcc/g使用1、生成可执行文件过程2、语法3、常用选项4、编译过程5、动静态库6、包含头文件的多文件编译7、链接外部库 一、yum 1、介绍 Linux中…...

理解线程id和简单封装原生线程库
一、理解线程id 首先我们要知道给用户提供的线程id不是内核里面LWP(轻量级进程id),而是pthread库自己维护的一个唯一值。 我们理解为什么线程id不是内核里面LWP,因为用户没有权限使用内核里面的字段,那是专门给OS管理…...

Unified 阻抗控制 architecture、framework、approach
Unified 阻抗控制(Unified Impedance Control)作为一种控制策略,其architecture(架构)、framework(框架)和approach(方法)为: 一、Unified 阻抗控制 Archite…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...