【数据结构之二叉树系列】万字深剖普通二叉树的遍历+分治算法思想

news2024/10/7 6:46:24

目录

    • 前言
    • 一、背景知识
    • 二、前序遍历
    • 三、中序遍历
    • 四、后序遍历
    • 五、求二叉树中结点的个数
      • 1. 遍历+计数
        • (1)前序遍历+计数
        • (2)中序遍历+计数
        • (3)后序遍历+计数
      • 2.分治算法思想(推荐)
  • 敬请期待

前言

前面我们学习的数据结构主要是线性表,我们在学习的时候主要是学习对数据结构中的数据进行增删查改,但是我们会发现,对于普通的二叉树而言,插入一个数据可以在很多地方进行插入,删除数据也可以在很多地方进行删除,同样的道理,在没有一定规律的情况下,想要在普通的二叉树中进行查找数据也是很麻烦的,修改也是一样的道理,所以,学习普通二叉树的增删查改是没有任何意义的,今天我们重点学习的是二叉树的几种重要的遍历方式:前序遍历,中序遍历,后序遍历,层序遍历。其中,前面三种遍历方式需要我们对递归有一个很深层次的理解才能够搞懂!!!今天我们主要以下面这棵树作为例子进行讲解。
在这里插入图片描述

一、背景知识

在学习二叉树的遍历之前我们需要知道,二叉树中可以分为很多的子树,每一棵子树都是由对应的根节点和左右子树组成的,其中根节点和左右子树都是有可能为空的,这个视具体情况而定。这个基本知识可以为后面学习递归和分治算法思想做很大的铺垫。任何的子树可能存在的情况为:空树,只有根节点,只有左子树,只有右子树,同时存在左右子树

  • 空树:即根节点为空指针的情况
  • 只有根节点:根节点不为空,但是左右子树均为空
  • 只有左子树:根节点和左子树不为空,右子树为空
  • 只有右子树:根节点和右子树不为空,左子树为空
  • 同时存在左右子树:根节点和左右子树均不为空

对应的就是下图的情况:
在这里插入图片描述

二、前序遍历

我们知道,树中的任何子树都是由根节点和左右子树构成的,对于每一棵子树而言,前序遍历方式的规则是:根节点,左子树,右子树。也就是说,我们可以将一棵树想象成很多的子树,我们在遍历一棵二叉树的时候,需要将所有的子树全部遍历完,而遍历方式就是规定了树中的遍历规则,即每一棵子树应该按照什么样的顺序去遍历访问。下面我们使用前序遍历的方式来详细介绍下图的遍历:
在这里插入图片描述

遍历过程:首先访问的是A结点对应的树,而A结点对应的树是由A结点和对应的左右子树构成的,根据前序遍历的方式,先访问的是A结点,然后访问A结点的左子树,A结点对应的左子树的根节点是B结点,所以接下来访问的就是B结点对应的树,B结点对应的树同样是由B结点和对应的左右子树构成,根据前序遍历的方式,先访问的是B结点,然后访问B结点的左子树,B结点对应的左子树就是D节点对应的树了,D结点对应的树是由D结点和对应的左右子树构成的,访问D结点的树时,先访问D结点,然后访问D结点对应的左子树,D结点对应的左子树是H结点对应的树,H结点对应的树同样是由D结点和对应的左右子树构成,访问H结点的树时先访问H结点,然后再访问H结点对应的左子树,我们会发现,当我们在访问H结点的左子树时,发现H结点的左子树是空,所以此时就会返回到H结点,此时对于H结点对应的树而言,H结点对应的树的根节点和左子树已经访问完成,所以接下来访问的就是H结点的右子树了,H结点的右子树是K结点对应的树,K结点对应的树是由K结点和对应的左右子树构成,访问K结点时,先访问的是K结点,然后访问K结点的树的左子树,访问K结点的左子树时,发现为空,所以此时应该访问K结点的右子树,发现也为空,所以此时会返回到H结点,H结点遍历完成,返回到D结点,此时相当于D结点的左子树遍历完成,所以接下来访问的是D结点的右子树,发现为空,返回到B结点,此时B结点对应的左子树访问完成,继续访问B结点的右子树,也就是E结点对应的树,E结点对应的树是由E结点和对应的左右子树构成,访问E树时,先访问E结点,再访问E树的左子树,访问E树的左子树时,发现为空,返回到E树,再访问右子树,发现为空,返回到B树,此时B树访问完成,也就是A树的左子树访问完成,接下来访问的就是A树的右子树了,情况和上面一样。

最后的遍历顺序为:
在这里插入图片描述
即:
在这里插入图片描述

  • 代码实现
typedef int BTDataType;
// 链式二叉树的定义
typedef struct BinaryTreeNode
{
	BTDataType data;  // 数据域
	struct BinaryTreeNode* left;// 指向左子树的指针
	struct BinaryTreeNode* right;// 指向右子树的指针
}BTNode;

// 前序遍历
void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

BTNode* CreateBinaryTree(BTDataType data)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		return NULL;
	}
	newnode->data = data;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

int main()
{
// 手动创建树的结点
	BTNode* Node1 = CreateBinaryTree(1);
	BTNode* Node2 = CreateBinaryTree(2);
	BTNode* Node3 = CreateBinaryTree(3);
	BTNode* Node4 = CreateBinaryTree(4);
	BTNode* Node5 = CreateBinaryTree(5);
	BTNode* Node6 = CreateBinaryTree(6);
// 手动将创建的结点链接起来
	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;

	PrevOrder(Node1);
	return 0;
}
  • 实验结果
    在这里插入图片描述
    根据上面对于前序遍历的分析,这里不难分析出这个结果。

三、中序遍历

中序遍历方式的规则是:左子树,根节点,右子树。注意在分析的过程是要将树分成很多子树进行访问的。这里同样以上面这棵树作为例子进行分析:
在这里插入图片描述

遍历过程: 首先给到树的根节点,所以先访问到A树,但是访问A树时,需要先访问A树的左子树,所以接下来访问A树的左子树,A树的左子树是B树,访问B树时,需要先访问B树的左子树,接下来访问B树的左子树,B树的左子树是D树,访问D树时,需要先访问D树的左子树,D树的左子树是H树,访问H树时需要先访问H树的左子树,当我们访问H树的左子树时,我们发现H树的左子树是空树,所以直接返回到H树,那么H树的左子树访问完成之后就应该访问H树的根节点也就是H结点,H结点返回完之后返回H树的右子树,H树的右子树是K树,访问K树时需要先访问K树的左子树,当我们访问K树的左子树时,发现K树的左子树为空,所以返回到K树,继续访问K树的根节点,也就是K结点,再访问K树的右子树,K树的右子树为空,所以返回到H树,H树的右子树访问完成,返回到D树,D树的左子树访问完成,继续访问D树的根节点,也就是D结点,再访问D树的右子树,D树的右子树为空,返回到B树,B树的左子树访问完成,继续访问B树的根节点,也就是B结点,再访问B树的右子树,B树的右子树为E树,访问E树时需要先访问E树的左子树,E树的左子树为空,返回到E树,继续访问E树的根节点,也就是E结点,再访问E树的右子树,E树的右子树为空,返回到B树,B树的右子树访问完成,即B树访问完成,即A树的左子树访问完成,这个时候才可以访问A树的根节点,也就是A结点,再继续访问A树的右子树,访问A树的右子树的过程和访问A树的左子树一模一样。

最终的访问顺序为:
在这里插入图片描述
即:
在这里插入图片描述

  • 代码实现
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

// 中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

BTNode* CreateBinaryTree(BTDataType data)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		return NULL;
	}
	newnode->data = data;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

int main()
{
	BTNode* Node1 = CreateBinaryTree(1);
	BTNode* Node2 = CreateBinaryTree(2);
	BTNode* Node3 = CreateBinaryTree(3);
	BTNode* Node4 = CreateBinaryTree(4);
	BTNode* Node5 = CreateBinaryTree(5);
	BTNode* Node6 = CreateBinaryTree(6);

	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;

	InOrder(Node1);
	return 0;
}
  • 实验结果
    在这里插入图片描述

四、后序遍历

后序遍历的规则是:左子树,右子树,根节点。
在遍历的过程中同样需要将待分析的树分成很多的子树,然后依次分析每棵子树的访问,访问每一棵子树的时候,都需要知道每一棵子树都是由根节点和左右子树构成,根据后序遍历的规则,需要先遍历左子树,再遍历右子树,最后遍历根节点。这里同样以上面这棵树作为例子进行分析
在这里插入图片描述

遍历过程: 首先给到的条件是树的根节点,所以肯定从根节点进行分析,首先访问A树,访问A树时,需要先访问A树的左子树,A树的左子树是B树,访问B树时需要先访问B树的左子树,B树的左子树是D树,访问D树时,需要先访问D树的左子树,D树的左子树是H树,访问H树时,需要先访问H树的左子树,H树的左子树是空树,所以返回到H树,继续访问H树的右子树,H树的右子树是K树,访问K树时,先访问K树的左子树,K树的左子树是空树,返回到K树,继续访问K树的右子树,K树的右子树为空树,返回到K树,再访问K树的根节点,也就是K结点,K树访问完成之后,H树的右子树访问完成,继续访问H树的根节点,也就是H结点,H树访问完成之后,相当于D树的左子树访问完成,继续访问D树的右子树,D树的右子树为空,返回到D树,继续访问D树的根节点,也就是D结点,D树访问完成之后,相当于B树的左子树访问完成,继续访问B树的右子树,B树的右子树是E树,访问E树时,需要先访问E树的左子树,E树的左子树是空,返回到E树,继续访问E树的右子树,E树的右子树为空,返回到E树,继续访问E树的根节点,也就是E结点,E树访问完成之后,相当于B树的右子树访问完成,返回到B树,继续访问B树的根节点,也就是B结点,B树访问完成之后,相当于A树的左子树访问完成,然后继续访问A树的右子树,访问过程和访问A树的左子树一模一样,当A树的右子树访问完成之后,最后再继续访问A树的根节点,也就是A结点,当A树访问完成之后,整棵树的遍历即完成。

  • 最终的访问顺序为:
    在这里插入图片描述
    即:
    在这里插入图片描述

  • 代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

// 后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

BTNode* CreateBinaryTree(BTDataType data)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		return NULL;
	}
	newnode->data = data;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

int main()
{
	BTNode* Node1 = CreateBinaryTree(1);
	BTNode* Node2 = CreateBinaryTree(2);
	BTNode* Node3 = CreateBinaryTree(3);
	BTNode* Node4 = CreateBinaryTree(4);
	BTNode* Node5 = CreateBinaryTree(5);
	BTNode* Node6 = CreateBinaryTree(6);

	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;

	PostOrder(Node1);
	return 0;
}
  • 实验结果:
    在这里插入图片描述

五、求二叉树中结点的个数

1. 遍历+计数

(1)前序遍历+计数

  • 代码实现
// 求结点个数
int count = 0;
void TreeNodeSize1(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	// 前序遍历
	count++;
	TreeNodeSize1(root->left);
	TreeNodeSize1(root->right);
}

int main()
{
	BTNode* Node1 = CreateBinaryTree(1);
	BTNode* Node2 = CreateBinaryTree(2);
	BTNode* Node3 = CreateBinaryTree(3);
	BTNode* Node4 = CreateBinaryTree(4);
	BTNode* Node5 = CreateBinaryTree(5);
	BTNode* Node6 = CreateBinaryTree(6);

	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;

	TreeNodeSize1(Node1);
	printf("TreeNodeSize:%d\n", count);
	return 0;
}
  • 实验结果
    在这里插入图片描述

(2)中序遍历+计数

  • 代码实现
// 求结点个数
void TreeNodeSize2(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	// 中序遍历
	
	TreeNodeSize2(root->left);
	count++;
	TreeNodeSize2(root->right);
}

int main()
{
	BTNode* Node1 = CreateBinaryTree(1);
	BTNode* Node2 = CreateBinaryTree(2);
	BTNode* Node3 = CreateBinaryTree(3);
	BTNode* Node4 = CreateBinaryTree(4);
	BTNode* Node5 = CreateBinaryTree(5);
	BTNode* Node6 = CreateBinaryTree(6);

	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;

	TreeNodeSize2(Node1);

	printf("TreeNodeSize:%d\n", count);
	return 0;
}

  • 实验结果
    在这里插入图片描述

(3)后序遍历+计数

  • 代码实现
// 求结点个数
void TreeNodeSize3(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	// 后序遍历

	TreeNodeSize3(root->left);
	TreeNodeSize3(root->right);
	count++;
}

int main()
{
	BTNode* Node1 = CreateBinaryTree(1);
	BTNode* Node2 = CreateBinaryTree(2);
	BTNode* Node3 = CreateBinaryTree(3);
	BTNode* Node4 = CreateBinaryTree(4);
	BTNode* Node5 = CreateBinaryTree(5);
	BTNode* Node6 = CreateBinaryTree(6);

	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;


	TreeNodeSize3(Node1);

	printf("TreeNodeSize:%d\n", count);
	return 0;
}
  • 实验结果
    在这里插入图片描述
    注意:
    上面三种遍历+计数的方式虽然通过上面的测试可以计算出二叉树中结点的个数,但是如果多次调用就会出现一些问题,我们以前序为例:
int main()
{
	BTNode* Node1 = CreateBinaryTree(1);
	BTNode* Node2 = CreateBinaryTree(2);
	BTNode* Node3 = CreateBinaryTree(3);
	BTNode* Node4 = CreateBinaryTree(4);
	BTNode* Node5 = CreateBinaryTree(5);
	BTNode* Node6 = CreateBinaryTree(6);

	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;


	 TreeNodeSize1(Node1);
	 printf("TreeNodeSize:%d\n", count);

	TreeNodeSize1(Node1);
	printf("TreeNodeSize:%d\n", count);

	TreeNodeSize1(Node1);
	printf("TreeNodeSize:%d\n", count);
	return 0;
}
  • 实验结果
    在这里插入图片描述
    我们会发现,上面的结果显然被累加了,所以结果显然是错误,因为我们是通过一个全局变量的计数器count来计数二叉树中结点的个数,所以我们每次在进行计数之前需要对计数器进行清零,防止累加。
  • 代码实现
int main()
{
	BTNode* Node1 = CreateBinaryTree(1);
	BTNode* Node2 = CreateBinaryTree(2);
	BTNode* Node3 = CreateBinaryTree(3);
	BTNode* Node4 = CreateBinaryTree(4);
	BTNode* Node5 = CreateBinaryTree(5);
	BTNode* Node6 = CreateBinaryTree(6);

	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;

	count = 0;
	 TreeNodeSize1(Node1);
	printf("TreeNodeSize:%d\n", count);
	
	count = 0;
	TreeNodeSize1(Node1);
	printf("TreeNodeSize:%d\n", count);

	count = 0;
	TreeNodeSize1(Node1);
	printf("TreeNodeSize:%d\n", count);
	return 0;
}
  • 实验结果
    在这里插入图片描述

2.分治算法思想(推荐)

分治算法思想的本质其实也是一种递归的算法思想,其原理是通过将原来的问题分解为子问题,直到不能再分解为止。
像在使用分治算法思想求解二叉树的结点个数时,我们可以将求解的结点个数转换成:待求树的左子树的结点个数+右子树的结点个数+根节点,树中的每一棵子树都是采用这种递归的形式进行求解,直到递归到叶子节点时,叶子节点的左右子树均是空,此时返回的就是叶子结点本身的个数,也就是1,然后依次返回,最终即可求得结果。

  • 代码实现
// 求结点个数
size_t TreeNodeSize4(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return TreeNodeSize4(root->left) + TreeNodeSize4(root->right) + 1;

int main()
{
	BTNode* Node1 = CreateBinaryTree(1);
	BTNode* Node2 = CreateBinaryTree(2);
	BTNode* Node3 = CreateBinaryTree(3);
	BTNode* Node4 = CreateBinaryTree(4);
	BTNode* Node5 = CreateBinaryTree(5);
	BTNode* Node6 = CreateBinaryTree(6);

	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;

	
	printf("TreeNodeSize:%zu\n", TreeNodeSize4(Node1));

	
	return 0;
}
}
  • 实验结果
    在这里插入图片描述

敬请期待

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/179457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java基础 IO

IO流 IO流 什么是IO流&#xff1f; 存储和读取数据的解决方案 I&#xff1a;input O&#xff1a;output 流&#xff1a;像水流一样传输数据 IO流的作用&#xff1f; 用于读写数据&#xff08;本地文件&#xff0c;网络&#xff09; IO流按照流向可以分类哪两种流&#xff1f…

数据库02_函数依赖,数据库范式,SQL语句关键字,数据库新技术---软考高级系统架构师009

1.首先我们来看这个,给定一个X,能确定一个Y那么就说,X确定Y,或者Y依赖x,那么 比如y = x * x 就是x确定y,或者y依赖于x 2.然后再来看图,那么左边的部分函数依赖,就是,通过A和B能决定C,那么如果A只用给就能决定C,那么就是部分函数依赖. 3.然后再来看,可以看到,A可以决定B,那么…

servlet过滤器Filter简要回顾-过滤请求字符编码,/和/*和/**的区别

servlet过滤器Filter简要回顾-过滤请求字符编码,/和/*和/**的区别servlet过滤器1.filter过滤器的含义2.filter过滤器的使用3.测试-过滤字符编码正确响应中文编码3.1 创建servlet用于显示中文字符3.2 自定义过滤器3.3 配置web.xml中的servlet映射以及过滤器请求拦截3.4 运行输出…

【编程入门】开源记事本(安卓版)

背景 前面已输出多个系列&#xff1a; 《十余种编程语言做个计算器》 《十余种编程语言写2048小游戏》 《17种编程语言10种排序算法》 《十余种编程语言写博客系统》 《十余种编程语言写云笔记》 本系列对比云笔记&#xff0c;将更为简化&#xff0c;去掉了网络调用&#xff0…

第二章 物理层

第二章 物理层 2.1 物理层的基本概念 物理层考虑的是怎样才能在连接各种就算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的传输媒体 物理层的主要任务描述为确定与传输媒体的接口有关的一些特性 机械特性 指明接口所用接线器的形状和尺寸&#xff0c;引脚数目和排…

辗转相除以及辗转相减法

文章目录前言辗转相除法&#xff08;又名欧几里算法&#xff09;辗转相减法&#xff08;又名更相减损法&#xff09;原始辗转相减法改版辗转相减法&#xff08;减的是指数&#xff09;参考文章前言 在学习Acwing c蓝桥杯辅导课第八讲数论-AcWing 1223. 最大比例时有使用到求指…

使用CNN进行2D路径规划

卷积神经网络(CNN)是解决图像分类、分割、目标检测等任务的流行模型。本文将CNN应用于解决简单的二维路径规划问题。主要使用Python, PyTorch, NumPy和OpenCV。 任务 简单地说&#xff0c;给定一个网格图&#xff0c;二维路径规划就是寻找从给定起点到所需目标位置&#xff0…

如何取消PDF文件的保护设置?

PDF文件可以设置两种保护模式&#xff0c;一种是打开文件保护&#xff0c;也就是设置打开密码&#xff0c;只有输入密码才能打开文件&#xff1b;另一种是限制保护&#xff0c;即限制密码&#xff0c;可以根据需要设置PDF文件是否可以进行编辑、复制、打印等操作。 如果不需要…

第二篇:Haploview做单倍型教程2--分析教程

大家好&#xff0c;我是邓飞&#xff0c;这里介绍一下如何使用Haploview进行单倍型的分析。 计划分为三篇文章&#xff1a; 第一篇&#xff1a;Haploview做单倍型教程1–软件安装第二篇&#xff1a;Haploview做单倍型教程2–分析教程第三篇&#xff1a;Haploview做单倍型教程…

kaggle竞赛 | 计算机视觉 | Doodle Recognition Challenge

目录赛题链接赛题背景数据集探索合并多个类别CSV数据集数据建模 (pytorch)赛题链接 https://www.kaggle.com/competitions/quickdraw-doodle-recognition/overview/evaluation 数据集从上述链接中找 赛题背景 Quick&#xff0c;Draw&#xff01;作为实验性游戏发布&#xff…

python元组

python元组 文章目录python元组一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.创建元组2.访问元组3.修改元组4.删除元组5.索引及截取6.元组运算符7.内置函数总结一、实验目的 掌握元组的用法 二、实验原理 Python 的元组与列表类似&#xff0c;不同之处在…

2. MySQL之mysql-connector-python的安装使用

MySQL 是最流行的关系型数据库管理系统&#xff0c;关于数据库以及MySQL相关知识&#xff0c;此处不再赘述。本篇介绍使用 mysql-connector-python 来连接使用 MySQL。 1. 安装mysql-connector-python 执行以下代码&#xff0c;没有报错&#xff0c;证明安装成功。 import my…

旗舰版:Stimulsoft Ultimate 2023.1.5 Crack

Stimulsoft Ultimate 是一套用于创建报告和仪表板的通用工具。该产品包括一整套适用于 WinForms、ASP.NET、.NET Core、JavaScript、WPF、PHP、Java 和其他环境的工具。 无需比较产品功能。Stimulsoft Ultimate 包括一切&#xff01; 报表设计器的一切 我们提供易于使用且功能齐…

Android深入系统完全讲解(41)

我们要学习的是整体逻辑&#xff0c;我们 C 找 Java 的依据是类和对象&#xff0c;参数中 JNIEnv *env, jobject obj 。 env 代表当前环境上下文&#xff0c;这个当我们多个线程调用的时候&#xff0c;需要 AttachCurrentThread 进行设定&#xff0c;让 env 关联到当前线程&…

Linux常见命令 24 - RPM命名管理-包命名与依赖性

目录 1. RPM包命名规则 2. RPM包依赖性 1. RPM包命名规则 如包全名&#xff1a;httpd-2.2.15-15.e16.centos.1.i686.rpm httpd&#xff1a;软件包名2.2.15&#xff1a;软件版本15&#xff1a;软件发布的次数el6.centos&#xff1a;适合的Linux平台&#xff1a;CentOS 6.xi6…

springboot和nacos整合mybatis-plus实现多数据源管理

文章目录1.依赖2.配置文件3.redis测试3.1redis配置文件3.2controller3.3测试4.mysql测试4.1数据库表和结构4.2实体类和枚举4.3DogMapper.xml4.4DogMapper4.5service和serviceImpl4.6controller4.7测试写了一个小demo&#xff0c;通过mybatis-plus实现多数据源管理使用了mysql和…

【笔记】A simple yet effective baseline for 3d human pose estimation

【论文】https://arxiv.org/abs/1705.03098v2 【pytorch】(本文代码参考)weigq/3d_pose_baseline_pytorch: A simple baseline for 3d human pose estimation in PyTorch. (github.com) 【tensorflow】https://github.com/una-dinosauria/3d-pose-baseline 基本上算作是2d人体…

Python压缩JS文件,PythonWeb程序员必看系列,重点是 slimit

Python 压缩文件系列文章&#xff0c;我们已经完成了 2 篇&#xff0c;具体如下&#xff1a; Python Flask 实现 HTML 文件压缩&#xff0c;9 级压缩 Python 压缩 css 文件&#xff0c;第三方模块推荐 压缩JS学习目录&#x1f6a9; jsmin 库&#x1f3a8; 库的安装&#x1f3a8…

HackTheBox Stocker API滥用,CVE-2020-24815获取用户shell,目录遍历提权

靶机地址&#xff1a; https://app.hackthebox.com/machines/Stocker枚举 使用nmap枚举靶机 nmap -sC -sV 10.10.11.196机子开放了22&#xff0c;80端口&#xff0c;我们本地解析一下这个域名 echo "10.10.11.196 stocker.htb" >> /etc/hosts 去浏览器访问…

操作系统真相还原_第5章第4节:特权级

文章目录特权级TSS简介CPL和DPL入门处理器提供的从低特权级到高特权级的方法门、调用门和RPL序特权级 保护模式下特权级按照权力大小分为0、1、2、3级 0特权级是操作系统内核所在的的特权级 TSS简介 TSS&#xff0c;即Task State Segment&#xff0c;意为任务状态段&#x…