数据结构之二叉树的超详细讲解(3)--(二叉树的遍历和操作)

news2024/10/6 0:33:31

 个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

数据结构之二叉树的超详细讲解(3)--(二叉树的遍历和操作)

收录于专栏【数据结构初阶
本专栏旨在分享学习数据结构学习的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1.前置说明

2.二叉树的遍历

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

代码实现: 

2.1.1前序遍历:

 2.1.2中序遍历:

2.1.3后序遍历:

力扣oj练习

2.2 层序遍历

代码实现: 

练习:

3.结点个数以及高度等

3.1二叉树节点个数

3.2二叉树叶子节点个数

3.3二叉树第k层节点个数

3.4二叉树查找值为x的节点

3.5二叉树的高度

4.二叉树的创建和销毁

4.1通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树

4.2二叉树销毁

4.3判断二叉树是否时完全二叉树


1.前置说明

如果没看树和二叉树的结构和概念建议先去看看

----数据结构之二叉树的超详细讲解(1)--(树和二叉树的概念和结构)-CSDN博客

---数据结构之二叉树的超详细讲解(2)--(堆的概念和结构的实现,堆排序和堆排序的应用)-CSDN博客

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树 操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

二叉树的结构表示:

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

手动创造一棵树:

BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);


	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	node5->right = node7;

	return node1;
}

 将创造树节点的方法封装成一个函数(BuyNode):

BTNode* BuyNode(int x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

这样我们就完成了一棵树的创建,如下图所示:

注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。

再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:

1. 空树

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

从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。

2.二叉树的遍历

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

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的结点进行相应的操作,并且每个结点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

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

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。

2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。

3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为 根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。 

示例:

假如我们的二叉树为:

前序遍历:

前序遍历即访问根结点的操作发生在遍历其左右子树之前。也就是先访问根节点在访问左右子树,简称为根左右

所以前序遍历为: 1 2 3 NULL NULL NULL 4 5 NULL 6 NULL NULL

中序遍历:

中序遍历即访问左节点的操作发生在遍历其根节点和右节点之前,也就是先访问左节点在访问根右,简称左根右

所以中序遍历为:NULL 3 NULL 2 NULL 1 NULL 5 NULL 4  NULL 6 NULL

后序遍历:

后序遍历即先访问左节点,在访问右节点和根节点,简称左右根

所以后序遍历为:NULL NULL 3 NULL 2 NULL NULL 5 NULL NULL 6 4 1

代码实现: 

我们之前已经手动创建了一棵树:

2.1.1前序遍历:

void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

 我们这里的逻辑为:首先判断节点是否为空,为空即返回NULL(这里N就代表为NULL),并return,注意:这里的return并不是直接退出,而是返回函数上一层,不为空即直接打印节点data值,然后递归左右子树,满足根左右的性质.

结果如下:

递归展开图:

这里怕宝子们还是不理解递归的原理,这里就展示前序的递归展开图,中序和后序是一样的

 由于篇幅有限,没有完全展示,但是原理都是一样的

 2.1.2中序遍历:

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

 我们这里的逻辑和前序方法差不多,也是使用递归,满足左根右的性质进行后序遍历

结果如下:

2.1.3后序遍历:

void postorder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	postorder(root->left);
	postorder(root->right);
	printf("%d ", root->data);
}

结果如下:

力扣oj练习

--144. 二叉树的前序遍历 - 力扣(LeetCode)

--94. 二叉树的中序遍历 - 力扣(LeetCode)

--145. 二叉树的后序遍历 - 力扣(LeetCode)

只要能把上面的知识看懂弄明白,这三个题目能很轻松的做出来.

2.2 层序遍历

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

 上图层序遍历的结果为:A B C D E F G H I

代码实现: 

void TreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		printf("%d ", front->data);

		if (front->left)
			QueuePush(&q, front->left);

		if (front->right)
			QueuePush(&q, front->right);
	}

	QueueDestroy(&q);
}

具体步骤:

  1. 初始化队列

    初始化一个队列并将根节点入队。如果根节点为空,则直接跳过。

  2. 层序遍历

    使用队列进行层序遍历。每次取出队列中的节点,打印节点的值,并将该节点的左右子节点分别入队。

  3. 清理资源

    销毁队列,释放资源。

这里需要用到队列的知识,使其能一层一层的遍历二叉树,对队列还不清楚的宝子们,可以看看下面的博客--数据结构之队列的超详细讲解-CSDN博客 

练习:

1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为(A )
A ABDHECFG
B ABCDEFGH
C HDBEAFCG
D HDEBFGCA
2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK; 中序遍历:HFIEJKG.则二叉树根结点为(A)
A E
B F
C G
D H
3.设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为__D__。
A adbce
B decab
C debac
D abcde
4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列A
A FEDCBA
B CBAFED
C DEFCBA
D ABCDEF

解答:

1. 完全二叉树按层次输出的序列为 ABCDEFGH ,该完全二叉树的前序序列为(A)

根据完全二叉树的定义,给出的层次遍历序列 ABCDEFGH 可以直接构建出二叉树,其中:

  • 根节点是 A
  • 第二层是 B 和 C
  • 第三层是 D, E, F, G
  • 第四层是 H

从这个结构我们可以推导前序遍历:

  • 前序遍历是根 -> 左子树 -> 右子树
  • 根是 A
  • 左子树的前序遍历是 B -> D -> H -> E
  • 右子树的前序遍历是 C -> F -> G

因此,前序遍历是:ABDHECFG

答案是 A:ABDHECFG

2. 二叉树的先序遍历和中序遍历如下:

先序遍历:EFHIGJK; 中序遍历:HFIEJKG

从先序遍历可以看出根结点是 E。根据中序遍历,E 左边的部分是左子树,右边的部分是右子树:

  • 中序遍历分割为: 左子树 HFI 和右子树 EJKG
  • 按照先序遍历的顺序,E 后面的字符分别属于左子树和右子树:
    • 左子树的先序遍历是 FHI
    • 右子树的先序遍历是 JKG

所以,根结点是 E。

答案是 A:E

3. 二叉树的中序遍历序列:badce,后序遍历序列:bdeca

从后序遍历可以看出根结点是 a。根据中序遍历,a 左边的是左子树,右边的是右子树:

  • 中序遍历分割为: 左子树 b 和右子树 dce
  • 后序遍历的顺序,左子树的顺序是 b,右子树的顺序是 dec

前序遍历是根 -> 左子树 -> 右子树:abcde

答案是 D:abcde

4. 二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF

这意味着每个节点只有一个子节点,并且这些节点按顺序排列,如果它们按层次输出,那么最深的节点将排在最后。

如果后序遍历和中序遍历相同:

  • 最右边的节点(即最后一个节点)是树的根节点
  • 其余节点依次按照层次遍历的顺序向左添加

按层次遍历从根开始,从层次的最右边开始,因此应该是 F -> E -> D -> C -> B -> A

答案是 A:FEDCBA

3.结点个数以及高度等

3.1二叉树节点个数

这里大家很容易写出错误代码,比如:

int TreeSize(BTNode* root)
{
	int size = 0;
	if (root == NULL)
		return 0;
	else
		++size;

	TreeSize(root->left);
	TreeSize(root->right);

	return size;
}

这段代码看起来非常好理解,相当于进行了一次前序遍历,将遍历的节点进行返回.

在这个函数中,size 是一个局部变量,每次调用 TreeSize 时都会重新初始化为 0。让我们逐步分析一下这个函数的行为。

分析:

  1. root == NULL 时,函数直接返回 0。这是树为空的情况。

  2. 如果 root 不为 NULL

    • int size = 0; 初始化 size 为 0。
    • ++size; 将 size 增加 1,所以 size 现在为 1。
  3. 然后递归调用 TreeSize(root->left)TreeSize(root->right),但是这些调用的返回值并没有被使用或累加到 size 上。

  4. 因此,无论左右子树中有多少节点,这些递归调用的结果都不会影响当前函数中的 size 变量。每次递归调用都会在自己的栈帧中初始化一个新的 size 变量,并且最终返回 1 或 0。

函数递归展开图:

用static进行改进: 

int TreeSize(BTNode* root)
{
	static int size = 0;
	if (root == NULL)
		return 0;
	else
		++size;

	TreeSize(root->left);
	TreeSize(root->right);

	return size;
}

static 变量在整个程序运行期间只会被初始化一次,这样就避免了局部变量一直的初始化 

结果如下:

 结果是没有问题的,可是当我们多次调用呢?

这里我重复调用三次:

结果如下:

正是因为 static 变量在整个程序运行期间只会被初始化一次,所以如果你多次用 TreeSize 函数,size 会一直累积之前的计算结果,而不会重新开始计算新的树的大小。

正确代码:

int TreeSize(BTNode* root)
{
	return root == NULL ? 0 :
		TreeSize(root->left) + TreeSize(root->right) + 1;
}

为了修正这些问题,可以将递归调用的结果累加到 size 中,并将最终的 size 值作为函数的返回值。 

函数递归展开图:

 

这段代码利用递归的方式计算二叉树的节点总数,通过递归遍历所有节点,实现了对整个二叉树的全面统计。递归的基准情况是遇到空节点返回 0,而在非空节点情况下,递归地计算左右子树的大小并进行累加,从而获得整棵树的节点总数。

3.2二叉树叶子节点个数

补充一下什么是叶子节点:

没有子树的节点叫做叶子节点,也称度为0的节点

代码展示:

int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;

	if (root->left == NULL && root->right == NULL)
		return 1;

	return TreeLeafSize(root->left)
		+ TreeLeafSize(root->right);
}

分析

  1. 基准情况

    • 如果 root 为 NULL,则树为空,没有叶节点。因此,返回 0。
  2. 叶节点情况

    • 如果 root 的左子节点和右子节点都为 NULL,那么当前节点为叶节点。因此,返回 1。
  3. 递归情况

    • 如果当前节点不是叶节点,则递归计算其左、右子树中的叶节点数量,并将它们加起来。
    • 通过递归调用 TreeLeafSize(root->left) 和 TreeLeafSize(root->right) 来分别计算左、右子树中的叶节点数量,并将结果相加。

递归展开图:

 

 结果如下:

结果为3,没有问题 

3.3二叉树第k层节点个数

代码展示:

int TreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;

	if (k == 1)
		return 1;

	// 子问题
	return TreeLevelKSize(root->left, k - 1)
		+ TreeLevelKSize(root->right, k - 1);
}

 函数递归展开图:

分析

  1. 基础情况处理

    • if (root == NULL) return 0;:如果当前节点 root 为空,则返回 0。这是递归的终止条件之一,表示空节点没有子节点,因此在任何层级上都没有节点。

    • if (k == 1) return 1;:当 k 等于 1 时,表示已经递归到目标层级。根据题设,每个节点自身被视为一个层级,因此返回 1。

  2. 递归调用

    return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);如果当前节点 root 不为空且 k > 1,则递归调用 TreeLevelKSize 函数来计算左子树和右子树中第 k-1 层级的节点数目。最终返回的是左子树第 k-1 层级节点数目和右子树第 k-1 层级节点数目的和。

3.4二叉树查找值为x的节点

代码展示:

BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

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

	BTNode* ret1 = TreeFind(root->left, x);
	if (ret1)
		return ret1;

	BTNode* ret2 = TreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;
}

函数递归展开图:

分析

  1. 基础情况处理

    • if (root == NULL) return NULL;:如果当前节点 root 为空,则直接返回 NULL,表示未找到目标节点。

    • if (root->data == x) return root;:如果当前节点的值等于 x,则返回当前节点 root,表示已找到目标节点。

  2. 递归查找

    • BTNode* ret1 = TreeFind(root->left, x);

      • 递归在左子树中查找值为 x 的节点。如果找到了(即 ret1 不为 NULL),直接返回 ret1
    • BTNode* ret2 = TreeFind(root->right, x);

      • 如果左子树中未找到目标节点,继续递归在右子树中查找值为 x 的节点。如果找到了(即 ret2 不为 NULL),直接返回 ret2
  3. 返回结果

    • 如果在当前节点及其左右子树中都未找到值为 x 的节点,则返回 NULL,表示整棵树中没有该节点。

3.5二叉树的高度

代码一:

int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	return TreeHeight(root->left) > TreeHeight(root->right) ?
		TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
}

分析

  1. 基准情况

    • 如果 root 为 NULL,则树为空,高度为 0。因此,返回 0。
  2. 递归情况

    • 如果当前节点不为空,则递归计算其左、右子树的高度,并返回较大的那个值加上 1。
    • 通过递归调用 TreeHeight(root->left) 和 TreeHeight(root->right) 来分别计算左、右子树的高度。
    • 使用条件运算符 ? : 来比较左、右子树的高度,并将较大的那个值加上 1 返回。

这个解法是没有问题的,但是它的时间效率不高,它在递归过程中有的值会重复计算多次

函数递归展开图:

我们可以看到,我们每次计算出左右子树的高度后,并不会进行记录,导致我们比较完成后还需要进行递归计算,导致效率不高

代码二:

int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ?
		leftHeight + 1 : rightHeight + 1;
}

 函数递归展开图:

可以发现我们用两个变量存储之后,每次我们都只需要一次,避免了重复计算,提高了时间效率 

这里大家可以试一试,在力扣上面右一道计a算二叉树的深度,如果你使用第一种代码,只通过了35个例子,超时了,因为后面的例子数据庞大,如果你使用第一种方案,最低层的值会被重复计算很多次,导致超出时间限制

而我们用第二种方法,能很顺利的通过

下面是原题链接:

--LCR 175. 计算二叉树的深度 - 力扣(LeetCode) 

4.二叉树的创建和销毁

4.1通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树

代码展示:

BTNode* CreateTree(char* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->data = a[(*pi)++];
	root->left = CreateTree(a, pi);
	root->right = CreateTree(a, pi);
	return root;
}

函数递归展开图:

 

函数分析:

  1. 参数

    • char* a:存储先序遍历序列的字符数组,包含节点的数据和 '#' 表示的空节点。
    • int* pi:指向当前处理的字符在数组 a 中的索引的指针。
  2. 返回值

    • BTNode*:返回值是指向根节点 BTNode 结构体的指针。
  3. 递归过程

    • 如果当前字符是 '#',表示遇到空节点,函数会返回 NULL,并且将指针 pi 向后移动一位。
    • 如果当前字符不是 '#',则创建一个新的节点,将当前字符作为节点的数据,并将指针 pi 向后移动一位。
    • 然后递归调用 CreateTree 函数来构建左子树和右子树,分别将它们赋给当前节点的 left 和 right 指针。
  4. 返回根节点

    • 最后返回根节点的指针。

4.2二叉树销毁

代码展示:

// 二叉树销毁
void TreeDestory(BTNode* root)
{
	if (root == NULL)
		return;

	TreeDestory(root->left);
	TreeDestory(root->right);
	free(root);
}

函数递归展开图:

函数分析:

  1. 检查根节点是否为空

    • 如果传入的根节点 root 为 NULL,说明树为空或者已经到达叶子节点的子节点(即空节点),直接返回,不进行任何操作。
  2. 递归销毁左子树

    • 调用 TreeDestory(root->left) 递归地销毁左子树。此调用会一直递归到最左子节点,然后逐级返回并销毁每个节点。
  3. 递归销毁右子树

    • 调用 TreeDestory(root->right) 递归地销毁右子树。此调用会一直递归到最右子节点,然后逐级返回并销毁每个节点。
  4. 释放当前节点的内存

    • 在左右子树都被销毁后,最后释放当前节点的内存 free(root)

4.3判断二叉树是否时完全二叉树

代码展示:

// 判断二叉树是否是完全二叉树
bool TreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 遇到第一个空,就可以开始判断,如果队列中还有非空,就不是完全二叉树
		if (front == NULL)
		{
			break;
		}

		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 如果有非空,就不是完全二叉树
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}

具体步骤:

  1. 初始化队列

    初始化一个队列并将根节点入队。如果根节点为空,则直接跳过。

  2. 层序遍历

    使用队列进行层序遍历。每次取出队列中的节点,如果遇到空节点(NULL),则退出该循环。否则,将该节点的左右子节点分别入队。

  3. 检测后续节点

    继续检查队列中剩余的节点。如果存在非空节点,则树不是完全二叉树,返回 false。否则,继续检查直到队列为空。

  4. 清理与返回

    销毁队列,释放资源,并返回 true 表示该二叉树是完全二叉树。

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

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

相关文章

论文阅读--Efficient Hybrid Zoom using Camera Fusion on Mobile Phones

这是谷歌影像团队 2023 年发表在 Siggraph Asia 上的一篇文章,主要介绍的是利用多摄融合的思路进行变焦。 单反相机因为卓越的硬件性能,可以非常方便的实现光学变焦。不过目前的智能手机,受制于物理空间的限制,还不能做到像单反一…

《洛基第二季》:多元宇宙的奇幻之旅

在浩瀚无垠的影视宇宙中,有一部剧如璀璨星辰般耀眼,那就是备受期待的《洛基第二季》。该剧由贾斯汀本森和艾伦穆尔黑德执导,汤姆希德勒斯顿、索菲娅迪马蒂诺、欧文威尔逊等主演,于2023年10月上线。 这部剧延续了第一季的故事情节&…

关于 pnpm monorepo 项目中 TS2742 Error 的 workaround

最近我在使用 pnpm 作为包管理器开发一个 monorepo 项目,从个人体验来说,在 monorepo 项目中,pnpm 确实要比 yarn classic 用得舒心,最让我欣喜的是 pnpm 对 workspace 协议的支持度很好;另外感受比较明显的一点就是&a…

《Windows API每日一练》5.4 键盘消息和字符集

本节我们将通过实例来说明不同国家的语言、字符集和字体之间的差异,以及Windows系统是如何处理的。 本节必须掌握的知识点: 第31练:显示键盘消息 非英语键盘问题 字符集和字体 第32练:显示默认字体信息 第33练:创建逻…

软考高级论文真题“论大数据lambda架构”

论文真题 大数据处理架构是专门用于处理和分析巨量复杂数据集的软件架构。它通常包括数据收集、存储、处理、分析和可视化等多个层面,旨在从海量、多样化的数据中提取有价值的信息。Lambda架构是大数据平台里最成熟、最稳定的架构,它是一种将批处理和流…

静态文件及模板

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 1 静态文件 动态Web应用也会需要静态文件,通常是CSS和JavaScript文件。Flask可以向已经配置好的Web服务器提供静态文件,只要…

AI大模型企业应用实战(18)-“消灭”LLM幻觉的利器 - RAG介绍

大模型在一定程度上去改变了我们生活生工作的思考的方式,然后也越来越多的个人还有企业在思考如何将大模型去应用到更加实际的呃生产生活中去,希望大语言模型能够呃有一些更多企业级别生产落地的实践,然后去帮助我们解决一些业务上的问题。目…

大模型续写评测

环境准备 通过remote-ssh连接云AI服务器,配置文件如下,供参考 # Read more about SSH config files: https://linux.die.net/man/5/ssh_config Host deepseekv1HostName rootconnect.bjc1.seetacloud.comUser rootPort 52984Password xxx # 密码从申请…

国产数据库 - 内核特性 - CloudberryDB中的Runtime Filter

国产数据库 - 内核特性 - CloudberryDB中的Runtime Filter 今年5月份GreenPlum官方将GitHub仓库代码全部删除,各个分支的issues和bugs讨论等信息全部清除,仅将master分支代码进行归档。对于国内应用GPDB的用户来说,这是一个挑战性事件&#x…

JWT介绍及其基本使用

JWT介绍及其基本使用 官网:https://jwt.io/ 什么是JWT 全称:JSON Web Token(JSON Web令牌) 一个开放标准(RFC 7519) ,它定义了一种紧凑和自包含的方式, 用于作为 JSON 对象在各方之间安全地传输信息。此信…

捷云等保一体机 产品服务一站式等保合规交付解决方案

等保2.0的变化 2019 年 5 月 13 日,网络安全等级保护制度 2.0 国家标准(简称“等保 2.0”)正式发布,将等保 2.0 基本要求、测评要求、安全设计技术要求框架统一为安全管理中心支持下的三重防护结构框架。定级对象在按照等保 2.0 …

任务5.1 初识Spark Streaming

实战概述:使用Spark Streaming进行词频统计 1. 项目背景与目标 背景: Spark Streaming是Apache Spark的流处理框架,用于构建可伸缩、高吞吐量的实时数据处理应用。目标: 实现一个实时词频统计系统,能够处理流式数据并统计文本中的单词出现频…

DP:二维费用背包问题+似包非包

二维费用的背包问题&#xff1a;大多以01背包为基础&#xff0c;存在两个限制条件&#xff01; 一、一和零 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public: //需要满足两个条件的我们称之为二位费用的背包问题int findMaxForm(vector<string>&…

在 IntelliJ IDEA 中使用 Java 和 Selenium 模拟 Chrome 浏览器教程

在 IntelliJ IDEA 中使用 Java 和 Selenium 模拟 Chrome 浏览器教程 1. 前言2. 环境准备3. 关闭谷歌自动更新通过服务禁用更新服务通过任务计划程序禁用更新任务 4. 项目添加 Maven 依赖项5. 编写自动化脚本6. 项目运行效果7. 代码示例8.常用方法示例页面请求定位标签获取内容操…

Python低溫半导体电子束量子波算法计算

&#x1f3af;要点 &#x1f3af;任意维度求解器&#xff0c;绘制三维投影结果 | &#x1f3af;解二维静电场、静磁场 | &#x1f3af;狄利克雷、诺依曼条件几何矩阵算子 | &#x1f3af;算法模拟低溫半导体材料 | &#x1f3af;计算曲面达西流 | &#x1f3af;电子结构计算和…

学习VXLAN -- 报文结构、原理和配置

目录 VXLAN背景什么是VXLANVXLAN的优势VXLAN报文结构一些特定名词BDVBDIFVAPVSIVSI-InterfaceAC VXLAN的实现原理图VXLAN MAC地址表项MAC地址动态学习 VXLAN隧道VXLAN隧道工作模式L2 GatewayIP Gateway VXLAN隧道的建立与关联VXLAN隧道建立的方式VXLAN对到与VXLAN关联的方式 配…

系统思考与创新解决

圆满结束了为期两天的《系统思考》课程。在这次学习中&#xff0c;大家积极使用系统环路图来分析并呈现跨部门的业务协同问题&#xff0c;以及探讨了推动成长环路背后的限制因素。这不仅增强了团队之间的理解和合作&#xff0c;也帮助我们一起识别阻碍组织发展的关键挑战。期待…

obsidian中用check list 打造待办清单

背景 在快节奏的现代生活中&#xff0c;有效管理个人时间和任务成为提升生活与工作效率的关键。 Obsidian&#xff0c;作为一款强大的知识管理和笔记应用&#xff0c;通过其丰富的插件生态&#xff0c;为我们提供了高度自定义的任务管理解决方案。本文旨在详细介绍如何在Obsid…

AI应用带你玩系列之SadTalker

前段时间我刷微信视频&#xff0c;我无意间点开了一个&#xff0c;画面缓缓展开&#xff0c;是一幅精致的水墨画&#xff0c;画中人物皆是古代装束&#xff0c;衣袂飘飘&#xff0c;仿佛能闻到墨香。然而&#xff0c;这宁静的画面突然被打破了&#xff0c;画中的人物开始动了起…

自动驾驶仿真Carla -ACC功能测试

我将详细说明如何使用Carla进行ACC&#xff08;自适应巡航控制&#xff09;测试&#xff0c;确保每个步骤贴合实际的Carla自动驾驶仿真标准&#xff0c;并提供相应的代码示例。 使用Carla进行ACC测试的步骤&#xff1a; 1. 环境设置和启动Carla 首先&#xff0c;确保你已经安装…