日撸Java三百行(day24:二叉树的建立)

news2024/12/24 21:35:32

目录

一、分析准备

二、代码实现

1.方法创建

2.数据测试

3.完整的程序代码

总结


一、分析准备

在日撸Java三百行(day22:二叉树的存储)中,我们学习的是如何将链表二叉树转换为顺序表二叉树进行存储,而今天我们要学习的是逆过程,即通过两个顺序表(包括数据顺序表和下标顺序表)建立链表二叉树。

根据链表的特性,要想建立一个链表二叉树,需要我们创建结点,然后再把所有结点链接起来。显然,在这个过程中,最重要的就是根据结点之间的逻辑关系正确进行链接,所以,我们先来看看树的结点之间的联系。

像下图这种完全二叉树,它们有一个非常良好的性质,就是左子树的下标=其根结点下标 * 2 + 1,右子树下标=其根结点 * 2 + 2。例如:结点b的下标为1,其根结点a的下标为0,二者满足0 * 2 + 1 = 1;结点e的下标为4,其根结点b的下标为1,二者满足1 * 2 + 2 = 4。

而对于非完全二叉树,我们同样可以采用完全二叉树的方式进行编号,即先把这个非完全二叉树转换为对应的完全二叉树,然后再依次编号,这样非完全二叉树的根结点与左右子树的下标关系仍满足上述规律。例如:下图中的结点d下标为4,其左子树f下标为9,二者满足4 * 2 + 1 = 9,其右子树g下标为10,二者满足4 * 2 + 2 = 10。

综上所述,我们得出结论:如果结点B的下标=结点A的下标 * 2 + 1,那么B就是A的左子树;如果结点C的下标=结点A的下标 * 2 + 2,那么C就是A的右子树。

此外,还需要注意以下几点:

  • 在二叉树中,随着结点从上往下、从左往右,其下标是逐渐变大的
  • 对于任何结点,其前驱结点的下标都比它自身的下标小,也就是说不管是在二叉树中还是在顺序表中,前驱结点的位置都在当前结点之前
  • 顺序表中的第一个结点总是二叉树的根结点,其是没有父结点的

二、代码实现

有了上述结论后,我们就可以开始代码模拟了,大体的思路是:创建一个结点顺序表存放所有结点,然后遍历该顺序表,在遍历的过程中,枚举当前遍历结点的所有前驱结点,看它们的下标是否满足我们总结出来的结论,若满足,则说明找到了当前结点的根结点,直接进行链接即可。

1.方法创建

    /**
	 *********************
	 * 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 if
				if(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

根据之前的分析,我们需要创建一个方法,以达到输入两个顺序表(数据顺序表和下标顺序表)得到一个链表二叉树的目的,大体步骤如下:

第一步,我们创建一个顺序表tempAllNodes用来存放所有的结点。由于tempAllNodes存放的是结点,而结点是二叉树类BinaryCharTree的对象,因此我们创建的这个tempAllNodes其实就是二叉树类BinaryCharTree的对象的集合,所以定义时使用BinaryCharTree修饰;同时tempAllNodes的最大长度,其实就等于输入的数据顺序表paraDataArray的长度。然后,利用一个for循环,将数据顺序表paraDataArray中的数据元素(即字符),一个一个拷贝到结点顺序表tempAllNodes中。

第二步,链接所有结点。创建一个两层for循环,外层for循环用来遍历整个结点顺序表(我们之前说过顺序表中的第一个结点是二叉树的根结点,所以此处 i 是从1开始的),内层for循环用于枚举当前遍历结点的所有前驱结点(由于前驱结点的位置始终在当前结点之前,所以这里 j < i )。然后我们利用之前总结的结论进行判断,若满足“ * 2 + 1”的关系,则链接为左子树;若满足“ * 2 + 2”的关系,则链接为右子树。

第三步,将根结点(即顺序表tempAllNodes的第一个结点)赋给二叉树类BinaryCharTree的成员变量。

至此,我们就完成了“通过两个顺序表建立一个链表二叉树”的方法创建。

2.数据测试

接着,我们开始数据测试,并通过前序遍历、中序遍历、后序遍历进行检验,如下:

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

3.完整的程序代码

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 if
		
		if(rightChild != null) {
			rightChild.preOrderVisit();
		} // Of if
	} // Of preOrderVisit
	
	/**
	 *********************
	 * In-order visit.
	 *********************
	 */
	public void inOrderVisit() {
		if(leftChild != null) {
			leftChild.inOrderVisit();
		} // Of if
		
		System.out.print("" + value + " ");
		
		if(rightChild != null) {
			rightChild.inOrderVisit();
		} // Of if
	} // Of inOrderVisit
	
	/**
	 *********************
	 * Post-order visit.
	 *********************
	 */
	public void postOrderVisit() {
		if(leftChild != null) {
			leftChild.postOrderVisit();
		} // Of if
		
		if(rightChild != null) {
			rightChild.postOrderVisit();
		} // Of if
		
		System.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 if
		
		if(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 if

			if (tempTree.rightChild != null) {
				tempQueue.enqueue(tempTree.rightChild);
				tempIntQueue.enqueue(tempIndex * 2 + 2);
			} // Of i
			tempTree = (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 if
			if (tempTree.leftChild != null) {
				tempQueue.enqueue(tempTree.leftChild);
				tempIntQueue.enqueue(Integer.valueOf(tempIndex * 2 + 2));
			} // Of if
			
			tempTree = (BinaryCharTree) tempQueue.dequeue();
			if (tempTree == null) {
			     break;
			} // Of if
			
			tempIndex = ((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 if
				if(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

	/**
	 *********************
	 * 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();
	}// Of main	
} // Of class BinaryCharTree

运行结果

总结

总的来说,今天的代码还是比较简单的,只在之前代码的基础上增加了一个构造方法和相应的数据测试。在具体实施过程中,我觉得最难的就是分清楚下标顺序表的真实索引值和下标顺序表中存放的值(下标顺序表中存放的值,即为结点按照完全二叉树模式进行的编号下标)。

通过今天的学习,我们其实可以发现,不同数据结构之间可以进行灵活的转换,而现实世界中的各种逻辑结构又总是非常复杂,所以当以后我们想要用数据结构去灵活地表示某种复杂的逻辑时,就可以考虑进行可逆的转换。

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

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

相关文章

厂家揭秘:劳保鞋里的防砸黑科技,这些材料你了解多少?

在工业生产的前沿阵地&#xff0c;安全生产始终是企业发展的基石&#xff0c;也是每一位劳动者的头等大事。在繁忙的生产线上&#xff0c;一双看似普通的劳保鞋&#xff0c;实则蕴含着保护我们双脚免受意外伤害的重要科技——防砸材料。今天&#xff0c;百华小编就来和大家盘点…

大模型RAG企业级项目实战:ChatDoc与文件聊天交互完整版代码

大模型RAG企业级项目实战&#xff1a;手把手带你搭建一套属于你的RAG系统 完整的 RAG 应用流程主要包含两个阶段&#xff1a; 数据准备阶段&#xff1a;&#xff08;1&#xff09;数据提取–> &#xff08;2&#xff09;分块&#xff08;Chunking&#xff09;–> &…

五、1 算数操作符位操作符

1、算数操作符 &#xff08;1&#xff09;“ / ”得到一个整数商&#xff0c;不包含余数 进行浮点数除法&#xff0c;操作符两端至少有一个浮点数 &#xff08;2&#xff09;“ % ”取模&#xff0c;得到余数 两端必须为整数 2、位操作符&#xff08;只适用于整型&#xff…

Linux 中的同步机制——rwsem

代码基于&#xff1a;Kernel 6.6 0. 前言 读写信号量的原理与读写锁类似&#xff0c;读写信号量归根到底是 “信号量”&#xff0c;读写锁归根到底是 “自旋锁”&#xff0c;而信号量与自旋锁的区别一个可以睡眠&#xff0c;一个只能自旋。 读写信号量原理&#xff1a; 允许…

Spring Data JPA 自动创建时间的相关注解和用法

以Springboot项目为例 在实体类上加上注解 EntityListeners(AuditingEntityListener.class)在相应的字段上添加对应的时间注解 LastModifiedDate 和 CreatedDateApplication启动类中添加注解 EnableJpaAuditing

动环监控是什么?没有它机房可能发生宕机风险!

在大数据时代&#xff0c;计算机和服务器管理着大量的数据&#xff0c;无论是十几平米的小机房&#xff0c;还是几千平米的大机房&#xff0c;一旦宕机&#xff0c;就可能造成最大损失。因此&#xff0c;利用动环监控系统去进行实时监测和统一管理&#xff0c;就成为了非常重要…

【Linux】Linux软件包管理器 yum 和 Linux编辑器vim

目录 1. Linux 软件包管理器 yum1.1 什么是软件包1.2 windows 机器和远端的 Linux 机器互传文件1.3 查看软件包1.4 安装软件1.5 卸载软件1.6 yum源 2. Linux编辑器-vim2.1 vim三大模式2.2 vim正常模式命令集2.2 vim底行模式命令集2.3 多文件 3. 简单vim配置3.1 配置文件的位置 …

YOLOv5 目标检测算法

简介&#xff1a; 目标检测在生活中应用领域非常广泛&#xff0c;列如&#xff1a;道路违规抓拍、未戴口罩识别、工地未佩戴安全帽抓拍、厨房出现老鼠检测。 还可以用在游戏辅助外挂。以枪战为例&#xff0c;在游戏过程中时刻检测有没有人头出现。当检测到目标人头&#xff0c;…

a标签下载文件重命名(download)不生效

项目场景&#xff1a; 移动端使用 a 标签下载文件 问题描述 下载的文件使用 download 重命名不生效 APP 中接收数据代码&#xff1a; const link document.createElement(a) // 创建a标签link.style.display none // 使其隐藏link.href http://192.168.103.1:8080/factory/v…

操作系统内存管理技术详解

操作系统内存管理技术详解&#xff1a;第一部分 引言 操作系统作为计算机系统的核心组件&#xff0c;负责管理硬件资源、提供用户接口和运行应用程序。在操作系统的众多功能中&#xff0c;内存管理无疑是最为关键的技术之一。本文将深入探讨操作系统内存管理的背后技术&…

如何选择图片和视频

文章目录 1. 概念介绍2. 方法与细节2.1 实现方法2.2 具体细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何选择视频文件"相关的内容&#xff0c;本章回中将介绍如何混合选择图片和视频文件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我…

Android逆向题解-Illusion-难度6

jeb反编译 s 是用户输入&#xff0c;s1 是加密后flag&#xff1a; KuG_V9v(yGS 传入Native函数 CheckFlag IDA分析Native函数 注意这里有一个陷阱&#xff0c;CheckFlag有静态注册函数和动态注册函数&#xff0c;会执行动态注册的函数。 a2 93 只会走sub_1028 sub_102…

如何找IEEE论文中的作者指南(INFORMATION FOR AUTHORS)

文章目录 0.为什么需要作者指南1.进入所要发表期刊的官网并找到作者指南1.1百度直接搜索Transactions on Cybernetics1.2 点击About Journal进入并点击左下角Publication Details1.3 展开后点击Informations for author1.4 下载作者指南 2.结果显示 0.为什么需要作者指南 说明你…

STM32CubleMX创建FreeRtos工程教程,图文教程

前言&#xff1a;STM32CubeMX 是一个开发工具&#xff0c;它已经将 FreeRTOS 这个实时操作系统&#xff08;RTOS&#xff09;集成到其工具中。换句话说&#xff0c;通过 STM32CubeMX&#xff0c;可以非常方便地为 STM32 微控制器生成配置代码&#xff0c;其中包括对 FreeRTOS 的…

进阶SpringBoot之员工管理系统(5)展示员工列表和添加员工

Employee.java&#xff1a;模拟员工表 package com.demo.web.pojo;import lombok.Data; import lombok.NoArgsConstructor;import java.util.Date;//员工表 Data NoArgsConstructor public class Employee {private Integer id;private String lastName;private String email;…

第十五章 数据管理成熟度评估5分

15.0 语境关系图 活动&#xff1a; 计划评估活动&#xff08;P&#xff09;。进行成熟度评估&#xff08;C&#xff09;。拟定建议&#xff08;D&#xff09;。制定有针对性的改进计划&#xff08;P&#xff09;。 5. 重新评估成熟度&#xff08;C&#xff09; 应定期进行重新…

18308 最长公共子序列长度

### 分析 1. **问题描述**&#xff1a; - 给定两个字符串&#xff0c;要求找到它们的最长公共子序列的长度。 2. **解决方案**&#xff1a; - 使用动态规划&#xff08;Dynamic Programming, DP&#xff09;来解决这个问题。 - 定义一个二维数组dp&#xff0c;其中d…

使用AWS Lambda轻松开启Amazon Rekognition之旅

这是本系列文章的第一篇&#xff0c;旨在通过动手实践&#xff0c;帮助大家学习亚马逊云科技的生成式AI相关技能。通过这些文章&#xff0c;大家将掌握如何利用亚马逊云科技的各类服务来应用AI技术。 那么让我们开始今天的内容吧&#xff01; 介绍 什么是Amazon Rekognition&…

Java基础之循环嵌套

循环嵌套 在一个循环内部可以嵌套另一个或多个循环。 外部循环每执行1次&#xff0c;内层循环会执行1轮(全部)。 案例1&#xff1a; 连续3天&#xff0c;每天都要表白5次。 package com.briup.chap03;public class Test03_Nest {public static void main(String[] args) {…

MyBatis-Plus 自动填充字段详细教程

所以&#xff0c;接受事实&#xff0c;调整下略微悲观的态度。未来&#xff0c;也不一定就只会发生不好的变化。这变化&#xff0c;说不定也有好的&#xff0c;不是吗&#xff1f; 今天编写一个详细的教程来介绍如何在 Spring Boot 项目中使用 MyBatis-Plus 实现自动填充时间字…