数据结构 —— 二叉树

1.树的概念及结构

1.1树的概念

树是一种非线性的数据结构,它有着多分支,层次性的特点。

由于其形态类似于自然界中倒过来的数,所以我们将这种数据结构称为“树形结构”

注意: 树形结构中,子树之间不能有交集,否它就不是树形结构

 

1.2 树的相关概念

  • 结点的度:一个结点含有的子树的个数称为该结点的度; 如上图:A的为6
  • 叶结点或终端结点:度为0的结点称为叶结点; 如上图:BCHI...等结点为叶结点
  • 非终端结点或分支结点:度不为0的结点; 如上图:DEFG...等结点为分支结点
  • 双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:AB的父结点
  • 孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:BA的孩子结点
  • 兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:BC是兄弟结点
  • 树的度:一棵树中,最大的结点的度称为树的度; 如上图:树的度为6
  • 结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推;
  • 树的高度或深度:树中结点的最大层次; 如上图:树的高度为4
  • 堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:HI互为兄弟结点
  • 结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先
  • 子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙
  • 森林:由mm>0)棵互不相交的树的集合称为森林;

1.3树的表示 

树的表示我们使用:孩子兄弟表示法

设计一个数的节点,其中包含数据域(存储数据)、指针域(左孩子指针,右兄弟指针)

typedef int DataType;
struct Node
{
 struct Node* firstChild1; // 第一个孩子结点
 struct Node* pNextBrother; // 指向其下一个兄弟结点
 DataType data; // 结点中的数据域
};

这种数的设计方法,我们可以通过左孩子指针找到 A节点 的第一个孩子(B),在通过孩子的右兄弟指针把 A节点 的所有孩子都找到

1.4 树在实际中的运用

树在实际中的运用:电脑中的数目录

2.二叉树的概念及结构 

2.1二叉树的概念 

在实际运用中,二叉树要比树更加实用

二叉树其实就是特殊的一种树,它的每个节点最多有两个子节点,通常被称为左子节点和右子节点

  •  二叉树不存在度大于2的结点
  •  二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

2.2现实中的二叉树

 2.3特殊的二叉树

 

  •  满二叉树:二叉树的每一层都是满的(特殊的完全二叉树)
  • 完全二叉树:二叉树的最后一层不一定是满的,但是它是连续的

像下面这个二叉树,最后一层并不连续,因此它并非是完全二叉树:

2.4二叉树的性质

2.5二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构
1. 顺序存储
顺序结构存储就是使用 数组来存储 ,一般使用数组 只适合表示完全二叉树 ,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储
二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树
2. 链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。
通常的方法是:链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,目前我们使用二叉链的新式学习
二叉链式实现的二叉树:

3.二叉树的顺序存储结构 

顺序存储结构只推荐完全叉树来进行存储,一般的二叉树容使用顺序结构进行存储,容易造成空间的大量浪费,现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段

堆在这篇文章中有所介绍 ———— 数据结构 - 堆

4.二叉树的链式结构的实现

在进行二叉树链式结构的实现时,我们首先回顾二叉树是:

1. 空树
2. 非空:根结点,根结点的左子树、根结点的右子树组成的

每一颗二叉树都可以看做是递归形成的因为:

每一颗二叉树都可以拆分成:根节点 左子树 右子树

它的左子树可以被拆分成 :根节点 左子树 右子树

它的右子树右也可以被拆分成 :根节点 左子树 右子树

依次类推直到变成一颗空树,不能被拆分,所以才会说二叉树可以看做是递归形成,二叉树可以被拆分成一个一个的小问题(即一个一个的子树 根节点),直到变成空树不能再被拆分,因此后序基本操作中基本都是按照递归概念实现的

4.1二叉树的前置声明

typedef int BTDataType;

typedef struct BinaryTreeNode    //二叉树的单个节点
{
 BTDataType _data;
 struct BinaryTreeNode* _left;   //左孩子
 struct BinaryTreeNode* _right;  //右孩子
}BTNode;

4.2二叉树的遍历

4.2.1前序、中序以及后序遍历

二叉树的遍历是指按照某种规则访问树中的所有节点,并且每个节点只被访问一次。访问结点所做的操作依赖于具体的应用问题。遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历 

1. 前序遍历 (Preorder Traversal )—— 访问顺序:根节点 —>左子树 —>右子树
2. 中序遍历(Inorder Traversal)——访问顺序:左子树 —>根节点 —>右子树
3. 后序遍历(Postorder Traversal)——访问顺序:左子树 —>右子树—>根节
前序遍历递归图解

前序遍历结果: 1 2 3 4 5 6
中序遍历结果: 3 2 1 5 4 6
后序遍历结果: 3 2 5 6 4 1
前中后序遍历的方式大抵相同,这里不做过多赘述
代码实现:
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root) {

	if (root == NULL)  //当访问的数为NULL树时停止访问
	{
		printf("N ");
		return;
	}

	printf("%d ",root->_data);//先便利根节点,整形的数据类型

	BinaryTreePrevOrder(root->_left);//左子树
	BinaryTreePrevOrder(root->_right);//右子树

}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root) {

	if (root == NULL)
	{
		printf("N ");
		return;
	}

	BinaryTreeInOrder(root->_left);//左子树
	printf("%d ", root->_data);//根节点
	BinaryTreeInOrder(root->_right);//右子树
}

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root) {
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	BinaryTreePostOrder(root->_left);//左子树
	BinaryTreePostOrder(root->_right);//右子树
	printf("%d ", root->_data);//根节点
}

4.2.2层序遍历 

层序遍历 :除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根结点所在层数为1 ,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第 2 层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

 代码实现:

二叉树的层序遍历并不是通过递归来完成的,而是通过 —— 数据结构中的队列来实现的

遍历的原理是从根节点开始,首先访问根节点,然后将根节点的左右子节点依次入队。接下来,从队列中取出一个节点(队首节点),访问该节点,再将其未被访问的左右子节点入队。重复此过程,直到队列为空,即所有节点都被访问过。

动图理解:

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
	//创建队列
	Queue qu;
	QueueInit(&qu);
	QueuePush(&qu, root);

	//开始拖家带口,当队列为NULL时,说明已经遍历完成,循环结束
	while (!QueueEmpty(&qu))
	{
		//先访问队头的元素
		BTNode* bt = QueueFront(&qu);//获取队头元素
		printf("%d ", bt->_data);

		//将树的左右孩子都带入队列中,NULL孩子除外
		if (bt->_left)
			QueuePush(&qu, bt->_left);
		if (bt->_right)
			QueuePush(&qu, bt->_right);

        //队头数据处队列
		QueuePop(&qu);
	}
    
    //销毁队列
	QueueDestroy(&qu);

}

5.二叉树总代码

队列的相关功能:

Queue.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef struct BinaryTreeNode* QDataType;   //队列中的元素是树的节点
// 链式结构:表示队列 
typedef struct QListNode
{
	struct QListNode* _next;
	QDataType _data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
	int size;
}Queue;

// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

// 初始化队列 
void QueueInit(Queue* q) {
	assert(q);

	q->size = 0;
	q->_front = NULL;
	q->_rear = NULL;
}
// 队尾入队列 
void QueuePush(Queue* q, QDataType data) {
	assert(q);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("QueuePush()::malloc()");
		return;
	}

	newnode->_data = data;
	newnode->_next = NULL;
	
	//队列为NULL
	if (q->_front == NULL)
	{
		q->_front = q->_rear = newnode;
	}
	else
	{
		q->_rear->_next = newnode;
		q->_rear = q->_rear->_next;
	}

	q->size++;
}
// 队头出队列 
void QueuePop(Queue* q) {
	assert(q);
	assert(q->size != 0);

	//单个节点
	if (q->_front == q->_rear)
	{
		free(q->_front);
		q->_front = q->_rear = NULL;
	}
	//多个节点
	else
	{
		QNode* next = q->_front->_next;
		free(q->_front);
		q->_front = next;
	}

	q->size--;
}
// 获取队列头部元素 
QDataType QueueFront(Queue* q) {

	assert(q);
	assert(q->_front);//队头不能为NULL

	return q->_front->_data;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* q) {
	assert(q);
	assert(q->_rear);//队尾不能为NULL

	return q->_rear->_data;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* q) {

	return q->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q) {
	assert(q);

	return q->size == 0;
}
// 销毁队列 
void QueueDestroy(Queue* q) {
	assert(q);

	QNode* cur = q->_front;
	while (cur)
	{
		QNode* next = cur->_next;
		free(cur);
		cur = next;
	}

	q->_front = q->_rear = NULL;
	q->size = 0;

	//这个应该留给用户去释放
	/*free(q);
	q = NULL;*/
}

二叉树相关功能:

BinaryTree.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"Queue.h"
typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;


// 二叉树销毁
void BinaryTreeDestory(BTNode** root);

// 二叉树节点个数
int BinaryTreeSize(BTNode* root);

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

BTNode* CreatBinaryTree();

BinaryTree.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"BinaryTree.h"
BTNode* BuyNode(BTDataType x) {

	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("Buynode()::malloc()");
		return newnode;
	}

	newnode->_data = x;
	newnode->_left = NULL;
	newnode->_right = NULL;
	return newnode;
}

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root) {

	if (root == NULL)  //当访问的数为NULL树时停止访问
	{
		printf("N ");
		return;
	}

	printf("%d ",root->_data);//先便利根节点,整形的数据类型

	BinaryTreePrevOrder(root->_left);//左子树
	BinaryTreePrevOrder(root->_right);//右子树

}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root) {

	if (root == NULL)
	{
		printf("N ");
		return;
	}

	BinaryTreeInOrder(root->_left);//左子树
	printf("%d ", root->_data);//根节点
	BinaryTreeInOrder(root->_right);//右子树
}

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root) {
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	BinaryTreePostOrder(root->_left);//左子树
	BinaryTreePostOrder(root->_right);//右子树
	printf("%d ", root->_data);//根节点
}

//求二叉树的高度
int maxDepth(BTNode* root) {

	if (root == NULL)   //如果为空树则返回 0 
	{
		return 0;
	}

	int lefthigh = maxDepth(root->_left);    //记录树的左子树高度
	int righthigh = maxDepth(root->_right); //记录树的右子树高度

    //左子树高则返回左子树的高度    右子树高则返回右子树高度
	return lefthigh > righthigh ? lefthigh + 1 : righthigh + 1; 

}

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root) {

	if (root == NULL)  //如果为空树则返回 0 
		return 0;

	if (root->_left == NULL && root->_right == NULL)  //如果是叶子节点就返回 1 
		return 1;

    //返回左子树 与 右子树总共的叶子节点
	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k) {

    //将找第k层问题转化成:层序遍历按照树的层次进行遍历,每次遍历一层,直到遍历到第k层或者遍历完整个树。   

	if (root == NULL)    //如果为空树则返回 0 
		return 0;

	if (root != NULL && k == 1) //当不为空且k为1时,到达所找层,返回1
		return 1;
	
    //一层一层的往下找
	if (root != NULL && k > 1)
		return BinaryTreeLevelKSize(root->_left,k - 1) 
        + BinaryTreeLevelKSize(root->_right,k - 1);
}

// 二叉树查找值为x的节点
//向这种找值的,一定要记录,把记录的递归返回
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {

	if (root == NULL)
		return NULL;

	if (root->_data == x)
		return root;

	BTNode* find1 = NULL;
	BTNode* find2 = NULL;

	
	find1 = BinaryTreeFind(root->_left, x);  //记录所找的节点

	if (find1)//如果左边找到了就不用去右边找了
		return find1;

	find2 = BinaryTreeFind(root->_right, x);
		return find2;
}

// 二叉树销毁
void BinaryTreeDestory(BTNode** root) {

	if (*root == NULL)
		return;
	BinaryTreeDestory(&((*root)->_left));//先销毁左子树
	BinaryTreeDestory(&((*root)->_right));//在销毁右子树
	free(*root);
	*root = NULL;
}

// 二叉树节点个数
int BinaryTreeSize(BTNode* root) {
	if (root == NULL)
		return 0;

	return BinaryTreeSize(root->_left)+ BinaryTreeSize(root->_right) + 1;
}

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
	//创建队列
	Queue qu;
	QueueInit(&qu);
	QueuePush(&qu, root);

	//开始拖家带口,当队列为NULL时,说明已经遍历完成,循环结束
	while (!QueueEmpty(&qu))
	{
		//先访问队头的元素
		BTNode* bt = QueueFront(&qu);//获取队头元素

		printf("%d ", bt->_data);

		//将树的左右孩子都带入队列中
		if (bt->_left)
			QueuePush(&qu, bt->_left);
		if (bt->_right)
			QueuePush(&qu, bt->_right);

		QueuePop(&qu);
	}

	QueueDestroy(&qu);


}

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root) {

	//创建队列
	Queue qu;
	QueueInit(&qu);
	QueuePush(&qu, root);

	//开始拖家带口,当队列为NULL时,说明已经遍历完成,循环结束
	while (!QueueEmpty(&qu))
	{
		//队列中存的数据是,树节点的指针,我们先访问队头的元素
		BTNode* bt = QueueFront(&qu);//获取队头元素

		if (bt == NULL)
		{
			break;
		}


		//将树的左右孩子都带入队列中,NULL也不例外
		QueuePush(&qu, bt->_left);
		QueuePush(&qu, bt->_right);

		QueuePop(&qu);
	}

	while (!QueueEmpty(&qu))
	{
		BTNode* bt = QueueFront(&qu);//获取队头元素

		//如果在遇到非空的节点,说明它不是一个完全二叉树返回false
		if (bt)
		{
			return false;
		}

		QueuePop(&qu);
	}

	QueueDestroy(&qu);

	return true;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SwiftUI 中 Grid 内多个 NavigationLink 同时发生导航之诡异问题的解决

问题现象 不知小伙伴们发现了没有?在 SwiftUI 中如果有多个 NavigationLink 视图嵌入在 Grid(包括 LazyVGrid 和 LazyHGrid)容器中,点击其中任意一个 NavigationLink 都会导致所有导航一起发生。 如上图所示,点击 Grid 中任何一个 NavigationLink,所有 NavigationLink 都…

[数据结构] --- 树

1 树的基本概念 1.1 树的定义 树是n(n>0)个结点的有限集。当 n 0 时&#xff0c;称为空树。在任意一棵树非空树中应满足&#xff1a; (1) 有且仅有一个特定的称为根 (root) 的结点&#xff1b; (2) 当 n > 1 时&#xff0c;其余结点可分为m(m>0)个互不相交的有限集…

bootloader原理介绍

bootloader解析 bootloader的引出 不知道你有没有想过这样一个问题&#xff0c;当你按下电源开关的那一瞬间&#xff0c;第一行代码是如何在芯片上运行起来的呢&#xff1f;我们都知道嵌入式软件代码&#xff0c;是需要通过一定的方式&#xff0c;烧录在硬件芯片中&#xff0c…

【Zotero】【国标csl调教(七)】导入专著M、学位论文D以及百度学术的问题

一、百度学术 百度学术导入的字段&#xff08;期卷号、页码&#xff09;等会有严重错误&#xff0c;不建议通过Baidu Scholar导入&#xff0c; 建议在文库编目字段自行查找修改 二、专著【M】以及学位论文【D】的出版地问题 国标对硕博论文【D】和专著【M】要求写上出版地 …

OpenSSL的一些使用案例

目录 一、介绍 二、基本使用 1、Shell &#xff08;1&#xff09;文件加解密 &#xff08;2&#xff09;生成密钥文件 2、API &#xff08;1&#xff09;md5sum &#xff08;2&#xff09;AES256加解密 一、介绍 本篇博客重点不是详细描述 OpenSSL 的用法&#xff0c;只…

昇思第7天

模型训练 模型训练一般分为四个步骤&#xff1a; 构建数据集。 定义神经网络模型。 定义超参、损失函数及优化器。 输入数据集进行训练与评估。 数据集加载 import mindspore from mindspore import nn # 从 MindSpore 数据集包中导入 vision 和 transforms 模块。 # visio…

使用DC/AC电源模块时需要注意的事项

BOSHIDA 使用DC/AC电源模块时需要注意的事项 1. 仔细阅读和理解产品说明书&#xff1a;在使用DC/AC电源模块之前&#xff0c;应该仔细阅读和理解产品说明书&#xff0c;了解其性能特点、技术要求和使用方法&#xff0c;以确保正确使用和避免潜在的安全风险。 2. 选择适当的电…

MySQL 9.0 发布了!

从昨晚开始&#xff0c;在DBA群里大家就在讨论MySQL 9.0发布的事情&#xff0c;但是Release Note和官方文档都没有更新&#xff0c;所以今天早上一上班就赶紧瞅了下具体更新了哪些内容&#xff1f; 整体看来&#xff0c;基本没什么创新。下面是9.0新增或废弃的一些特性。 &…

Power Platform功能管理实战概述

Power Platform功能管理实战概述 Microsoft Power Platform是一个强大的低代码开发平台&#xff0c;它使组织能够自动化商业流程、开发自定义应用程序&#xff0c;并加强与客户的连接。该平台由四个主要组件组成&#xff1a;Power Apps、Power Automate、Power BI和Power Virt…

【探索Linux】P.36(传输层 —— TCP协议段格式)

阅读导航 引言一、TCP段的基本格式二、控制位详细介绍三、16位接收窗口大小⭕窗口大小的作用⭕窗口大小的限制⭕窗口缩放选项⭕窗口大小的更新⭕窗口大小与拥塞控制 四、紧急指针温馨提示 引言 在上一篇文章中&#xff0c;我们深入探讨了一种无连接的UDP协议&#xff0c;它以其…

Searchsploit漏洞利用搜索工具的介绍及使用

目录 0x00 介绍0x01 常用参数0x02 使用1. 在线搜索2. 使用步骤3. 使用实例 0x00 介绍 kali自带的&#xff0c;Searchsploit会通过本地的Exploit-db查找软件漏洞信息。 Exploit Database&#xff08;https://gitlab.com/exploit-database/exploitdb&#xff09;存储了大量的漏洞…

33.哀家要长脑子了!

憋说了&#xff0c;感觉好不容易长出来的脑子又缩回去了。。。 1.539. 最小时间差 - 力扣&#xff08;LeetCode&#xff09; 把所有时间排好序&#xff0c;然后计算两两之间的分钟差就好&#xff0c;但是要注意加上最后一个和第一个的判断&#xff0c;因为这个时间是按字典序来…

AI研究的主要推动力会是什么?ChatGPT团队研究科学家:算力成本下降

AI 研究发展的主要推动力是什么&#xff1f;在最近的一次演讲中&#xff0c;OpenAI 研究科学家 Hyung Won Chung 给出了自己的答案。 近日&#xff0c;斯坦福大学《CS25: Transformers United V4》课程迎来了一位我们熟悉的技术牛人&#xff1a;Hyung Won Chung。 Chung 是 O…

Hadoop-03-Hadoop集群 免密登录 超详细 3节点公网云 分发脚本 踩坑笔记 SSH免密 服务互通 集群搭建 开启ROOT

章节内容 上一节完成&#xff1a; HDFS集群XML的配置MapReduce集群XML的配置Yarn集群XML的配置统一权限DNS统一配置 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭建过一次&…

Spring容器生命周期中如前置运行程序和后置运行程序

在Spring容器加入一个实现了BeanPostProcessor接口bean实例&#xff0c;重写postProcessBeforeInitialization、postProcessAfterInitialization方法&#xff0c;在方法里面写具体的实现&#xff0c;从而达到Spring容器在初如化前或销毁时执行预定的程序&#xff0c;方法如下&a…

深入浅出:npm常用命令详解与实践【保姆级教程】

大家好,我是CodeQi! 在我刚开始学习前端开发的时候,有一件事情让我特别头疼:管理和安装各种各样的依赖包。 那时候,我还不知道 npm 的存在,手动下载和管理这些库简直是噩梦。 后来,我终于接触到了 npm(Node Package Manager),它不仅帮我解决了依赖管理问题,还让我…

解决Visual Studio 一直弹出管理员身份运行问题(win10/11解决办法)

不知道大家是否有遇到这个问题 解决办法也很简单 找到启动文件 如果是快捷方式就继续打开文件位置 找到这个程序启动项 右键 选择 兼容性疑难解答&#xff08;win11 则需要 按住 shift 右键&#xff09; win10 解决办法 这样操作完后就可以了 win11解决办法按以下选择就行

深入理解策略梯度算法

策略梯度&#xff08;Policy Gradient&#xff09;算法是强化学习中的一种重要方法&#xff0c;通过优化策略以获得最大回报。本文将详细介绍策略梯度算法的基本原理&#xff0c;推导其数学公式&#xff0c;并提供具体的例子来指导其实现。 策略梯度算法的基本概念 在强化学习…

AI大模型时代来临:企业如何抢占先机?

AI大模型时代来临:企业如何抢占先机? 2023年,被誉为大模型元年,AI大模型的发展如同一股不可阻挡的潮流,正迅速改变着我们的工作和生活方式。从金融到医疗,从教育到制造业,AI大模型正以其强大的生成能力和智能分析,重塑着行业的未来。 智能化:企业核心能力的转变 企…