二叉树的介绍及二叉树的链式结构的实现(C语言版)

news2024/10/7 6:50:07

前言

        二叉树是一种特殊的树,它最大的度为2,每个节点至多只有两个子树。它是一种基础的数据结构,后面很多重要的数据结构都是依靠它来进行实现的。了解并且掌握它是很重要的。

目录

1.二叉树的介绍

        1.1概念

        1.2现实中的二叉树

        1.3特殊的二叉树

        1.4二叉树的性

        1.5二叉树的存储结构 

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

        2.1创建一颗伪二叉树

        2.2二叉树的遍历

                2.2.1前序,中序和后序遍历

                2.2.2层序遍历 

        2.3二叉树的节点个数及高度等

        2.4二叉树的创建及销毁 

        2.5全部代码


 

1.二叉树的介绍

        1.1概念

        一颗二叉树是节点的一个有限集合,该集合:

        1.或者为空

        2.由根节点外加两颗别称为左子树和右子树的二叉树组成

        1.2现实中的二叉树

        1.3特殊的二叉树

        满二叉树:一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且节点的总数是二的K次方-1,则它就是满二叉树。

        完全二叉树 : 完全二叉树是一种效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有N个节点的二叉树,当且仅当每个节点都与深度为K的满二叉树中编号从1到N的节点一一对应时,称为完全二叉树。

        1.4二叉树的性

         1.若规定根节点的层数为1,则一颗非空二叉树的第i层最多有2的i-1次方个节点

         2.若规定根节点的层数为1,则深度为h的二叉树最大的节点数为2的h次方减1。

         3.对于任意一颗二叉树,如果度为零其叶子结点的个数为n0,度为2的分支节点个数为n2,则有n0 = n2 + 1;

        4.若规定根节点的层数为1,其n个节点的满二叉树的深度,h = log2(n+1).(log2(n + 1)是以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,则无右孩子。

        1.5二叉树的存储结构 

        二叉树一般有两种结构的存储方式,一种是顺序结构,一种是链式结构。

        1.顺序存储

        顺序存储使用的数组,一般数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费,而现实中只有堆 才使用数组来存储。二叉树的顺序存储在物理上是数组,在逻辑上是一颗二叉树。

         2.链式存储

        二叉树的链式存储结构是指,用链表来表示一颗二叉树,即用链表来指示元素之间的逻辑关系。通常的方法是每个节点由左,右指针域和数据域组成。左,右指针分别用来给出该节点左孩子和右孩子所在节点的地址。链式存储结构又分为二叉链和三叉链。现在我们使用的是二叉链。

         

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

        2.1创建一颗伪二叉树

        这里需要快速创建一颗二叉树,为了降低理解的难度,先创建一颗伪二叉树来进行学习,便于理解。

        //BTree.h

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef char BTDataType;
typedef struct BTreeNode
{
	BTDataType _data;
	struct BTreeNode* _left;
	struct BTreeNode* _right;
}BTreeNode;
BTreeNode* BuyBTreeNode(BTreeNode* node, BTDataType c);//申请一棵树的节点

BTreeNode* CreatBinaryTree(BTreeNode* root);//创建一棵树

        //BTree.c

BTreeNode* BuyBTreeNode(BTreeNode* node,BTDataType c)
{
	BTreeNode*cur = (BTreeNode*)malloc(sizeof(BTreeNode));
	cur->_data = c;
	cur->_left = cur->_right = NULL;
}
BTreeNode* CreatBinaryTree(BTreeNode* root)//创建一棵树
{
	root = BuyBTreeNode(root, 'A');
	BTreeNode* B = BuyBTreeNode(root, 'B');
	BTreeNode* C = BuyBTreeNode(root, 'C');
	BTreeNode* D = BuyBTreeNode(root, 'D');
	BTreeNode* E = BuyBTreeNode(root, 'E');
	BTreeNode* F = BuyBTreeNode(root, 'F');

	root->_left = B;
	root->_right = C;
	B->_left = D;
	C->_left = E;
	C->_right = F;
	return root;
}

        2.2二叉树的遍历

                2.2.1前序,中序和后序遍历

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

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

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

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

        3.后序遍历(Post Traversal)--访问根节点的操作发生在左右子树之后。

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

        

        前序遍历的代码:

// 二叉树前序遍历
void PreOrder(BTreeNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%c ",root->_data);
	PreOrder(root->_left);//左子树
	PreOrder(root->_right);//右子树
}

        前序遍历的递归图解: 

 

        中序遍历和后序遍历的代码: 

        

// 二叉树中序遍历
void InOrder(BTreeNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PreOrder(root->_left);//左子树
	printf("%c ", root->_data);//根
	PreOrder(root->_right);//右子树
}
// 二叉树后序遍历
void PostOrder(BTreeNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PreOrder(root->_left);//左子树
	PreOrder(root->_right);//右子树
	printf("%c ", root->_data);//根

}

         中序和后序遍历的展开图解和前序遍历的类似,有兴趣的友友可以自己画画看。

                2.2.2层序遍历 

        二叉树的层序遍历是通过借助队列来实现的,将一颗树的根入队,出队时,如果根的左,右节点不为空就将根的左右节点依次入队,当队列为空时结束循环,这样就完成了二叉树的层序遍历。  

void LevelOrder(BTreeNode* root)//层序遍历
{
	//创建队列
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))//队列不为空,出队头的元素,同时取节点判断左右子树是否为空
	{
		BTreeNode* cur = QueueFront(&q);
		QueuePop(&q);
		printf("%c ", cur->_data);
		if (cur->_left)//左子树存在
		{
			QueuePush(&q, cur->_left);//将当前节点的左子树入队
		}
		if (cur->_right)//右子树存在
		{
			QueuePush(&q, cur->_right);//将当前节点的右子树入队
		}
		
	}
}

        2.3二叉树的节点个数及高度等

        递归求二叉树的高度是在当前节点求出左,右子树的高度,再将左,右子树中高度大的那个加一就是当前树的高度。

int BinarTreelen(BTreeNode* root)//求树的高度
{

	if (root == NULL)
		return 0;
	int leftsize = BinarTreelen(root->_left);
	int rightsize = BinarTreelen(root->_right);
	return leftsize > rightsize ? leftsize + 1 : rightsize + 1;//高度等于下一层左右节点高的那个加1
}

        二叉树叶子结点的个数,通过递归求解,首先叶子节点肯定满足左右子树都为空,所以如果左右子树都为空的话,就直接返回1,说明当前节点就是叶子节点,否则继续递归去找叶子节点。

int BinaryTreeLeafSize(BTreeNode* root)
{
	if (root == NULL)//节点为空直接返回
		return 0;
	if (root->_left == NULL && root->_right == NULL)//左右节点都为空
		return 1;//说明当前节点为叶子节点
	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);//递归去求左右子树
}

        求二叉树的节点个数,如果当前节点不为空,就+1,然后递归求左,右子树中的节点数。如果当前节点为NULL则返回0.

// 二叉树节点个数
int BinaryTreeSize(BTreeNode* root)
{
	if (root == NULL)//当前节点为NULL
		return 0;
	return 1 + BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right);//前序遍历的方式求树的节点个数
}

         二叉树查找值为x的节点,通过前序遍历二叉树,如果二叉树当前节点的值等于x就返回当前节点。

BTreeNode* BinaryTreeFind(BTreeNode* root, BTDataType x)
{
	if (root == NULL)//如果节点为NULL
		return NULL;//返回NULL
	if (root->_data == x)
		return root;//找到值为x的节点,返回
	BTreeNode*cur = BinaryTreeFind(root->_left, x);
	if (cur)//找到了就返回节点的地址
		return cur;
	cur = BinaryTreeFind(root->_right, x);
	if (cur)//找到了就返回节点的地址
		return cur;
	return NULL;//如果这棵树没有找到就返回NULL
}

        求二叉树的第K层的节点个数,采用分治的思想:一颗树第K层的节点等于左子树K-1层的节点+右子树K-1层的节点。  结束条件是,如果当K等于1时,返回1,当当前节点为NULL时返回0。

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTreeNode* root, int _k)
{
	if (root == NULL)
		return 0;
	if (_k == 1 && root)//如果K等于1直接返回
		return 1;
	return BinaryTreeLevelKSize(root->_left, _k - 1) + BinaryTreeLevelKSize(root->_right, _k - 1);//递归到左,右子树K-1层中去找节点数
}

        判断一颗二叉树是不是完全二叉树,借助 队列,采用层序遍历的方式,但是这里不判断当前树的左右子树是否为空,取队头的数据时,直接将该节点的左,右子树入队。当取到的队头的数据为空时,结束循环。如果是完全二叉树,那么剩余在队列中的所有的NULL应该是连续的。再将队列中所有的元素都出队,如果全部为空就满足完全二叉树,如果不是就不满足完全二叉树。 

bool BinaryTreeComplete(BTreeNode* root)//判断一棵树是不是完全二叉树
{
	if (root == NULL)
		return true;
	//创建队列
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))//队列不为空,出队头的元素,同时判断取出来的节点是否为空
	{
		BTreeNode* cur = QueueFront(&q);
		QueuePop(&q);
		if (cur == NULL)
			break;//当前节点为空直接结束循环
		QueuePush(&q, cur->_left);
		QueuePush(&q, cur->_right);
	}
	while (!QueueEmpty(&q))
	{
		//判断队列中剩下的元素是否有不为空的
		BTreeNode* cur = QueueFront(&q);
		QueuePop(&q);
		if (cur)
			return false;
	}
	//如果走到这里还没有返回说明是完全二叉树
	return true;
}

        2.4二叉树的创建及销毁 

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

        思路:通过递归去构建二叉树,因为给出的是前序遍历的数组,而数组中的#表示NULL,因此需要参数来记录数组的位置,也需要将字符数组传递给构建函数,如果遇到#就返回NULL,如果不是就申请空间初始化data域,并且将申请的空间进行返回,然后去递归构建左右子树。

#include <stdio.h>
#include<stdlib.h>

typedef struct BTreeNode
{

	char _data;

	struct BTNode* _left;

	struct BTNode* _right;

}BTreeNode;

void InOrder(BTreeNode* root)

{
	if (root == NULL)
		return;
	//左子树
	InOrder(root->_left);
	//根
	printf("%c ", root->_data);
	//右子树
	InOrder(root->_right);
}

BTreeNode* BinaryTreeCreate(char* a, int* pi)
{
	if (a[*pi] == '#')
	{
		++(*pi);
		return NULL;
	}
	BTreeNode* node = (BTreeNode*)malloc(sizeof(BTreeNode));//申请节点
	node->_data = a[*pi];
	(*pi)++;
	//递归构建左右子树
	node->_left = BinaryTreeCreate(a, pi);
	node->_right = BinaryTreeCreate(a, pi);
	return node;
}
int main() 
{
	char s[50] = { 0 };
	scanf("%s", s);
	int i = 0;
	//使用前序遍历构建树
	BTreeNode* root = BinaryTreeCreate(s, &i);
	//使用中序遍历打印树的节点的值
	InOrder(root);
	return 0;
}

        二叉树的销毁,如果采用前序遍历的方式进行销毁,就需要保存当前节点,所以建议采用后序遍历的方式进行销毁。 

// 二叉树销毁
void BinaryTreeDestory(BTreeNode* root)//这里为了保持接口的一致性采用一级指针
{
	if (root == NULL)
		return;
	BinaryTreeDestory(root->_left);
	BinaryTreeDestory(root->_right);
	free(root);
}

        2.5全部代码

        //BTree.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
typedef char BTDataType;
typedef struct BTreeNode
{
	BTDataType _data;
	struct BTreeNode* _left;
	struct BTreeNode* _right;
}BTreeNode;
BTreeNode* BuyBTreeNode(BTreeNode* node, BTDataType c);//申请一棵树的节点

BTreeNode* CreatBinaryTree(BTreeNode* root);//创建一棵树

// 二叉树前序遍历
void PreOrder(BTreeNode* root);
// 二叉树中序遍历
void InOrder(BTreeNode* root);
// 二叉树后序遍历
void PostOrder(BTreeNode* root);

// 二叉树节点个数
int BinaryTreeSize(BTreeNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTreeNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTreeNode* root, int _k);

int BinarTreelen(BTreeNode* root);//求树的高度
// 二叉树查找值为x的节点
BTreeNode* BinaryTreeFind(BTreeNode* root, BTDataType x);

void LevelOrder(BTreeNode* root);//层序遍历

// 二叉树销毁
void BinaryTreeDestory(BTreeNode* root);
bool BinaryTreeComplete(BTreeNode* root);//判断一棵树是不是完全二叉树

         //BTree.c

#include"BTree.h"
#include"Queue.h"
BTreeNode* BuyBTreeNode(BTreeNode* node,BTDataType c)
{
	BTreeNode*cur = (BTreeNode*)malloc(sizeof(BTreeNode));
	cur->_data = c;
	cur->_left = cur->_right = NULL;
	return cur;
}
BTreeNode* CreatBinaryTree(BTreeNode* root)//创建一棵树
{
	root = BuyBTreeNode(root, 'A');
	BTreeNode* B = BuyBTreeNode(root, 'B');
	BTreeNode* C = BuyBTreeNode(root, 'C');
	BTreeNode* D = BuyBTreeNode(root, 'D');
	BTreeNode* E = BuyBTreeNode(root, 'E');
	BTreeNode* F = BuyBTreeNode(root, 'F');

	root->_left = B;
	root->_right = C;
	B->_left = D;
	C->_left = E;
	C->_right = F;
	return root;
}
// 二叉树前序遍历
void PreOrder(BTreeNode* root){
	if (root == NULL)
	{ return;}
	printf("%c ",root->_data);
	PreOrder(root->_left);//左子树
	PreOrder(root->_right);//右子树
}
// 二叉树中序遍历
void InOrder(BTreeNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PreOrder(root->_left);//左子树
	printf("%c ", root->_data);//根
	PreOrder(root->_right);//右子树
}
// 二叉树后序遍历
void PostOrder(BTreeNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PreOrder(root->_left);//左子树
	PreOrder(root->_right);//右子树
	printf("%c ", root->_data);//根

}

// 二叉树节点个数
int BinaryTreeSize(BTreeNode* root)
{
	if (root == NULL)//当前节点为NULL
		return 0;
	return 1 + BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right);//前序遍历的方式求树的节点个数
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTreeNode* root)
{
	if (root == NULL)//节点为空直接返回
		return 0;
	if (root->_left == NULL && root->_right == NULL)//左右节点都为空
		return 1;//说明当前节点为叶子节点
	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);//递归去求左右子树
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTreeNode* root, int _k)
{
	if (root == NULL)
		return 0;
	if (_k == 1 && root)//如果K等于1直接返回
		return 1;
	return BinaryTreeLevelKSize(root->_left, _k - 1) + BinaryTreeLevelKSize(root->_right, _k - 1);//递归到左,右子树K-1层中去找节点数
}
int BinarTreelen(BTreeNode* root)//求树的高度
{

	if (root == NULL)
		return 0;
	int leftsize = BinarTreelen(root->_left);
	int rightsize = BinarTreelen(root->_right);
	return leftsize > rightsize ? leftsize + 1 : rightsize + 1;//高度等于下一层左右节点高的那个加1
}
// 二叉树查找值为x的节点

BTreeNode* BinaryTreeFind(BTreeNode* root, BTDataType x)
{
	if (root == NULL)//如果节点为NULL
		return NULL;//返回NULL
	if (root->_data == x)
		return root;//找到值为x的节点,返回
	BTreeNode*cur = BinaryTreeFind(root->_left, x);
	if (cur)//找到了就返回节点的地址
		return cur;
	cur = BinaryTreeFind(root->_right, x);
	if (cur)//找到了就返回节点的地址
		return cur;
	return NULL;//如果这棵树没有找到就返回NULL
}

void LevelOrder(BTreeNode* root)//层序遍历
{
	//创建队列
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))//队列不为空,出队头的元素,同时取节点判断左右子树是否为空
	{
		BTreeNode* cur = QueueFront(&q);
		QueuePop(&q);
		printf("%c ", cur->_data);
		if (cur->_left)//左子树存在
		{
			QueuePush(&q, cur->_left);
		}
		if (cur->_right)//右子树存在
		{
			QueuePush(&q, cur->_right);
		}
		
	}
}

// 二叉树销毁
void BinaryTreeDestory(BTreeNode* root)//这里为了保持接口的一致性采用一级指针
{
	if (root == NULL)
		return;
	BinaryTreeDestory(root->_left);
	BinaryTreeDestory(root->_right);
	free(root);
}
bool BinaryTreeComplete(BTreeNode* root)//判断一棵树是不是完全二叉树
{
	if (root == NULL)
		return true;
	//创建队列
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))//队列不为空,出队头的元素,同时判断取出来的节点是否为空
	{
		BTreeNode* cur = QueueFront(&q);
		QueuePop(&q);
		if (cur == NULL)
			break;//当前节点为空直接结束循环
		QueuePush(&q, cur->_left);
		QueuePush(&q, cur->_right);
	}
	while (!QueueEmpty(&q))
	{
		//判断队列中剩下的元素是否有不为空的
		BTreeNode* cur = QueueFront(&q);
		QueuePop(&q);
		if (cur)
			return false;
	}
	//如果走到这里还没有返回说明是完全二叉树
	return true;
}

#include <stdio.h>
#include<stdlib.h>

typedef struct BTreeNode
{

	char _data;

	struct BTNode* _left;

	struct BTNode* _right;

}BTreeNode;

void InOrder(BTreeNode* root)

{
	if (root == NULL)
		return;
	//左子树
	InOrder(root->_left);
	//根
	printf("%c ", root->_data);
	//右子树
	InOrder(root->_right);
}

BTreeNode* BinaryTreeCreate(char* a, int* pi)
{
	if (a[*pi] == '#')
	{
		++(*pi);
		return NULL;
	}
	BTreeNode* node = (BTreeNode*)malloc(sizeof(BTreeNode));//申请节点
	node->_data = a[*pi];
	(*pi)++;
	//递归构建左右子树
	node->_left = BinaryTreeCreate(a, pi);
	node->_right = BinaryTreeCreate(a, pi);
	return node;
}

//Queue.h

#pragma once
#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
#include<stdbool.h>
typedef struct BTreeNode BTreeNode;
typedef  BTreeNode* QDataType;//树节点的声明

typedef struct QueueNode
{
	struct QueueNode* _next;
	QDataType _data;
}QueueNode;
typedef struct  Queue//队列的结构
{
	QueueNode* _head;//头指针
	QueueNode* _tail;//尾指针
}Queue;

void QueueInit(Queue* qu);//初始化栈

void QueueDestory(Queue* qu);//摧毁栈

void QueuePush(Queue* qu,QDataType data);//入队

void QueuePop(Queue* qu);//出队

QDataType QueueFront(Queue* qu);//返回队头元素
QDataType QueueBack(Queue* qu);//返回队尾元素

size_t QueueSize(Queue* qu);//队列长度

bool QueueEmpty(Queue* qu);//判断队列是否为空

        //Queue.c

#include"Queue.h"
void QueueInit(Queue* qu)//初始化栈
{
	qu->_head = qu->_tail = NULL;
}
void QueueDestory(Queue* qu)//摧毁栈
{
	//确保指针有效
	assert(qu);
	QueueNode* cur = qu->_head;
	while (cur)
	{
		QueueNode* next = cur->_next;
		free(cur);
	}
}
void QueuePush(Queue* qu,QDataType data)//入队
{
	if (qu->_head == NULL)
	{
		qu->_head = (QueueNode*)malloc(sizeof(QueueNode));
		qu->_tail = qu->_head;
		qu->_head->_next = NULL;
		qu->_head->_data = data;
	}
	else
	{
		//尾部入数据
		QueueNode* cur = qu->_tail;
		QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
		cur->_next = newNode;
		newNode->_next = NULL;
		qu->_tail = newNode;
		newNode->_data = data;
	}
}
void QueuePop(Queue* qu)//出队
{
	//队头出数据
	QueueNode* head = qu->_head;
	qu->_head = head->_next;
	free(head);
}
QDataType QueueFront(Queue* qu)//返回队头元素
{
	return qu->_head->_data;
}
QDataType QueueBack(Queue* qu)//返回队尾元素
{
	return qu->_tail->_data;
}
size_t QueueSize(Queue* qu)//队列长度
{
	assert(qu);//确保指针存在
	QueueNode* cur = qu->_head;
	size_t size = 0;
	while (cur)
	{
		++size;
		cur = cur->_next;
	}
	return size;
}
bool QueueEmpty(Queue* qu)//判断队列是否为空
{
	return !qu->_head;
}

        //Test.c

#include"BTree.h"
void TestBTreeNode()
{
	BTreeNode* root = NULL;
	root = CreatBinaryTree(root);
	/*PreOrder(root);
	printf("\n");
	InOrder(root);
	printf("\n");

	PostOrder(root);*/
	
	//printf("%d\n", BinaryTreeSize(root));
	printf("%d\n", BinaryTreeLeafSize(root));
	printf("%d\n", BinarTreelen(root));
	//LevelOrder(root);
	printf("%d\n", BinaryTreeComplete(root));
	printf("%d\n", BinaryTreeLevelKSize(root, 3));
	BinaryTreeDestory(root);
	root = NULL;//根节点置空,防止野指针的问题
}
int main()
{
	TestBTreeNode();
	
}

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

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

相关文章

AP5192 DC-DC降压恒流LED汽车灯 LED长条灯 汽车雾灯驱动IC

AP5192是一款PWM工作模式,高效率、外围简单、 内置功率MOS管&#xff0c;适用于4.5-100V输入的高精度 降压LED恒流驱动芯片。最大电流1.5A。 AP5192可实现线性调光和PWM调光&#xff0c;线性调光 脚有效电压范围0.55-2.6V. AP5192 工作频率可以通过RT 外部电阻编程 来设定&…

hadoop大数据集群中更换磁盘,balance的速度缓慢问题(解决)

hadoop大数据集群中更换磁盘&#xff0c;balance的速度缓慢问题&#xff08;解决&#xff09; 看现象只有4个bloucks在执行的 调整参数&#xff1a; 增大配置参数&#xff0c;观察重新负载的速度 修改配置文件 hdfs-site.xml dfs.datanode.balance.max.concurrent.moves100 …

JavaScript中的事件委托(event delegation)

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ JavaScript事件委托⭐ 事件冒泡&#xff08;Event Bubbling&#xff09;⭐ 事件委托的优点⭐ 如何使用事件委托⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启…

什么是AJAX?如何使用原生JavaScript搭建AJAX请求?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ AJAX&#xff08;Asynchronous JavaScript and XML&#xff09;⭐ 原生JavaScript中的AJAX请求1. 创建XMLHttpRequest对象2. 配置请求3. 设置回调函数4. 发送请求 ⭐ 完整示例⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开…

RPC框架的核心是什么

文章目录 什么是 RPC封装的艺术&#xff08;如何隐藏底层逻辑&#xff09;协议的实现序列化和反序列化&#xff08;编解码&#xff09;总结 什么是 RPC 首先思考这样一个问题&#xff0c;假设你不知道任何框架&#xff0c;现在有两台机器&#xff0c;每台机器上有一个服务&…

由Android10适配到Android12时遇到相关编译失败问题

最近Android系统各大应用商店联合发出公告&#xff0c;处于个人隐私安全考虑&#xff0c;强制APP适配到Android 11及以上版本。下面是其中应用市场的公告&#xff08;顺带提醒没适配的同学&#xff09;&#xff1a; 适配前的开发环境 名称版本Android studioGiraffe | 2022.3…

行业追踪,2023-08-29

自动复盘 2023-08-29 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

<AMBA总线篇> AXI总线协议介绍

目录 01 AXI协议简介 AXI协议特性 AXI协议传输特性 02 AXI协议架构 AXI协议架构 write transaction(写传输) read tramsaction(读传输) Interface and interconnect 典型的AXI系统拓扑 03 文章总结 大家好&#xff0c;这里是程序员杰克。一名平平无奇的嵌入式软件工程…

requests之post请求data传参和json传参区别

问题描述 在一次接口post测试请求传参异常的记录 print(header)rp requests.post(EvnUrlConfig.getUrl(url),headersheader,datauserDevcieParam)传输到后台服务器报了异常 原因分析&#xff1a; 显而易见我的请求头的content-type类型有异常了&#xff0c;但我明明传的是app…

(Windows )本地连接远程服务器(Linux),免密码登录设置

在使用VScode连接远程服务器时&#xff0c;每次打开都要输入密码&#xff0c;以及使用ssh登录或其它方法登录&#xff0c;都要本地输入密码&#xff0c;这大大降低了使用感受&#xff0c;下面总结了免密码登录的方法&#xff0c;用起来巴适得很&#xff0c;起飞。 目录 PowerSh…

CON021 9200-00006N处理器

时钟速度&#xff1a;时钟速度是处理器的工作频率&#xff0c;通常以赫兹&#xff08;Hz&#xff09;表示。它决定了处理器每秒钟可以执行多少个指令。较高的时钟速度通常意味着更快的性能。 核心数&#xff1a;现代处理器通常有多个核心&#xff0c;每个核心都可以独立执行任…

泊松回归和地理加权泊松回归

01 泊松回归 泊松回归(Poisson Regression)是一种广义线性模型,用于建立离散型响应变量(计数数据)与一个或多个预测变量之间的关系。它以法国数学家西蒙丹尼泊松(Simon Denis Poisson)的名字命名,适用于计算“事件发生次数”的概率,比如交通事故发生次数、产品缺陷数…

element-ui分析

目录解析 element ├── github // 存放了elementui贡献指南&#xff0c;issue 和 PR模板 ├── build // 存放打包相关的配置文件 ├── examples // 组件相关示例demo ├── packages // 组件源码 ├── src // 存放入口文件和一些工具辅助函数 ├── test // 单元测试…

CATIA Composer R2023安装教程

软件下载 软件&#xff1a;CATIA Composer版本&#xff1a;2023语言&#xff1a;简体中文大小&#xff1a;1.82G安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.60GHz 内存8G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pa…

第三节 函数

第三节 函数 目录 一&#xff0e; 函数是什么&#xff1f;二&#xff0e; C语言中函数的分类1. 库函数&#xff1a;2. 自定义函数 三&#xff0e; 函数的参数1. 实际参数&#xff08;实参&#xff09;&#xff1a;2. 形式参数&#xff08;形参&#xff09;&#xff1a; 四&…

【计算机视觉】YOLO 入门:训练 COCO128 数据集

一、COCO128 数据集 我们以最近大热的YOLOv8为例&#xff0c;回顾一下之前的安装过程&#xff1a; %pip install ultralytics import ultralytics ultralytics.checks()这里选择训练的数据集为&#xff1a;COCO128 COCO128是一个小型教程数据集&#xff0c;由COCOtrain2017中…

【pyqt5界面化工具开发-9】触发事件的绑定 信号-槽

目录 0x00 前言&#xff1a; 一、基础代码布局 二、添加逻辑代码 三、触发事件绑定逻辑代码 0x00 前言&#xff1a; 1.信号(signal) 事件(点击、关闭等状态发生改变的触发事件) 2.槽( slot) 捕获信号后--->执行相应的逻辑代码 3.信号-槽 链接 为实现&#xff1a;触发事件…

java-初识Servlet,Tomcat,JDBC

文章目录 前言一、ServletServlet 生命周期Servlet 实例Servlet 过滤器 二、TomcatJDBCJDBC连接数据库实例 总结 前言 java入门须知的重要概念/名词/技术 等 一、Servlet Servlet是Java Web开发中的一个核心组件&#xff0c;它是基于Java语言编写的服务器端程序&#xff0c;…

【CI/CD技术专题】「Docker实战系列」本地进行生成镜像以及标签Tag推送到DockerHub

背景介绍 Docker镜像构建成功后&#xff0c;只要有docker环境就可以使用&#xff0c;但必须将镜像推送到Docker Hub上去。创建的镜像最好要符合Docker Hub的tag要求&#xff0c;因为在Docker Hub注册的用户名是liboware&#xff0c;最后利用docker push命令推送镜像到公共仓库…

2023视觉SLAM的研究改进方向

1. 增加对动态场景的鲁棒性&#xff08;动态SLAM&#xff09; 传统的视觉SLAM算法通常假设场景是静态的&#xff0c;这种假设对于动态场景是不适用的。在动态场景中&#xff0c;物体的位置和姿态会发生变化&#xff0c;这会对视觉SLAM算法的精度和鲁棒性造成很大的影响。因此&…