【数据结构】堆和二叉树(2)

文章目录

  • 前言
  • 一、建堆和堆排序
    • 1.堆排序
  • 二、二叉树链式结构的实现
    • 1.二叉树的遍历
  • 三、链式二叉树的功能函数
    • 1.二叉树结点个数
    • 2.二叉树叶子结点个数
    • 3.二叉树的高度
    • 4.二叉树第k层结点个数
    • 5. 二叉树查找值为x的结点
    • 6.二叉树销毁
  • 总结


前言

接着上一篇博客,我们继续分享关于堆和二叉树的知识,这里小编会跟大家分享关于如何建堆,堆排序,二叉树的链式结构以及相关知识。
在这里插入图片描述


一、建堆和堆排序

如果我们这里想要实现堆排序,那么我们就需要一个堆,在堆的基础之上实现排序。所以这里就需要建堆了。那这里就有一个问题了。
如果是升序的话我们该建大堆还是小堆呢?降序又该建大堆还是小堆呢?
在这里插入图片描述
这里我们可以得出结论了嘛!降序建小堆,升序建大堆

1.堆排序

1.建堆
跟之前的堆的插入一样,但是这样有个弊端就是,每插入一个数据就要申请一个空间呀。大大地减低了代码的运行效率。那我们是数组的话那是不是太浪费时间了呀,还要一个个的插入。那有没有一种方法就是在不开辟新的空间的前提下让数组里面的数据有序呀?这里就要用到排序了呀。可以用冒号,选择。但是这两种的效率是不是很低呀?时间复杂度是O(N)^2。欧克,今天我们就要引入一个新的排序方法:堆排序。既然是堆排序,那是不是就需要一个堆呀?那怎样建堆呢?有两种建堆方式:

//   向上建堆
void HeapSort(int* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		AdjustUp(arr, i);
	}
	int main()
{
	int arr[] = { 2,4,5,6,7 };
	HeapSort(arr, sizeof(arr) / sizeof(int));
	return 0;
}
//向下建堆 
void HeapSort(int* arr, int n)
{
	for (int i = (n-1-1)/2; i >=0; i--)
	{
		AdjustDowan(arr, n,i);
	}
}
		int main()
{
	int arr[] = { 2,4,5,6,7 };
	HeapSort(arr, sizeof(arr) / sizeof(int));
	return 0;
}

这里需要注意的是。向下调整是从最后一个父亲节点开始依次向下调整的。拿为什么不是从最后一个数据开始呢?因为最后一个数据已经有序了呀!那还需要调整吗?
这样建堆的话是不是就避免了要重新开辟一个新的空间呀?在原数组之上直接建堆。
2.向上调整建堆和向下调整建堆

//                            向上调整建堆
void AdjustUp(int* arr, int chiled)
{
	int parent = (chiled - 1) / 2;
	while (chiled > 0)
	{
		if (arr[chiled] < arr[parent])
		{
			Swap(&arr[chiled], &arr[parent]);
			chiled = parent;
			parent = (chiled - 1) / 2; 
		}
		else
		{
			break;
		}
	}
}
//                            向下调整建堆
void AdjustDown(int* arr, int n, int parent)
{
	int chiled = 2 * parent + 1;
	while (chiled < n)
	{
		if (chiled + 1 < n && arr[chiled + 1] < arr[chiled])
		{
			++chiled;
		}
		if (arr[parent] >arr[chiled])
		{
			Swap(&arr[parent], &arr[chiled]);
			parent = chiled;
			chiled = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

这个就是向上和向下调整建堆,这个就是我们之前的堆的插入和堆删除那里的向上调整建堆是一样的。如果不知道的可以取看一下小编之前讲堆的那个章节:https://editor.csdn.net/md/articleId=142728291

void Swap(int* p1, int* p2)
{
	int* tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustUp(int* arr, int chiled)
{
	int parent = (chiled - 1) / 2;
	while (chiled > 0)
	{
		if (arr[chiled] < arr[parent])
		{
			Swap(&arr[chiled], &arr[parent]);
			chiled = parent;
			parent = (chiled - 1) / 2; 
		}
		else
		{
			break;
		}
	}
}
void AdjustDown(int* arr, int n, int parent)
{
	int chiled = 2 * parent + 1;
	while (chiled < n)
	{
		if (chiled + 1 < n && arr[chiled + 1] < arr[chiled])
		{
			++chiled;
		}
		if (arr[parent] >arr[chiled])
		{
			Swap(&arr[parent], &arr[chiled]);
			parent = chiled;
			chiled = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		AdjustUp(arr, i);
	}
	int end = n - 1;//最后一个数据
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, end, 0);
		--end; 
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[] = { 2,4,5,6,7 };
	HeapSort(arr, sizeof(arr) / sizeof(int));
	return 0;
}

大家可以看出我们我们这个代码则是一个建小堆的过程,建小堆是不是降序呀?我们来来看看这个代码的运行结果:
在这里插入图片描述
那如果是升序的话,只需要把向上和向下调整建堆哪里改一下就欧克了,改成大堆就行了,这回该知道为什么:降序建小堆,升序建大堆
以上就是关于建堆和堆排序的相关知识了。接下来我们来看看二叉树链式结构的实现

二、二叉树链式结构的实现

为了方便学习链式二叉树的知识,那我们必须要有一棵树呀!这里呢为了方便好理解,我这里呢就直接手搓一颗树用来分享:
在这里插入图片描述
这下面是他的代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
BTNode* BuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newnode->data = x;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}
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);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}
int main()
{
	CreatBinaryTree();
	return 0;
}

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

1.二叉树的遍历

二叉树的遍历呢分为前序遍历,中序遍历,后序遍历按照规则,每种遍历的顺序是不一样的。

前序遍历:根—>左子树—>右子树
中序遍历:左子树—>根—>右子树
后序遍历:左子树—>右子树—>根
我们拿第一种来说,他的意思就是先访问根节点,然后在访问左子树,最后才右子树
在这里插入图片描述
首先我们先访问的是1,然后在访问1节点的左子树,1的左子树不为空,所以访问2,在访问2的左子树,2的左子树不为空,所以访问3,然后在访问3的左子树,3的左子树为空,返回到3,在访问3的右子树,3的右子树为空 ,返回到3,然后3访问完了就返回到2,在访问2的右子树。右子树为空,返回到2,2也访问完了返回到1,1的左子树全部访问完毕,接下来就访问右子树4,4的左子树不为空,所以访问5,在访问5的左子树,5的左子树为空,返回到5,在访问5的右子树,右子树为空返回到5,5访问完了在返回到4,在访问4的右子树,右子树不位空,所以访问6,在访问6的左子树,左子树为空,返回到6,在访问6的右子树,右子树为空,返回到6,6已经被访问完,返回到4,4的已经被访问完,在返回到1,1也被访问完了,所以跳出。
注意:不管怎样访问,我们都把二叉树分为根,左子树和右子树三部分。
一般的情况从逻辑的角度的方面来说,逻辑上我们就是将一棵树的前序遍历分为根的访问和左右子树的遍历。左右子树的遍历我们又可以看成一棵树的前序遍历。所以这里要用到了递归。

代码实现:我们现在已经了解前序遍历。那我们要怎样才能实现代码呢?

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

由于前序,中序,后序遍历大同小异,所以我这里就全部写出来了。
在这里插入图片描述
层序遍历:
从字面意思来理解就是一层一层的遍历嘛。确实,它的确是一层一层的遍历。首先访问第一层的根节点,然后在从左到右访问第二层,这样依次类推。那样怎样代码实现,这里就要借助我们之前学的队列了 。利用队列先进先出的规则来实现。
在这里插入图片描述

void TreeLevelOrder(BTNode* p)//层序遍历
{
	Queue pq;
	QueueInit(&pq);//初始化队列
	if(p)
	   QueuePush(&pq, p);//第一层入队列
	while (!QueueEmpty(&pq))//队列不为空
	{
		BTNode* head = QueueFron(&pq);//取出队头数据
		QueuePop(&pq);//出队列
		printf("%d ", head->a);//访问
		if (head->left)
		{
			QueuePush(&pq, head->left);//左孩子入队列
		}
		if (head->right)
		{
			QueuePush(&pq, head->right);//右孩子入队列
		}
	}
	QueueDestroy(&pq);//销毁队列
}

这里需要注意的是:我们这里存储的是结点指针,因为我们要访问左右子树。

三、链式二叉树的功能函数

1.二叉树结点个数

想要算出二叉树的个数,大多数情况我们会想到在遍历的时候用一个变量来表示节点的个数:就像这样:

int TreeSize(BTNode* root)
{
	int size = 0;
	if (root == NULL)
	{
		return 0;
	}
	size++;
	TreeSize(root->left); 
	TreeSize(root->right);
	return size;
}
int main()
{
	BTNode *root= CreatBinaryTree();
	printf("节点的个数:");
	int ret = TreeSize(root); 
	printf("%d", ret);
}

在这里插入图片描述
但是这里的结果却是1呀?为什么呢?其实呀?这里的size是一个局部变量,每次调用呢都会被置为0,所以后面最多只能自加一次,所以结果是1,那我们把这里改为静态的使他的生命周期变为全局变量呢?

int TreeSize(BTNode* root)
{
	static int size = 0;
	if (root == NULL)
	{
		return 0;
	}
	size++;
	TreeSize(root->left);
	TreeSize(root->right);
	return size;
}
int main()
{
	int size = 0;
	BTNode *root= CreatBinaryTree();
	printf("节点的个数:");
	int ret = TreeSize(root); 
	printf("%d", ret);
	printf("\n");
	printf("节点的个数:"); 
	int ret1 = TreeSize(root);
	printf("%d", ret1);
}

在这里插入图片描述
这里呢我们可以看见第一次呢是没有问题的,但是第二次调用为什么就是12了呢?因为这里的局部的静态变量只会初始化一次,但你第二次调用的是它初始化的值是上一次调用的结果。所以这里就是6+6=12了。那要这样解决呢?这里我们只需要在第二次调用之前重新把size置为0就行了。

int size=0;
int TreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	size++;
	TreeSize(root->left);
	TreeSize(root->right);
	return size;
}
int main()
{
	BTNode *root= CreatBinaryTree();
	printf("节点的个数:%d\n",TreeSize(root));
	size = 0;
	printf("节点的个数:%d", TreeSize(root));
	printf("\n");
}

在这里插入图片描述
当然还有一种就是传一个形参,从外边传一个变量的形参过来。

int  TreeSaze(BTNode*root,int* k)
{
	if (root == NULL)
	{
		return 0;
	}
	(*k)++;;
	TreeSaze(root->left,k);   
	TreeSaze(root->right,k);   
	return (*k);
}

这里的返回值其实要不要都无所谓的,返回也行,不返回也行。
这里呢以上的就是算二叉树节点的代码了,其实还有一种更简单的思路:分治递归
我们把二叉树的所有的节点都看成左子树+右子树+根
在这里插入图片描述
我们结合代码来看。

int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return  BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
int main()
{

	BTNode* root = CreatBinaryTree();
	printf("节点的个数是:");
	printf("%d\n", BinaryTreeSize(root));
	return 0;
}

这样子是不是就简单多了呀?只是这个比较难理解。不过只要你熟练掌握递归就可以很轻松的理解了。

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); 
}

这里要注意的是要考虑如果树是空树的情况

3.二叉树的高度

想要算出二叉树的高度,是不是要算出左右子树的高度呀?然后选择高度最高的来加上根的高度呀?
要是是一个空树的话,那就返回0。

int TreeHight(BTNode* root)//高度
{

	if (root == NULL)
	{
		return 0;
	}
	return TreeHight(root->left)> TreeHight(root->right) ? 
	TreeHight(root->left)+1: TreeHight(root->right)+1;
}
int main()
{
			
	BTNode* root = CreatBinaryTree();
	printf("节点的个数是:");
	printf("%d\n", TreeHight(root));
	return 0;
}

这样可不可以呢?由于我们这里给的数据太少了,所以代码就能跑过。但是这里存在一个效率问题。
在这里插入图片描述

int TreeHight(BTNode* root)//高度
{

	if (root == NULL)
	{
		return 0;
	}
	int LeftHight = TreeHight(root->left);
	int RightHight = TreeHight(root->right);
	return  LeftHight > RightHight ? LeftHight + 1: RightHight+1;
}
int main()
{
			
	BTNode* root = CreatBinaryTree();
	printf("节点的个数是:");
	printf("%d\n", TreeHight(root));
	return 0;
}

像这样把访问的数据储存起来。

4.二叉树第k层结点个数

在这里插入图片描述

int TreeLeveKsize(BTNode* root,int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return TreeLeveKsize(root->left,k-1) + TreeLeveKsize(root->right,k-1);
}

5. 二叉树查找值为x的结点

查找x的节点其实是一个看似很简单。实则不简单的一个代码,思路好理解,但是要写出正确的代码就得要仔细思考了

BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL; 
	if (root->data == x)
		return root ;
	TreeFind(root->left,x);
	TreeFind(root->right, x);
}

来看看这个代码是不是正确的。其实这里有两个问题,第一个就是如果我找到要找的数据。那我是不是要返回上一个栈帧呀,但是这里数据没有被接收呀。那我们返回的是什么?就不知道了是吧。第二呢就是。如果在左子树已经找到了,那是不是就不需要再找了呀?那万一右子树也有一个要找的数据怎么办?这时候直接返回第一次找到的数据就ok,原因就是函数返回值只能返回一个。不能返回多个数据。
那正确的代码就是:

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); 
	{ 
		return ret2;
	}
	return NULL;
}
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;
	}
	return  TreeFind(root->right, x);
	
}

这两种代码都是可以的。

6.二叉树销毁

二叉树的销毁跟其他的销毁有点不一样哦。这里要用到后序遍历来销毁。那为什么要用后序遍历呢而不用前序遍历和中序遍历呢?这里其实都可以。但是这个时候我们就要一个变量来储存左孩子和有孩子(前序遍历销毁)。如果直接释放掉根节点,不然就找不到根的左右孩子了

void BinaryTreeDestory1(BTNode* root)//前序遍历销毁
{
	
	if (root == NULL)
			return;
	BTNode* newnode = root->left;
    BTNode* newnode1 = root->right;
	free(root);
	BinaryTreeDestory1(newnode);
	BinaryTreeDestory1(newnode1);
	//root=NULL;
}
void BinaryTreeDestory2(BTNode* root)//中序遍历销毁
{

	if (root == NULL)
		return;
	BTNode* newnode1 = root->right;
	BinaryTreeDestory2(root->left );
	free(root);
	BinaryTreeDestory2(newnode1);
	//root=NULL;
}
void BinaryTreeDestory(BTNode*root)//后序遍历销毁
{
	if (root == NULL)
		return;
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right );
	free(root);
	//root=NULL;
}
int main()
{
	BTNode* root = CreatBinaryTree();
	BinaryTreeDestory(root);
	root = NULL;
	return 0;
}

那我们这里在释放后要不要把root置为空呢?这里置不置空都行,因为传的是一级指针呀。形参的改变不会改变外边的实参。所以这里可以置可不置。正常使用呢是在主函数哪里当我要用的时候在置为空


总结

今天的分享就到这里吧。再见咯,各位!
在这里插入图片描述

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

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

相关文章

Ubuntu24.04网络异常与应对方案记录

PS: 参加过408改卷的ZJU ghsongzju.edu.cn 开启嘲讽: 你们知道408有多简单吗&#xff0c;操作系统真实水平自己知道就行&#xff5e;&#xff5e; Requested credits of master in UWSC30&#xff0c;in ZJU24&#xff0c;domestic master is too simple dmesg dmesg 是一个用…

就是这个样的粗爆,手搓一个计算器:弧长计算器

作为程序员&#xff0c;没有合适的工具&#xff0c;就得手搓一个&#xff0c;PC端&#xff0c;移动端均可适用。废话不多说&#xff0c;直接上代码。 HTML: <div class"calculator"><label for"radius">圆的半径 (r)&#xff1a;</label&…

ServletContext介绍

文章目录 1、ServletContext对象介绍1_方法介绍2_用例分析 2、ServletContainerInitializer1_整体结构2_工作原理3_使用案例 3、Spring案例源码分析1_注册DispatcherServlet2_注册配置类3_SpringServletContainerInitializer 4_总结 ServletContext 表示上下文对象&#xff0c;…

【论文复现】MSA+抑郁症模型总结(三)

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀MSA抑郁症模型 热门研究领域&#xff1a;情感计算的横向发展1. 概述2. 论文地址3. 研究背景4. 主要贡献5. 模型结构和代码6. 数据集介绍7. 性…

使用 Umami 部署博客分析工具

Umami 简介 Umami 是一款开源且注重隐私的网站分析工具&#xff0c;可替代 Google Analytics。它提供网站流量和用户行为等见解&#xff0c;但不使用 Cookie 或收集个人数据&#xff0c;符合隐私法规。Umami 轻巧易用&#xff0c;可自行托管。 如果你有自己的博客&#xff0c;…

JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习

先简单记录下简单使用跟测试&#xff0c;后续再补充具体&#xff0c;最近有用到&#xff0c;简单来说就是后端(服务端)编写个发射器&#xff0c;实现一次请求&#xff0c;一直向前端客户端发射数据&#xff0c;直到发射器执行完毕&#xff0c;模拟ai一句一句回复的效果 Respon…

在IntelliJ IDEA中创建带子模块的SpringBoot工程

前言 在项目开发中&#xff0c;一个工程往往有若干子工程或模块&#xff0c;所以主工程一般是一个容器&#xff0c;本文介绍在IntelliJ IDEA中创建带多模块的SpringBoot工程的详细步骤。 1、首先打开IntellJ IDEA&#xff08;以下简称IDEA&#xff09;,创建一个新项目。假定新…

【LeetCode】每日一题 2024_11_9 设计相邻元素求和服务(构造,哈希)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 题目&#xff1a;设计相邻元素求和服务 近几天不知道力扣发什么疯&#xff0c;每日一题出的太抽象了&#xff0c;我题解是写不了一点了 . . . 今天稍微正常了些&#xff0c;就又来更新了~ 代码与解题思路…

如何搭建企业内部知识库?:打造专属智能体,为企业提供高效智能的知识管理

在当今数据爆炸的时代&#xff0c;虽然AI强大&#xff0c;但常规的AI工具或搜索引擎在面对复杂、专业领域的问题时&#xff0c;可能给出模棱两可的回应&#xff0c;无法满足企业精细化的需求。这就是为什么&#xff0c;企业需要一个专属的AI知识库 —— 它不仅能存储你的数据&a…

胶囊网络、MobileNet、坐标注意力机制的应用

文章目录 摘要Abstract1.胶囊网络1.1 动态路由 2.坐标注意力机制3.MobileNet3.1 深度卷积3.2 逐点卷积3.3 深度可分离卷积3.3.1 深度可分离卷积与标准卷积的对比3.3.2 卷积操作的代码实现3.3.2.1 函数原型3.3.2.2 标准卷积3.3.2.3 分组卷积3.3.2.4 深度可分离卷积 3.4 MobileNe…

一个开源、免费(MIT License)、功能强大、可扩展的电动汽车充电控制器和家庭能源管理系统(带私活源码)

项目介绍 evcc是一个开源、免费&#xff08;MIT License&#xff09;、功能强大、可扩展的电动汽车充电控制器和家庭能源管理系统&#xff0c;支持广泛的设备和品牌&#xff0c;提供简单的用户界面和强大的功能。 功能特点 用户界面&#xff1a;简单且清晰的用户界面。 充电器…

Pr 视频过渡:沉浸式视频

效果面板/视频过渡/沉浸式视频 Video Transitions/Immersive Video Adobe Premiere Pro 的视频过渡效果中&#xff0c;沉浸式视频 Immersive Video效果组主要用于 VR 视频剪辑之间的过渡。 自动 VR 属性 Auto VR Properties是所有 VR 视频过渡效果的通用选项。 默认勾选&#x…

[OpenGL]使用OpenGL实现硬阴影效果

一、简介 本文介绍了如何使用OpenGL实现硬阴影效果&#xff0c;并在最后给出了全部的代码。本文基于[OpenGL]渲染Shadow Map&#xff0c;实现硬阴影的流程如下&#xff1a; 首先&#xff0c;以光源为视角&#xff0c;渲染场景的深度图&#xff0c;将light space中的深度图存储…

成都睿明智科技有限公司抖音电商服务效果如何?

在这个短视频风起云涌的时代&#xff0c;抖音电商以其独特的魅力&#xff0c;成为了众多商家竞相追逐的新蓝海。而在这片波澜壮阔的商海中&#xff0c;成都睿明智科技有限公司犹如一艘稳健的航船&#xff0c;引领着无数企业驶向成功的彼岸。今天&#xff0c;就让我们一起揭开成…

uniapp 实现瀑布流

效果演示 组件下载 瀑布流布局-waterfall - DCloud 插件市场

集合进阶(JAVA笔记第二十九期)

p.s.这是萌新自己自学总结的笔记&#xff0c;如果想学习得更透彻的话还是请去看大佬的讲解 集合基础看这里 目录 集合体系结构单列集合Collection各个方法的注意事项add()remove()contains() Collection三种遍历方式迭代器遍历增强for遍历lambda表达式遍历匿名内部类遍历 Lis…

使用LlamaIndex框架构建RAG应用的基础实践指南

前言 上一篇文章[检索增强生成 Retrieval-Augmented Generation]介绍了什么是 RAG &#xff0c;并详细对比了和大模型微调方式的区别。 目前实现 RAG 的主流框架就是 [LangChain] 和 [LlamaIndex]&#xff0c;LangChain 更适合需要复杂对话流程、上下文管理、以及多步骤任务的…

小程序开发进阶之路-AI编程助手

之前&#xff0c;我独自一人开发了一个名为“心情追忆”的小程序&#xff0c;旨在帮助用户记录日常的心情变化及重要时刻。从项目的构思、设计、前端&#xff08;小程序&#xff09;开发、后端搭建到最终部署&#xff0c;所有环节都由我一人包办。经过一个月的努力&#xff0c;…

Typora导出pdf手动分页和设置字体样式

手动分页 <div style"page-break-after: always;"></div>鼠标点击代码才会显示&#xff0c;不点击会隐藏。导出pdf时&#xff0c;该位置会分页 设置字体大小、加粗、居中、空格 <p style"font-size:30px; font-weight: bold; text-align: cen…

【GCN】 代码详解 (1) 如何运行【pytorch】可运行版本

Graph Convolutional Networks 代码详解 前言0.引言1.环境配置2. 代码的运行2.1 报错处理2.2 运行结果 3.总结 前言 在前文中&#xff0c;已经对图卷积神经网络&#xff08;Graph Convolutional Neural Networks, GCN&#xff09;的理论基础进行了深入探讨。接下来的章节将会进…