【数据结构】二叉树(详细)

news2024/9/17 7:16:15

二叉树

  • 1.树
    • 1.1定义
    • 1.2基本术语
    • 1.3树形结构和线性结构
    • 1.4树的存储结构
      • 1.4.1双亲表示法
      • 1.4.2孩子兄弟表示法
  • 2.二叉树
    • 2.1定义
    • 2.2特殊二叉树
    • 2.3性质
    • 2.4存储结构
      • 2.4.1顺序存储
      • 2.4.2链式存储结构
  • 3.二叉树的基本操作
    • 3.1前序遍历(先序遍历)
    • 3.2中序遍历
    • 3.3后序遍历
    • 3.4层序遍历
  • 4.二叉树练习
  • 5.二叉树的创建和销毁
    • 5.1二叉树的创建
    • 5.2二叉树的销毁


1.树

学习二叉树,首先得了解树,从树的基本概念出发。

1.1定义

树是n个节点的的有限集合,是一种非线性结构。当n=0时称为空树,对于非空树T:(1)只有一个根结点(root);(2)除根节点外的其余结点可分为m个互不相交的有限集T1,T2,……,Tm,其中每个集合本身又是一棵树,称为根的子树。
在这里插入图片描述

1.2基本术语

  • 结点:树的一个独立单元,包含一个数据元素或者指向其子树的分支。如图中的A,B,C等。
  • 结点的度:结点拥有的子树数称为结点的度(也可以理解为这个结点有多少个孩子)。如A的度是2,B的度是3,D的度是0。
  • 树的度:树的各个结点的度的最大值。如图中的树的度为3。
  • 叶子结点(或者终端结点):度为0的结点。如图中的D,E,F,G。
  • 非终端结点:度不为0的结点。除根结点外,非终端结点也称为内部结点。如图中B,C。
  • 孩子结点(或者子节点):一个结点的子树的根结点称为该结点的孩子结点。如图中B和C是A的子结点。
  • 双亲结点(或者父结点):一个结点有一个子结点,该结点称为其子结点的父结点。如图中,A是B和C的双亲结点。
  • 兄弟结点:同一双亲的孩子之间互称兄弟。如图中B和C是兄弟结点。
  • 祖先:从根结点到该结点所经分支上的所有结点。如D的祖先是A和B。
  • 子孙:以某结点为根的子树的任一结点都称为该结点的子孙。如D,E,F是B的子孙。
  • 层次:从根结点开始,根结点为第一层,根的孩子为第二层,以此类推直到最后一层。如A是第一层,B是第二层,D是第三层。
  • 深度:树中结点的最大层次。如A这棵树的深度是3。
  • 森林:由m棵互不相交的树构成的集合。如去掉A结点,B和C这两棵子树就是森林。

这么多概念,但常用的只有结点的度、双亲结点、叶子结点、树的层次、树的高度、结点的祖先和子孙。

在这里插入图片描述

1.3树形结构和线性结构

树形结构线性结构
树的根结点没有双亲结点线性表的第一个元素没有前驱
树的叶子结点没有子结点线性表的最后一个元素没有后继
树的内部结点有一个前驱和多个后继线性表的其余元素只有一个前驱和一个后继

1.4树的存储结构

树有多种存储结构:双亲表示法、孩子链表表示法、孩子兄弟表示法等。由于今天的猪脚是二叉树,所以在这里简单介绍下双亲表示法和孩子兄弟表示法。

1.4.1双亲表示法

//结点
#define MAXSIZE 100
typedef int ElemType;
typedef struct
{
	ElemType x;//结点的数据
	int parent;//结点的双亲结点的下标
}PTNode;
typedef struct
{
	PTNode a[MAXSIZE];//结点数组
	int size;//结点个数
}PTree;

在这里插入图片描述

1.4.2孩子兄弟表示法

//结点
typedef int ElemType;
typedef struct CBTreeNode
{
	ElemType x;//数据
	struct CBTreeNode* firstchild;//指向结点的左孩子,也就是第一个孩子
	struct CBTreeNode* Nextbrother;//指向同一父结点的兄弟
}CBTNode;

在这里插入图片描述


2.二叉树

2.1定义

二叉树是n(n>=0)个结点的有限集合。当n=0时为空树,当n不为0时,二叉树有以下特点:1.每个结点的度不超过2(可以理解为二孩政策下的结点最多只能有两个孩子)
2.每个结点的左子树和右子树顺序不能颠倒,所以二叉树是有序树在这里插入图片描述

2.2特殊二叉树

  1. 满二叉树:每一层结点数都达到最大,那么它就是满二叉树。如第1层最多有2 ^0个结点,第2层最多有 2 ^1个结点,则第k层最多有2 ^(k-1)个结点,假设这棵满二叉树有k层,那么它总共有2 ^0+2 ^1+……+2 ^(k-1) = 2 ^k-1个结点。
  2. 完全二叉树:深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称为完全二叉树。(简介版:完全二叉树的前k-1层是满二叉树,最后一层从左到右依次连续)

在这里插入图片描述

2.3性质

  1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点。
  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h-1。
  3. 对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。
    证明
    在这里插入图片描述
  4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h= log (n+1)(ps:log是以2
    为底,n+1为对数)。
    证明
    在这里插入图片描述
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
    (1). 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
    (2). 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
    (3).若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子
    在这里插入图片描述

2.4存储结构

2.4.1顺序存储

二叉树的顺序存储是用数组存储的,其中结点之间的关系用下标来表示。即二叉树的逻辑结构是树,但是其物理结构是数组。
在这里插入图片描述
但这种存储结构会造成空间浪费,适用于完全二叉树和满二叉树。
在这里插入图片描述
但这个结构却可以来实现一个很牛逼的排序:堆排序。

2.4.2链式存储结构

链式存储结构可以解决顺序存储结构浪费空间的问题,二叉树的链式存储表示有二叉链表、三叉链表、双亲链表、线索链表等。这里重点讲二叉链表。

//二叉树的节点
typedef int DataType;
typedef struct BinaryTreeNode
{
	DataType val;
	struct BinaryTreeNode* left;//指向左孩子的指针
	struct BinaryTreeNode* right;//指向右孩子的指针
}BTNode;

在这里插入图片描述

下面讲二叉树的基本操作。

3.二叉树的基本操作

遍历是指每个结点被访问一次且仅被访问一次。二叉树有一个前驱和两个后继,这注定其遍历不同于线性结构的遍历。二叉树的遍历有前序遍历、中序遍历,后序遍历,层序遍历。

3.1前序遍历(先序遍历)

  1. 概念
    前序遍历:先访问根节点,再访问左子树和右子树。
    在这里插入图片描述

由图可知,树的前序遍历是递归的,所以可以用递归来实现。

  1. 代码实现
//先手动创建一个二叉树
BTNode* BuyNode(DataType x)//申请一个结点
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc failed");
		return NULL;
	}
	newnode->left = newnode->right = NULL;
	newnode->val = x;
	return newnode;
}
BTNode* CreateBT()
{
	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;//返回根结点
}
//前序遍历
void PreOrder(BTNode* root)
{
	//思路:先访问根结点,再访问左子树和右子树
	if (root==NULL)
	{
		printf("NULL ");
		return;
	}
	//先访问根结点
	printf("%d ", root->val);
	//再访问左右子树
	PreOrder(root->left);
	PreOrder(root->right);
}
int main()
{
	BTNode* root = CreateBT();
	PreOrder(root);
	return 0;
}

答案
在这里插入图片描述

3.2中序遍历

  1. 概念
    中序遍历:先访问左子树,再访问根节点,最后访问右子树。
    在这里插入图片描述
    同样可以用递归实现
  2. 代码实现
// 二叉树中序遍历
void InOrder(BTNode* root)
{
	//思路:先访问左子树,再访问根节点,最后访问右子树
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	//先访问左子树
	InOrder(root->left);
	//再访问根节点
	printf("%d ", root->val);
	//最后访问右子树
	InOrder(root->right);
}

答案
在这里插入图片描述

3.3后序遍历

  1. 概念
    后序遍历:先访问左子树,再访问右子树,最后访问根节点。
    在这里插入图片描述
  2. 代码实现
//后序遍历
void PostOrder(BTNode* root)
{
	//思路:先访问左子树,再访问右子树,最后访问根结点
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->val);
}

答案
在这里插入图片描述

3.4层序遍历

  1. 概念
    层序遍历:从上到下,从左到右,依次访问。
    在这里插入图片描述
  2. 代码实现
// 层序遍历
void LevelOrder(BTNode* root)
{
	//思路:用队列实现,出队一层,入队下一层
	Queue q;
	QInit(&q);
	//如果非空就直接入队
	if (root)
	{
		QPush(&q, root);
	}
	//队列中只入队非空元素
	while (!QEmpty(&q))
	{
		//先出队
		BTNode* tmp = QPop(&q);
		printf("%d ", tmp->val );
		//再判断左右子树是否为空
		if (tmp->left)
		{
			QPush(&q, tmp->left);
		}
		if (tmp->right)
		{
			QPush(&q, tmp->right);
		}
	}
	QDestroy(&q);
}

答案
在这里插入图片描述

4.二叉树练习

样例
在这里插入图片描述

  1. 求二叉树的节点个数
// 二叉树节点个数

//法一:
int size = 0;//也可以使用静态变量
void TreeSize(BTNode* root)
{
	//思路:只要节点不为空,就记录一次
	if (root == NULL)
	{
		return 0;
	}
	++size;
	TreeSize(root->left);
	TreeSize(root->right);
}


//法二:
int BinaryTreeSize(BTNode* root)
{
	//思路:二叉树的节点个数 = 左子树的节点个数 + 右子树的节点个数
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1 ;
}

注意
但是法一有个弊端:size是全局(或静态)变量,每次调用都得初始化一次。
答案
在这里插入图片描述

  1. 求二叉树的叶子节点个数
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	//思路:二叉树的叶子节点 = 左子树的叶子节点 + 右子树的叶子节点
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

答案
在这里插入图片描述

  1. 求二叉树的高度
// 二叉树的高度(深度)
int TreeHeight(BTNode* root)
{
	// 思路:二叉树的高度 = 左子树的高度和右子树的高度的最大值 + 1
	if (root == NULL)
	{
		return 0;
	}
	int left = TreeHeight(root->left);
	int right = TreeHeight(root->right);
	return left > right ? left + 1 : right + 1;
}

答案
在这里插入图片描述

  1. 求第k层节点个数
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	//思路:
	//对于第一层,是求第k层节点个数(k)
	//对于第二层,是求第k-1层节点个数(k-1)
	//……
	//对于第k层,是求这一层节点个数(1)
	//第k层节点个数 = 左子树第k层节点个数 + 右子树第k层节点个数
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

答案
在这里插入图片描述

  1. 判断是否是单值二叉树(LeetCode965)OJ链接
    单值二叉树:二叉树的每个节点都有相同的值
// 判断是否是单值二叉树
bool isUnivalTree(BTNode* root)
{
	//思路:比较根节点与左右子树是否相等,不相等就返回false,相等就判断左右子树是否是单值二叉树
	if (root == NULL)
	{
		return true;
	}
	if (root->left && root->left->val != root->val)
	{
		return false;
	}
	if (root->right && root->right->val != root->val)
	{
		return false;
	}
	return isUnivalTree(root->left) && isUnivalTree(root->right);
}

答案
在这里插入图片描述

  1. 查找一个值为x的节点
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, DataType x)
{
	//思路:先判断根的值是否与x相等,相等就返回根,
	//不等就判断是否与左子树相等,相等就返回
	//不等就判断是否与右子树相等,相等就返回,不等就返回NULL
	if (root == NULL)
	{
		return NULL;
	}
	if (root->val == x)
	{
		return root;
	}
	BTNode*tmp = BinaryTreeFind(root->left, x);
	if (tmp != NULL)
	{
		return tmp;
	}
	tmp = BinaryTreeFind(root->right, x);
	if (tmp != NULL)
	{
		return tmp;
	}
	return NULL;
}
  1. 判断两棵二叉树是否相等(LeetCode100)OJ链接
// 判断两棵二叉树是否相等
bool isSameTree(BTNode* p, BTNode* q)
{
	//思路:先判断根结点是否相等,再判断左子树是否相等,最后判断右子树是否相等
	if (p == NULL && q == NULL)
	{
		return true;
	}
	if (p == NULL || q == NULL)
	{
		return false;
	}
	if (p->val != q->val)
	{
		return false;
	}
	return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}

答案
在这里插入图片描述

  1. 判断是否是对称二叉树(LeetCode101)OJ链接
bool isLefRig(BTNode* p1, BTNode* p2)//功能类似于判断两棵二叉树是否相等
{
	if (p1 == NULL && p2 == NULL)
	{
		return true;
	}
	if (p1 == NULL || p2 == NULL)
	{
		return false;
	}
	if (p1->val != p2->val)
	{
		return false;
	}
	return isLefRig(p1->left, p2->right) && isLefRig(p1->right, p2->left);
}
bool isSymmetric(BTNode* root)
{
·	//思路:写一个辅助函数,功能与判断二叉树是否相等类似
	if (root == NULL)
	{
		return true;
	}
	return isLefRig(root->left, root->right);
}

答案
在这里插入图片描述

  1. 二叉树的前序遍历(LeetCode144)OJ链接
int TreeSize(struct TreeNode* root)
{
	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
void PreOrder(struct TreeNode* root, int* a, int* i)
{
	if (root == NULL)
	{
		return;
	}
	//先对根结点进行操作,再对左右子树进行操作
	a[(*i)++] = root->val;
	PreOrder(root->left, a, i);
	PreOrder(root->right, a, i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
	//我们需要知道这棵树的大小,再来动态申请
	int size = TreeSize(root);
    *returnSize = size;
	int* ret = (int*)malloc(sizeof(int) * size);
	//写一个函数来实现遍历,不要在这个函数遍历,因为遍历需要递归,所以TreeSize会重复调用
	int i = 0;//用来记录数组的下标,方便存储数据
	PreOrder(root, ret, &i);//为什么要传下标的地址?因为要对下标进行修改
	return ret;
}

答案
在这里插入图片描述

  1. 二叉树的中序遍历(LeetCode94)OJ链接
int TreeSize(struct TreeNode* root)
{
	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
void InOrder(struct TreeNode* root, int* a, int* i)
{
	if (root == NULL)
	{
		return;
	}
	InOrder(root->left, a, i);
    a[(*i)++] = root->val;
	InOrder(root->right, a, i);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){
	//我们需要知道这棵树的大小,再来动态申请
	int size = TreeSize(root);
    *returnSize = size;
	int* ret = (int*)malloc(sizeof(int) * size);
	//写一个函数来实现遍历,不要在这个函数遍历,因为遍历需要递归,所以TreeSize会重复调用
	int i = 0;//用来记录数组的下标,方便存储数据
	InOrder(root, ret, &i);//为什么要传下标的地址?因为要对下标进行修改
	return ret;
}

结果
在这里插入图片描述

  1. 二叉树的后序遍历(LeetCode145)OJ链接
int BinaryTreeSize(struct TreeNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
void PostOrder(struct TreeNode* root, int* ret, int* pi)
{
	if (root == NULL)
	{
		return 0;
	}
    PostOrder(root->left, ret, pi);
	PostOrder(root->right, ret, pi);
    ret[(*pi)++] = root->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize){
	int size = BinaryTreeSize(root);
	*returnSize = size;
	int* ret = (int*)malloc(sizeof(int) * size);//用来存放前序序列
	int i = 0;
	PostOrder(root, ret, &i);
	return ret;
}

结果
在这里插入图片描述

  1. 判断一棵树是否是另一棵树的子树
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
	//思路:用当前节点所在树与sub进行比较,相等返回真,不相等用当前根结点的左子树比较,再不相等用右子树比较
	if (root == NULL)
	{
		return false;
	}
	if (isSameTree(root, subRoot))
	{
		return true;
	}
	return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}

结果
在这里插入图片描述

  1. 判断是否是完全二叉树
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	//思路:利用层序遍历,将完全二叉树的所有节点全部入队,
	//当出队时节点为NULL,如果为完全二叉树,则队列中的其余节点都是NULL,
	//所以最后判断队列中的节点是否不为NULL,即可判断
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode*tmp = QueueFront(&q);
		QueuePop(&q);
		if (tmp == NULL)
		{
			break;//当遇到NULL,就跳出循环
		}
		else
		{
			QueuePush(&q, tmp->left);//如果为NULL也入队
			QueuePush(&q, tmp->right);
		}
	}
	while (!QueueEmpty(&q))
	{
		BTNode* tmp = QueueFront(&q);
		QueuePop(&q);
		if (tmp)
		{
			QueueDestroy(&q);//注意销毁队列,防止内存泄漏
			return false;
		}
	}
	QueueDestroy(&q);//等下学习怎么销毁,先记住它有销毁的功能
	return true;
}
int main()
{
	char arr[100] = { 0 };
	scanf("%s", arr);
	int i = 0;
	int size = strlen(arr);
	BTNode* root = CreateBT(arr, &i,size);
	InOrder(root);
}

5.二叉树的创建和销毁

前面的二叉树是我们手动创建的,现在学习了前序、中序、后序遍历,就可以利用它们来创建和销毁二叉树。

5.1二叉树的创建

二叉树遍历OJ链接

BTNode* BuyNode(DataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc failed");
		return NULL;
	}
	newnode->left = NULL;
	newnode->right = NULL;
	newnode->val = x;
	return newnode;
}
BTNode* CreateBT(char* arr, int* pi,int size)
{	//ABC##DE#G##F###
	if (*pi == size)//递归的停止条件
	{
		return NULL;
	}
	if (arr[*pi] == '#')
	{
		(*pi)++;//遇到#,不要忘记跳过
		return NULL;
	}
	BTNode* root = BuyNode(arr[(*pi)++]);
	root->left = CreateBT(arr, pi,size);
	root->right = CreateBT(arr, pi,size);
	return root;
}
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	InOrder(root->left);
	printf("%c ", root->val);
	InOrder(root->right);
}
int main()
{
	char arr[100] = { 0 };
	scanf("%s", arr);
	int i = 0;
	int size = strlen(arr);
	BTNode* root = CreateBT(arr, &i,size);
	InOrder(root);
}

结果
在这里插入图片描述

5.2二叉树的销毁

// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
	//如果用前序或者中序遍历销毁二叉树会导致内存泄漏(找不到左右子树)
	//所以用后序遍历销毁二叉树
	if (root == NULL)
	{
		return;
	}
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
	root == NULL;
}

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

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

相关文章

开放原子训练营(第三季)inBuilder低代码开发实验室---报销单录入系统

作为一名低代码初学者&#xff0c;我使用inBuilder系统设计了一款报销单录入系统&#xff0c;实现了报销单录入与显示报销单列表的功能&#xff08;如图1与图2所示&#xff09;&#xff0c;并获得了很多开发心得。从inBuilder系统的优点、缺点以及开发过程三方面出发&#xff0…

go继承nacos配置中心并读取配置信息

配置中心 为什么需要配置中心 平时我们写一个demo的时候&#xff0c;或者说一个单体的应用&#xff0c;都会有一个配置文件&#xff0c;不管是 json文件或者yaml文件&#xff0c;里面包含了redis,mysql,es等信息&#xff0c;如果我们修改了配置文件&#xff0c;往往我们需要重…

和Ai一起学习CMake(一)

和Ai一起学习CMake 现在人工智能爆火&#xff0c;ChatGPT、new bing等层出不穷。我们借助Ai来学习一下CMake。下面是我与Ai的问答&#xff0c;这个学习主要是通过Ai来学习&#xff0c;但是防止Ai乱说话&#xff0c;我会结合自身的知识和实际操作给出相应的补充。 我的环境如下…

玄子Share - Tomcat 9 安装配置教程(含安装包)

玄子Share - Tomcat 9 安装配置教程&#xff08;含安装包&#xff09; 下载 下载链接 官网 https://tomcat.apache.org/ 下载教程 进入 Tomcat 官网&#xff0c;点击左侧导航栏&#xff0c;选择需要的版本下载。这里按照课程要求选择tomcat 9的版本 Mirrors&#xff1a;选…

为什么越来越多的企业开始选择云计算?

一、前言 随着数字化时代的到来&#xff0c;企业对于数据的需求越来越大&#xff0c;而传统的数据存储方式已经无法满足企业的需求。云计算作为一种新兴的技术&#xff0c;可以为企业提供更加灵活、高效、安全的数据存储和处理方式&#xff0c;因此越来越多的企业开始选择云计算…

element ui el-calendar日历组件使用总结

el-calendar日历组件使用总结 1. 需求说明2. 基础实现3. 只展示当月数据4. 动态切换日历里的数据实现5. 顺便把快捷方式隐藏了&#xff0c;不让用户选择上一个月下一个月 1. 需求说明 按月显示&#xff0c;每次只显示一个月的日期&#xff0c;其余隐藏 日历组件的显示时间随着…

数值分析-牛顿插值公式

目录 一、引言 二、牛顿插值公式的基本概念 1.插值问题 2.插值多项式 3.牛顿插值公式 三、牛顿插值公式的推导过程 四、牛顿插值公式的应用 1.图像处理 2.信号处理 五、牛顿插值公式的优缺点 1. 优点 2. 缺点 六、总结 一、引言 在数值分析中&#xff0c;插值是一…

干掉复杂的Java工具类,Hutool 工具库确实香~

Hutool 大家已经比较熟悉了&#xff0c;这是一个超全的 Java 工具库&#xff0c;深受国内开发者的喜爱。 我之前其实是不太喜欢使用这种功能太多的工具类的&#xff0c;也比较担心稳定性和安全性&#xff0c;后面慢慢接受了就感觉其实也还好。而且&#xff0c;我们还可以按需只…

如何优雅地停掉线程?

很久很久以前&#xff0c;在一个名为“Springboot”的村庄中&#xff0c;住着一群热爱编程的程序员。他们喜欢探索新技术、优化自己的代码&#xff0c;为了打造更好的软件而不断努力着。 在这个村庄中&#xff0c;有一个名叫小明的程序员&#xff0c;他是村庄中最优秀的程序员…

等概率随机函数设计技巧

文章目录 1. 关于Math.random()函数2. 用1 ~ 5的随机函数加工出1 ~ 7的随机函数3. LeetCode 470. 用 Rand7() 实现 Rand10()4. 把不等概率随机函数变成等概率随机函数5. 用a ~ b的随机函数加工出c ~ d的随机函数 1. 关于Math.random()函数 Java 中 Math.random() 函数是等概率…

微服务和领域驱动

一、微服务 1.1 什么是微服务 微服务就是一些协同工作的小而自治的服务。 关键词&#xff1a; 小而自治 -- 小 “小”这个概念&#xff0c;一方面体现在微服务的内聚性上。 内聚性也可以称之为单一职责原则&#xff1a;“把因相同原因而变化的东西聚合到一起&#xff0c;…

python能成为编程届的网红么?

Python本身就是编程语言届的网红&#xff0c;Python&#xff0c;年龄可能比很多读者都要大&#xff0c;但是它在更新快速的编程界却一直表现出色&#xff0c;甚至有人把它比作是编程界的《葵花宝典》&#xff0c;只是Python的速成之法相较《葵花宝典》有过之而无不及。 但是能…

短视频矩阵系统.代码实时分享

短视频矩阵系统核心技术研发是为满足现代社交网络时代用户对视频分享和观看的需求而推出的一项技术。它旨在提供高质量的视频传输、智能推荐算法、实时互动等功能。短视频矩阵系统设计上考虑了多个关键技术&#xff0c;包括多媒体编解码技术、网络通讯技术、机器学习算法等。通…

[pgrx开发postgresql数据库扩展]附.更新开发环境安装脚本

pgrx更新到0.83之后&#xff0c;我本来还没感觉&#xff0c;但是我五一放假一来&#xff0c;发现我的WSL环境居然就挂了…… 果然是非稳定版本就是不靠谱了…… 所以我干脆搞了个虚拟机&#xff0c;重新安装了一套&#xff0c;还别说&#xff0c;更新到了0.83之后&#xff0c;安…

(十一)地理数据库创建——创建新的地理数据库

地理数据库创建——创建新的地理数据库 目录 地理数据库创建——创建新的地理数据库 1.地理数据库概述2.地理数据库建立一般过程2.1地理数据库设计2.2地理数据库建立2.2.1从头开始建立一个新的地理数据库2.2.2移植已经存在数据到地理数据库2.2.3用CASE工具建立地理数据库 2.3建…

学习HCIP的day.04

目录 七、关于OSPF的不规则区域问题 1、通过隧道链路&#xff08;Tunnel&#xff09; 2、OSPF的虚链路配置 3、多进程双向重发布 八、OSPF的数据库表 九、OSPF优化 1、汇总 2、特殊区域 --- 用于减少各个非骨干区域的LSA数量 七、关于OSPF的不规则区域问题 分为两种情…

【MySQL实战2 作业解析】

这里写自定义目录标题 作业回顾作业步骤完成方法恢复数据库设置查询日志的开关删除退市股票以及新股的无效交易日的数据删除个股数据表查询merged_table这张表里3开头的股票中每个月成交量最大的股票下载日志文件&#xff0c;备份数据库 作业回顾 作业内容发布在社区里&#x…

输入捕获模式测频率、PWMI模式测频率占空比

一、知识点 TIM输入捕获模式&#xff1a; 1、输入捕获模式测频率占空比 信号源&#xff1a;产生一个频率和占空比可调的波形 无信号发生器的情况&#xff1a;先用PWM模块&#xff0c;在PA0端口输出一个频率和占空比可调的波形&#xff0c;把PA0和PA6连在一起&#xff0c;PA6为输…

Ubuntu 安装 Mysql

主要内容 本文主要是实现在虚拟机 Ubuntu 18.04 成功安装 MySQL 5.7&#xff0c;并实现远程访问功能&#xff0c;以 windows 下客户端访问虚拟机上的 mysql 数据库。 1. 切换至 root 用户 &#xff0c;shell 终端指令均执行在 root 用户下 sudo su 2. 安装并设置 mysql 安…

DOM操作-获取元素的方式

DOM—文档对象模型 ●DOM&#xff08;Document Object Model&#xff09;&#xff1a; 文档对象模型 ●其实就是操作 html 中的标签的一些能力 ●或者说是一整套操作文档流的属性和方法的集合 ●我们可以操作哪些内容 ○获取一个元素 ○移除一个元素 ○创建一个元素 ○向页面里…