草庐IT

【数据结构】二叉树的实现

7昂7. 2024-06-27 原文

文章目录

一、二叉树的概念

一棵二叉树是结点的一个有限集合,该集合分为两点: 一是为空和二是由一个根节点加上两棵别称为左子树和右子树的二叉树组成从图上看出有2个性质:

  1. 二叉树不存在度大于2的结点
  2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

对于任意的二叉树都是由以下几种情况复合而成的:

二、特殊的二叉树

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

2、完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。要注意的是满二叉树是一种特殊的完全二叉树。

如图:

三、二叉树的性质

二叉树数的性质有五点:

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

四、二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
1、顺序存储:
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

2、链式存储
二叉树的链式存储结构:是用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,这只讲二叉链。

五、二叉树链式结构实现

(1)创建结构体


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

先使用typedef int BTDataType,是为了方便改类型,在结构体里创建3个成员变量,BTDataType a,表示结点数据。struct BinaryTreeNode* left表示存储结点左孩子的地址,struct BinaryTreeNode* right表示存储结点右孩子的地址。

(2)具体函数实现及实现

1.0 二叉树的构建

BTNode* BuyBTNode(BTDataType x)//二叉树的构建
{
	 BTNode* node = (struct BTNode*) malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}

二叉树构建也就是为每一个结点申请空间,用malloc开辟,地址放在node变量中,如果改指针为空就exit,接着把数据值放到结点数据里,把2它的左右孩子都置为空。

1.1 二叉树的销毁

void BinaryTreeDestory(BTNode* root)//二叉树的销毁
{
	if (root)
	{
		BinaryTreeDestory(root->left);
		BinaryTreeDestory(root->right);
		free(root);
	}

}

二叉树销毁根节点为空就不用销毁,反之,先递归释放左子树,再递归释放右子树,最后释放结点,如果先释放结点,会找不到左右子树的地址。

1.2 二叉树节点个数


int BinaryTreeSize(BTNode* root)// 二叉树节点个数
{
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;

}

求二叉树结点个数需要递归左子树和右子树,去建立栈帧,直到它们的子树为空返回0,之后再加1,表示记录一个结点,然后从下到上开始return返回,栈帧逐层销毁,最终返回结点个数。

1.3 二叉树叶子结点个数

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

求叶子结点个数,也就是求同时没有左右孩子的结点,思路还是递归左子树和右子树,之和即为最终的叶子节点个数。如果结点为空直接返回0,如果它们的左右孩子都为空,则表示它是叶子结点,返回1,最后递归返回总个数。

1.4 二叉树第k层节点个数

int BinaryTreeLevelKSize(BTNode* root, int k)// 二叉树第k层节点个数
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

求第k层个数,从二叉树结构可以发现,如果k=1,他自己就是第k层,如果k>1,第k层就等于左子树k-1层个数加右子树第k-1层个数,相对位置往下走,层层递进,一直减到1,就是第k层。同样递归左右子树,最后返回最终第k层结点个数。

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

struct BinaryTreeNode* BinaryTreeFind(BTNode* root, BTDataType x)// 二叉树查找值为x的节点
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	struct BinaryTreeNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
		return ret1;
	struct BinaryTreeNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
		return ret2;
	
}

二叉树查找,如果结点为空,返回空,如果找到直接返回结点指针,没找到继续分别递归左右子树,为了防止返回出现随机值,这里需要接收一下指针,如果找到返回该结点指针。

1.6 二叉树的高度

int BinaryTreeHeight(BTNode* root)//二叉树的高度
{
	if (root == NULL)
	{
		return 0;
	}
	int leftheight = BinaryTreeHeight(root->left);
	int rightheight = BinaryTreeHeight(root->right);
	return leftheight > rightheight ? leftheight + 1:rightheight + 1;

}

求二叉树高度思路把问题还是分解为子问题,结点高度等于左子树高度和右子树高度大的那个一个加1,因为高度是最长的那条路径,分别递归左子树和右子树去比较,如果都为0,任取一个加1。最终返回最终高度。

1.7 二叉树前序遍历

void BinaryTreePrevOrder(BTNode* root)// 二叉树前序遍历 
{
	if (root == NULL)
	{
	
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

前序遍历更为简单,如果为空直接返回,接着打印根数据,再递归左子树,再递归右子树。

1.8 二叉树中序遍历

void BinaryTreeInOrder(BTNode* root)// 二叉树中序遍历
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d ", root->data);
	BinaryTreeInOrder(root->right);
}

中序遍历,如果为空直接返回,先递归左子树,再打印根数据,再递归右子树。

1.9 二叉树后序遍历

void BinaryTreePostOrder(BTNode* root)// 二叉树后序遍历
{
	if (root == NULL)
	{
		
		printf("NULL ");
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d ", root->data);
}

中序遍历,如果为空直接返回,先递归左子树,再打印根数据,再递归右子树。

2.0 层序遍历

void BinaryTreeLevelOrder(BTNode* root)// 层序遍历
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		QueuePop(&q);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

层序遍历在这用队列去实现,队列里面存地址。如果根节点不为空,就先把这个结点地址导入进去。之后用while循环来控制终止,队列不为空就进去,然后先把上次导入进去的队列头部数据保存起来放到front指针变量,便于删除在队列里删除它,还不影响导入它的左右孩子,左右孩子不为空则导入。最后释放队列。

2.1 判断二叉树是否是完全二叉树

bool BinaryTreeComplete(BTNode* root)// 判断二叉树是否是完全二叉树
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;

}

判断是否是完全二叉树的思路:是同样是用队列去实现,如果从队列出,遇到结点为空,就去判断后面的是否为空,后面只要出现一个非空就不是完全二叉树,后面全为空就是二叉树。
1、如果根结点不为空,就先导入队列中,接着同样是用while循环,里面是导入数据即结点地址,先把队列头保存起来,再pop掉,if语句为真,停止,说明遇到空了,否则继续导入数据包括空也导入进去。
2、接着再用whil循环,出到空后,继续往后出数。只要找到一个非空就返回false,直到循环结束,返回真,说明都是空。

(3)二叉树实现代码

(1)Queue.c

#include"Queue.h"
void QueueInit(Queue* qq)//队列初始化
{
	assert(qq);
	qq->head = qq->tail = NULL;
	qq->size = 0;

}

void QueuePush(Queue* qq, QDataType x)//入队列
{
	assert(qq);
    QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
	}
	newnode->data = x;
	newnode->next = NULL;
	if (qq->tail == NULL)
	{
		qq->head = qq->tail = newnode;
	}
	else
	{
		qq->tail->next = newnode;
		qq->tail = qq->tail->next;
	}
	qq->size++;
}

void QueuePop(Queue* qq)//出队列
{
	assert(qq);
	assert(!QueueEmpty(qq));
	if (qq->head->next == NULL)
	{
		free(qq->head);
		qq->head = qq->tail = NULL;
	}
	else
	{
		QNode* del = qq->head;
		qq->head = qq->head->next;
		free(del);
	}
	qq->size--;
	
}

QDataType QueueFront(Queue* qq)//取队列首元素
{
	assert(qq);
	assert(!QueueEmpty(qq));
	return qq->head->data;


}

QDataType QueueBack(Queue* qq)//取队列尾元素
{
	assert(qq);
	assert(!QueueEmpty(qq));
	return qq->tail->data;

}

int QueueSize(Queue* qq)//返回队列个数
{
	assert(qq);
	return qq->size;
}

bool QueueEmpty(Queue* qq)//判断是否为空
{
	assert(qq);
	return qq->head == NULL &&qq->tail == NULL;

}

void QueueDestroy(Queue* qq)//销毁队列
{
	assert(qq);
	QNode* cur = qq->head;
	while (cur)
	{
		QNode* del = cur;
		cur = cur->next;
		free(del);
	}
	qq->head = qq->tail = NULL;
	qq->size = 0;
}

(2)Queue.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

struct BinaryTreeNode;
typedef struct BinaryTreeNode* QDataType;

typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;
typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;
}Queue;
void QueueInit(Queue* qq);//队列初始化

void QueuePush(Queue* qq,QDataType x);//入队列

void QueuePop(Queue* qq);//出队列

QDataType QueueFront(Queue* qq);//取队列首元素

QDataType QueueBack(Queue* qq);//取队列尾元素

int QueueSize(Queue* qq);//返回队列个数

bool QueueEmpty(Queue* qq);//判断是否为空

void QueueDestroy(Queue* qq);//销毁队列

(3)test.c

#include"BinaryTree.h"
int main()
{
	BTNode* n1 = BuyBTNode(1);
	BTNode* n2 = BuyBTNode(2);
	BTNode* n3 = BuyBTNode(3);
	BTNode* n4 = BuyBTNode(4);
	BTNode* n5 = BuyBTNode(5);
	BTNode* n6 = BuyBTNode(6);
	BTNode* n7 = BuyBTNode(7);

	n2->right = n7;
	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n4->left = n5;
	n4->right = n6;

	BinaryTreePrevOrder(n1);//前序遍历
	printf("\n");

	BinaryTreeInOrder(n1);//中序遍历
	printf("\n");

	BinaryTreePostOrder(n1);//后序遍历
	printf("\n");

	printf("TreeSize:%d\n", BinaryTreeSize(n1));
	printf("TreeLeafSize:%d\n", BinaryTreeLeafSize(n1));
	printf("TreeHeight:%d\n", BinaryTreeHeight(n1));
	printf("TreeKLevelSize:%d\n", BinaryTreeLevelKSize(n1, 3));
	printf("TreeFind:%p\n", BinaryTreeFind(n1, 7));

	BinaryTreeLevelOrder(n1);

	return 0;
	
}

(4)BinaryTree.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

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

#include"Queue.h"

BTNode* BuyBTNode(BTDataType x);//二叉树的构建

void BinaryTreeDestory(BTNode** root);//二叉树的销毁

int BinaryTreeSize(BTNode* root);// 二叉树节点个数

int BinaryTreeLeafSize(BTNode* root);//二叉树叶子结点个数

int BinaryTreeLevelKSize(BTNode* root, int k);// 二叉树第k层节点个数

BTNode* BinaryTreeFind(BTNode* root, BTDataType x);// 二叉树查找值为x的节点

int BinaryTreeHeight(BTNode* root);//二叉树的高度

void BinaryTreePrevOrder(BTNode* root);// 二叉树前序遍历 

void BinaryTreeInOrder(BTNode* root);// 二叉树中序遍历

void BinaryTreePostOrder(BTNode* root);// 二叉树后序遍历

void BinaryTreeLevelOrder(BTNode* root);// 层序遍历

bool BinaryTreeComplete(BTNode* root);// 判断二叉树是否是完全二叉树

(5)BinaryTree.c

#include"BinaryTree.h"

BTNode* BuyBTNode(BTDataType x)//二叉树的构建
{
	 BTNode* node = (struct BTNode*) malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}

void BinaryTreeDestory(BTNode* root)//二叉树的销毁
{
	if (root)
	{
		BinaryTreeDestory(root->left);
		BinaryTreeDestory(root->right);
		free(root);
	}

}

int BinaryTreeSize(BTNode* root)// 二叉树节点个数
{
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 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);
}

int BinaryTreeLevelKSize(BTNode* root, int k)// 二叉树第k层节点个数
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

struct BinaryTreeNode* BinaryTreeFind(BTNode* root, BTDataType x)// 二叉树查找值为x的节点
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	struct BinaryTreeNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
		return ret1;
	struct BinaryTreeNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
		return ret2;
	
}

int BinaryTreeHeight(BTNode* root)//二叉树的高度
{
	if (root == NULL)
	{
		return 0;
	}
	int leftheight = BinaryTreeHeight(root->left);
	int rightheight = BinaryTreeHeight(root->right);
	return leftheight > rightheight ? leftheight + 1:rightheight + 1;

}

void BinaryTreePrevOrder(BTNode* root)// 二叉树前序遍历 
{
	if (root == NULL)
	{
	
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

void BinaryTreeInOrder(BTNode* root)// 二叉树中序遍历
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d ", root->data);
	BinaryTreeInOrder(root->right);
}

void BinaryTreePostOrder(BTNode* root)// 二叉树后序遍历
{
	if (root == NULL)
	{
		
		printf("NULL ");
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d ", root->data);
}

void BinaryTreeLevelOrder(BTNode* root)// 层序遍历
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		QueuePop(&q);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

bool BinaryTreeComplete(BTNode* root)// 判断二叉树是否是完全二叉树
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;

}

(4)二叉树测试结果

有关【数据结构】二叉树的实现的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  5. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  6. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  7. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  8. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  9. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  10. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

随机推荐