数据结构:实现链式结构二叉树(Tree) 手把手带你入门数据结构~

文章目录

  • 前言
  • 一、链式结构二叉树的概念
    • 1. 定义
    • 2. 节点结构
    • 3. 操作
    • 4. 优势与劣势
  • 二、链式结构二叉树的实现
    • 1. 树结构的定义
    • 2. 树的遍历
      • (1)前序遍历
      • (2)中序遍历
      • (3)后序遍历
    • 3. 二叉树结点个数
    • 4. 二叉树叶子结点个数
    • 5. 二叉树第k层结点个数
    • 6. 二叉树的深度/高度
    • 7. 二叉树查找值为x的结点
    • 8. 二叉树的销毁
  • 三、层序遍历
    • 1. 层序遍历的概念
    • 2. 层序遍历的实现
  • 四、判断二叉树是否为完全二叉树
  • 总结


前言


在这里插入图片描述

`

一、链式结构二叉树的概念

链式结构二叉树是一种使用链表(或指针)来表示二叉树的数据结构。与数组表示的二叉树不同,链式结构能够动态地管理内存,适合存储动态变化的数据。下面是链式结构二叉树的基本概念:

1. 定义

  • 二叉树:每个节点最多有两个子节点(左子节点和右子节点)。
  • 链式结构:每个节点通过指针链接到其子节点。

2. 节点结构

一个二叉树的节点通常包含三个部分:

  • 数据域:存储节点的数据(如整数、字符等)。
  • 左指针:指向左子节点的指针。
  • 右指针:指向右子节点的指针。

在C语言中,可以用以下结构体定义一个二叉树节点:

typedef struct TreeNode {int data;                  // 数据域struct TreeNode* left;    // 左子节点指针struct TreeNode* right;   // 右子节点指针
} TreeNode;

3. 操作

常见的操作包括:

  • 插入:将新节点插入到树中,通常根据某种顺序(如二叉搜索树的性质)。
  • 删除:从树中删除一个节点,需考虑不同情况(如叶子节点、只有一个子节点的节点、两个子节点的节点)。
  • 遍历:遍历树的节点,包括前序遍历、中序遍历和后序遍历。

4. 优势与劣势

  • 优势

    • 动态内存分配,适合变化频繁的数据。
    • 节省空间:不需要为树的每一层预留空间。
  • 劣势

    • 访问速度慢于数组,因为需要遍历指针。
    • 存储结构复杂,维护指针可能引入错误。

这种结构在许多算法和数据处理问题中非常有用,比如二叉搜索树、平衡树(如 AVL 树和红黑树)等。


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

1. 树结构的定义

前面说到树的定义,首先这个链表的结点要储存自身的数据,接下来每一个节点都有两个指针。一个指向它的左孩子,另一个指向它的右孩子。

有了这两个指针,树才能连接起来。
在这里插入图片描述

//定义二叉树的链式结构
//二叉树结点的结构typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;         //左孩子struct BinaryTreeNode* right;        //右孩子
}BTNode;

2. 树的遍历

(1)前序遍历

//前序遍历
void PreOrder(BTNode* root);

什么是前序遍历呢?
我们来看这一张图
在这里插入图片描述

对于1这个结点,他是当前的根节点,他拥有左右两个孩子 2,3

在这里插入图片描述

对于这个2结点当根节点,他也有他的两个孩子----4,NULL.

对于前序遍历来说,就是按照 根----左----右的顺序进行遍历。
如下图所示:
在这里插入图片描述

对于每个根节点来说,它的遍历方法都是 根----左----右,我们有时候可能认为遍历到一个结点的左孩子或者右孩子就要回去,其实是不对的。因为它的孩子的遍历方式也是 根----左----右

因此,前序遍历是递归实现的。

//前序遍历 ------根左右
void PreOrder(BTNode* root)
{if (root == NULL){return;}printf("%d ", root->data);PreOrder(root->left);PreOrder(root->right);
}

在这里插入图片描述

我们结合代码,再从函数栈帧的角度看一下递归具体的实现
在这里插入图片描述


(2)中序遍历

//中序遍历------左根右
void InOrder(BTNode* root);

不同于中序遍历,中序遍历的顺序是 左----根----右
在这里插入图片描述

对于每一个节点,先遍历它的左孩子,在遍历它自己,最后遍历它的右孩子

如下图所示:
在这里插入图片描述

故中序遍历的顺序是4–2–1–3.

//中序遍历------左根右
void InOrder(BTNode* root)
{if (root == NULL){return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}

(3)后序遍历

//后序遍历------左右根
void PostOrder(BTNode* root);

区别于前两种遍历方式,后序遍历的顺序是左----右---根
在这里插入图片描述

对于每一个节点,先遍历它的左孩子,在遍历它的右孩子,最后遍历它自己。

如下图所示:
在这里插入图片描述
故后序遍历的顺序为 4–2–3–1.

//后序遍历------左右根
void PostOrder(BTNode* root)
{if (root == NULL){return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);
}

3. 二叉树结点个数

// ⼆叉树结点个数
// 一个结点的个数等于其左右子树结点个数之和加上它本身
int BinaryTreeSize(BTNode* root);

二叉树的结点个数等于什么呢?
我们很容易就能想到一种方法:
定义一个count来计数,我们呢可以使用中序遍历,每遍历到一个有效的结点,就让count++最后返回count。

这样的方法可不可行呢?

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

这样看似解决了问题,但实际上是不行的。
归根结底的原因是形参的传递不能改变实参。
在这里插入图片描述
那我们就有疑问了,既然你形参的改变不影响实参,那我直接传地址啊?
这样可不可行呢?
在这里插入图片描述
看似传了地址就解决了这个问题,但我们如果连续打印两次树中结点个数的话,你会发现节点个数又多加了一次。
这是因为地址的内容被我们改变,下次再用size的初始值就不是0了,需要我们手动释放。因此这种方法也不好。

那我们该怎么做呢?
可以这样想, 一个结点的个数等于其左右子树结点个数之和加上它本身。
在这里插入图片描述
只要分别找到每一个结点左子树和右子树结点之和,就是我们的答案。
将大事化小,当root为NULL的时候结束,不正是递归的思想吗?

// ⼆叉树结点个数
// 一个结点的个数等于其左右子树结点个数之和加上它本身
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

4. 二叉树叶子结点个数

// ⼆叉树叶⼦结点个数
// 二叉树叶子结点的个数等于二叉树左右子树叶子结点的个数之和
int BinaryTreeLeafSize(BTNode* root);

叶子结点就是树每一个分支最下层的结点,它没有子结点。

类比上述的方法,我们可以将二叉树叶子结点的个数看成二叉树左右子树叶子结点的个数之和。
在这里插入图片描述

// ⼆叉树叶⼦结点个数
// 二叉树叶子结点的个数等于二叉树左右子树叶子结点的个数之和
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);
}

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

// ⼆叉树第k层结点个数
// ⼆叉树第k层结点个数等于左右子树第K层的结点个数和
// 注意是K == 1
int BinaryTreeLevelKSize(BTNode* root, int k);

第K层的结点个数可以看成左子树第K层和右子树第K层的结点数之和。

在这里插入图片描述

// ⼆叉树第k层结点个数等于左右子树第K层的结点个数和
// 注意是K == 1
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

6. 二叉树的深度/高度

// ⼆叉树的深度/⾼度
// 二叉树的深度要分别比较左右子树最深的那一个
// 每一个结点的深度都等于其左右子树中最深的那个
int BinaryTreeDepth(BTNode* root);

二叉树的深度就是二叉树所有完整分支最长的那一个结点的个数。

二叉树的深度就等于左子树和右子树中最长的那一个。每一个结点都可以看成由他的左子树和右子树的最长深度,最终反回到root == NULL;

在这里插入图片描述

// ⼆叉树的深度/⾼度
// 二叉树的深度要分别比较左右子树最深的那一个
// 每一个结点的深度都等于其左右子树中最深的那个
int BinaryTreeDepth(BTNode* root)
{if (root == NULL){return 0;}int leftDepth = BinaryTreeDepth(root->left);int rightDepth = BinaryTreeDepth(root->right);return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;

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

// ⼆叉树查找值为x的结点
// 左根右的方式找
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

我们使用中序遍历的方法递归树就好,直接找到等于x的位置,返回当前结点,没找到就返回NULL;

在这里插入图片描述

// ⼆叉树查找值为x的结点
// 左根右的方式找
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* leftFind = BinaryTreeFind(root->left, x);if (leftFind){return leftFind;}BTNode* rightFind = BinaryTreeFind(root->right, x);if (rightFind){return rightFind;}return NULL;
}

8. 二叉树的销毁

// ⼆叉树销毁
// 以左右根的方式销毁
// 根被销毁了因此要传二级指针
void BinaryTreeDestroy(BTNode** root);

首先,销毁要使用后序遍历的方法,因为如果根结点先被销毁,就一定找不到他的左右孩子了。

接下来一定要用二级指针,因为最终的树的根结点被销毁,我们传过来的root要改变,如果只用一级指针的话形参的改变是无法影响到实参的,因此必须要传一级指针的地址。

在这里插入图片描述

// ⼆叉树销毁
// 以左右根的方式销毁
// 根被销毁了因此要传二级指针
void BinaryTreeDestroy(BTNode** root)
{if (*root == NULL){return;}BinaryTreeDestroy(&((*root)->left));BinaryTreeDestroy(&((*root)->right));free(*root);*root = NULL;
}

三、层序遍历

1. 层序遍历的概念

除了先序遍历、中序遍历、后序遍历外,还可以对⼆叉树进⾏层序遍历。设⼆叉树的根结点所在层数为1,层序遍历就是从所在⼆叉树的根结点出发,⾸先访问第⼀层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,⾃上⽽下,⾃左⾄右逐层访问树的结点的过程就是层序遍历。

实现层序遍历需要额外借助数据结构:队列


2. 层序遍历的实现

实现层序遍历首先我们要有一个队列。

如下图:
在这里插入图片描述

第一步: 将树根结点插入队列。
在这里插入图片描述
第二步: 取出队头元素,如果它的左右子树不为空就将其按顺序入队。

在这里插入图片描述
第三步: 继续出队头元素,将出队列元素的左右孩子如果不为空让其入队。
在这里插入图片描述
第四步: 继续重复上一步骤,出队头元素,将出队列元素的左右孩子如果不为空让其入队。

此时4这个结点没有左右孩子,因此不用入队

在这里插入图片描述
第五步:重复上述步骤直到队列为空。

这里我们就完成了层序遍历,遍历顺序为 1----2----3----4

在这里插入图片描述

具体的实现为:

// 层序遍历
// 层序遍历需要借助队列,根出队,左右子树放入其中,直到一个左右子树都为空
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);//先插入根结点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);}}QueueDestroy(&q);
}

四、判断二叉树是否为完全二叉树

如何判断二叉树是否是完全二叉树呢?

这里J桑直接给出方法:
同样需要借助队列,不过这次就算是结点的左右孩子无论它是否为NULL,都将它插入队列,循环出队列头,直到跳出第一个循环时,如果队列中所有的元素都是NULL,那么树就是完全二叉树,如果有不为NULL的元素,那他就不是完全二叉树。

第一步: 将树根结点插入队列。
在这里插入图片描述
第二步: 队头元素出队,取其左右孩子入队
在这里插入图片描述
第三步: 重复上述步骤
在这里插入图片描述
在这里插入图片描述
此时循环结束,队列中有非空元素,因此该树不是完全二叉树

具体实现为:

//判断二叉树是否为完全二叉树
//---借助队列,不管他的孩子是不是空都放到队列中,满二叉树结束时一定队列里全为空
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}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;
}

总结

到这里我们链式结构的二叉树(BinaryTree)就实现完成啦~

下面是完成代码:(记得写Queue哦~)

//Heap.c
#include"Tree.h"
#include"Queue.h"//前序遍历 ------根左右
void PreOrder(BTNode* root)
{if (root == NULL){return;}printf("%d ", root->data);PreOrder(root->left);PreOrder(root->right);
}//中序遍历------左根右
void InOrder(BTNode* root)
{if (root == NULL){return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}//后序遍历------左右根
void PostOrder(BTNode* root)
{if (root == NULL){return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);
}// ⼆叉树结点个数
// 一个结点的个数等于其左右子树结点个数之和加上它本身
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}// ⼆叉树叶⼦结点个数
// 二叉树叶子结点的个数等于二叉树左右子树叶子结点的个数之和
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);
}// ⼆叉树第k层结点个数
// ⼆叉树第k层结点个数等于左右子树第K层的结点个数和
// 注意是K == 1
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}// ⼆叉树的深度/⾼度
// 二叉树的深度要分别比较左右子树最深的那一个
// 每一个结点的深度都等于其左右子树中最深的那个
int BinaryTreeDepth(BTNode* root)
{if (root == NULL){return 0;}int leftDepth = BinaryTreeDepth(root->left);int rightDepth = BinaryTreeDepth(root->right);return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}// ⼆叉树查找值为x的结点
// 左根右的方式找
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* leftFind = BinaryTreeFind(root->left, x);if (leftFind){return leftFind;}BTNode* rightFind = BinaryTreeFind(root->right, x);if (rightFind){return rightFind;}return NULL;
}// ⼆叉树销毁
// 以左右根的方式销毁
// 根被销毁了因此要传二级指针
void BinaryTreeDestroy(BTNode** root)
{if (*root == NULL){return;}BinaryTreeDestroy(&((*root)->left));BinaryTreeDestroy(&((*root)->right));free(*root);*root = NULL;
}// 层序遍历
// 层序遍历需要借助队列,根出队,左右子树放入其中,直到一个左右子树都为空
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);//先插入根结点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);}}QueueDestroy(&q);
}//判断二叉树是否为完全二叉树
//---借助队列,不管他的孩子是不是空都放到队列中,满二叉树结束时一定队列里全为空
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}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;
}
//Heap.h
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//定义二叉树的链式结构
//二叉树结点的结构typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;         //左孩子struct BinaryTreeNode* right;        //右孩子
}BTNode;//前序遍历
void PreOrder(BTNode* root);//中序遍历
void InOrder(BTNode* root);//后序遍历 
void PostOrder(BTNode* root);// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root);// ⼆叉树叶⼦结点个数
int BinaryTreeLeafSize(BTNode* root);// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k);//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root);// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);// ⼆叉树销毁
void BinaryTreeDestroy(BTNode** root);//层序遍历
void LevelOrder(BTNode* root);//判断二叉树是否为完全二叉树
bool BinaryTreeComplete(BTNode* root);
//测试样例
#include"Tree.h"BTNode* buyNode(BTDataType x)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->left = newnode->right = NULL;return newnode;
}void test01()
{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 = node3;node2->right = node4;//node2->right = node5;//node3->left = node6;//PreOrder(node1);//printf("\n");//InOrder(node1);//printf("\n");//PostOrder(node1);//int size = 0;//BinaryTreeSize(node1, &size);//printf("size : %d\n", size);size = 0;//BinaryTreeSize(node1, &size);//printf("size : %d\n", size);//printf("size:%d\n", BinaryTreeSize(node1));//printf("size:%d\n", BinaryTreeSize(node1));//printf("size:%d\n", BinaryTreeSize(node1));//printf("leaf size: %d\n", BinaryTreeLeafSize(node1));//printf("第K层size : %d\n", BinaryTreeLevelKSize(node1, 2));//printf("depth/height:%d\n", BinaryTreeDepth(node1));//BTNode* find =BinaryTreeFind(node1, 33);//printf("%s\n", find == NULL ? "未找到!" : "找到了!");//LevelOrder(node1);bool ret = BinaryTreeComplete(node1);printf("%s\n", ret == false ? "不是完全二叉树" : "是完全二叉树");BinaryTreeDestroy(&node1);
}int main()
{test01();return 0;
}

谢谢大家~

真相永远只有一个!

在这里插入图片描述

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

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

相关文章

828华为云征文 | 基于华为云Flexus云服务器X搭建部署——AI知识库问答系统(使用1panel面板安装)

&#x1f680;对于企业来讲为什么需要华为云Flexus X来搭建自己的知识库问答系统&#xff1f;&#xff1f;&#xff1f; 【重塑知识边界&#xff0c;华为云Flexus云服务器X引领开源问答新纪元&#xff01;】 &#x1f31f; 解锁知识新动力&#xff0c;华为云Flexus云服务器X携…

力扣 简单 876.链表的中间结点

文章目录 题目介绍题解 题目介绍 题解 法一&#xff1a; class Solution {public ListNode middleNode(ListNode head) {ListNode cur head;int n 0;while (cur ! null) {n;cur cur.next;}ListNode curr head;for (int i 0; i < n / 2; i) {curr curr.next;}return …

C++ 红黑树封装map和set

目录 前言 1.红黑树的改造 1.1主题框架 1.2迭代器 operator &#xff08;&#xff09; begin&#xff08;&#xff09;和end&#xff08;&#xff09; 1.3红黑树相关接口的改造 Find函数的改造 Insert 函数的改造 2.红黑树改造的完整代码 3.Set的封装 4.Map的封装 前…

freeRDP OPenssl

libusb需要下载 我使用的是VS2019编译 所以需要include 与vs2019 在cmake里面修改路径 C:/Users/JPM/source/repos/freeRDP/FreeRDP-stable-2.0/libusb-1.0.24/include/libusb-1.0 C:/Users/JPM/source/repos/freeRDP/FreeRDP-stable-2.0/libusb-1.0.24/VS2019/MS64/static/l…

pycharm24.2运行框中无法输入中文但是可以粘贴中文、输入英文、数字

文章目录 一、问题描述二、解决办法解决办法1解决办法2解决办法3 一、问题描述 pycharm24.2版本中运行框中无法输入中文&#xff0c;敲击空格键发现中文并没有输入进去: 但是可以粘贴中文: 输入英文、数字没有问题。 二、解决办法 该问题为pycharm24.2版本问题。 解决办法…

AI无人直播新标杆,一站式直播解决方案:打造专属舞台!

AI无人直播新标杆&#xff0c;一站式直播解决方案&#xff1a;打造专属舞台&#xff01; 在数字化浪潮的汹涌澎湃中&#xff0c;AI技术正以前所未有的速度渗透至各行各业&#xff0c;其中&#xff0c;直播行业作为数字内容传播的重要阵地&#xff0c;正经历着一场由AI引领的深刻…

OpenHarmony(鸿蒙南向)——平台驱动指南【MIPI CSI】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 概述 功能简介 CSI&#xff08;Camera Serial Interface&#xf…

数字人形象自定义制作:readyplayer

网址&#xff1a; https://readyplayer.me/ 支持上传照片和拍照&#xff0c;会自动识别变成卡通风格 其他选项是配置选项&#xff1a;穿着、样貌等 上面弄好后右上角点击next&#xff0c;创建的模型可以下载3d glb文件 glb文件在线打开&#xff1a; https://gltf-viewer.d…

SpinalHDL之语义(Semantic)(一)

本文作为SpinalHDL学习笔记第六十九篇,介绍SpinalHDL的赋值(Assignments)。 目录: 1.赋值(Assignments) 2.位宽检查(Width checking) 3.组合逻辑环路(Combinatorial loops) ⼀、赋值(Assignments) SpinalHDL中有多个赋值运算: //因为硬件的并发性, `a`的值⼀直是1 val a…

CrossOver24支持的游戏有那些

CrossOver刚刚更新了24版本&#xff0c;支持《地平线零之曙光》、《以撒的结合&#xff1a;重生》等游戏。一起来看看它有哪些更新吧&#xff01; 一、功能优化 - 更新 Wine 至最新的稳定版 Wine 9.0&#xff0c;引入了 7000多个更新和针对各种软件游戏的优化。 - 更新 Wine M…

Azure Kinect 人体跟踪关节

Azure Kinect 人体跟踪关节 azure kinect dk 提取人体骨骼 要在Azure Kinect DK上提取人体骨骼&#xff0c;你需要使用Azure Kinect SDK和OpenPose库。以下是一个简化的代码示例&#xff0c;展示如何集成这两个库来提取骨骼关键点&#xff1a; 首先&#xff0c;确保你已经安装…

【YashanDB知识库】decode函数中的子查询被不必要地多次执行

本文内容转自YashanDB官网&#xff0c;具体内容请见https://www.yashandb.com/newsinfo/7441387.html?templateId1718516 问题现象 客户向yashandb下发的SQL语句执行时间超过6分钟仍未出结果 问题的风险及影响 SQL语句性能慢&#xff0c;影响客户业务 问题影响的版本 所…

蜂窝物联网全网通sim卡切网技术方案软硬件实现教程(设备根据基站信号质量自动切网)

01 物联网系统中为什么要使用三合一卡 三合一卡为用户解决了单一运营商网络无法全覆盖的缺陷&#xff0c;避免再次采购的经济成本以及时间成本和因没有信号设备停止工作造成的损失&#xff0c;保证仅需一次采购并提高设备工作效率和入网活跃度。例如下面地区的设备&#xff0…

RPA自动化流程机器人有哪些优势?

在数字化快速推进的大背景下&#xff0c;人工智能正在以前所未有的速度改变着生活和生产方式&#xff0c;而RPA自动化流程机器人作为其中一种最重要的革命性的技术&#xff0c;已经成为企业数字化中不可或缺的重要力量&#xff0c;让员工加速从“重复性工作”中摆脱出来。 金智…

【芋道源码】gitee很火的开源项目pig——后台管理快速开发框架使用笔记(微服务版之本地开发环境篇)

后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09; 后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09; 后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09;前言一、如何获取项目&#…

推荐十款主流的采购管理系统,为企业选型提供参考!

大家都明白采购对制造型企业的重要性&#xff0c;但是在面对市场上琳琅满目的采购管理系统企业却不知道该如何选择&#xff0c;不要担心。 本篇文章将对市面上知名的采购管理系统进行综合测评&#xff0c;深入剖析这些平台的特点与优势。看完这篇内容&#xff0c;你将对不同采…

前台项目启动/打包报错 Error: error:0308010C:digital envelope routines::unsupported

在package.json中修改启动/打包语句 如图&#xff0c;我这里是打包时候报错&#xff0c;就在build里前面加上 set NODE_OPTIONS--openssl-legacy-provider && 再次打包&#xff0c;成功。

【OpenAI o1思维链CoT必看论文】谷歌“思维链提示“让AI更懂人类推理

原创 超 超的闲思世界 AI的推理能力正迎来一场重大突破。谷歌大脑团队最新开发的"思维链提示"方法&#xff0c;让大型语言模型在复杂推理任务上展现出惊人的进步。这项创新技术无需对模型进行额外训练&#xff0c;却能显著提升AI的推理能力&#xff0c;让机器的思…

微电网与大电网主动同步控制

前言 大电网正常运行时&#xff0c;微电网通过大电网得到正常的电压频率参数支撑&#xff0c;大电网故障时&#xff0c;微电网的电压和频率支撑需要通过分布式电源提供&#xff0c;从而保持自身独立运行。分布式电源提供的电压信息会因为自身的下垂特性随本地负荷的改变不断变…

vue 中获取数值但是只获取到了 Promise 属性,获取不到其中的值

左边的请求能获取到数据&#xff0c;右边的不行&#xff1f; 改成这样即可