数据结构之二叉树的超详细讲解(3)--(二叉树的遍历和操作)

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

数据结构之二叉树的超详细讲解(3)--(二叉树链式结构的实现)

收录于专栏【数据结构初阶】
本专栏旨在分享学习数据结构学习的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1.前置说明

2.二叉树的遍历

2.1 前序、中序以及后序遍历

代码实现: 

2.1.1前序遍历:

 2.1.2中序遍历:

2.1.3后序遍历:

力扣oj练习

2.2 层序遍历

代码实现: 

练习:

3.结点个数以及高度等

3.1二叉树节点个数

3.2二叉树叶子节点个数

3.3二叉树第k层节点个数

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

3.5二叉树的高度

4.二叉树的创建和销毁

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

4.2二叉树销毁

4.3判断二叉树是否时完全二叉树


1.前置说明

如果没看树和二叉树的结构和概念建议先去看看

----数据结构之二叉树的超详细讲解(1)--(树和二叉树的概念和结构)-CSDN博客

---数据结构之二叉树的超详细讲解(2)--(堆的概念和结构的实现,堆排序和堆排序的应用)-CSDN博客

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树 操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

二叉树的结构表示:

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

手动创造一棵树:

BTNode* CreatBinaryTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);BTNode* node7 = BuyNode(7);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;node5->right = node7;return node1;
}

 将创造树节点的方法封装成一个函数(BuyNode):

BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");return NULL;}node->data = x;node->left = NULL;node->right = NULL;return node;
}

这样我们就完成了一棵树的创建,如下图所示:

注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。

再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:

1. 空树

2. 非空:根结点,根结点的左子树、根结点的右子树组成的。

从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。

2.二叉树的遍历

2.1 前序、中序以及后序遍历

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

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

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

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

3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

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

示例:

假如我们的二叉树为:

前序遍历:

前序遍历即访问根结点的操作发生在遍历其左右子树之前。也就是先访问根节点在访问左右子树,简称为根左右

所以前序遍历为: 1 2 3 NULL NULL NULL 4 5 NULL 6 NULL NULL

中序遍历:

中序遍历即访问左节点的操作发生在遍历其根节点和右节点之前,也就是先访问左节点在访问根右,简称左根右

所以中序遍历为:NULL 3 NULL 2 NULL 1 NULL 5 NULL 4  NULL 6 NULL

后序遍历:

后序遍历即先访问左节点,在访问右节点和根节点,简称左右根

所以后序遍历为:NULL NULL 3 NULL 2 NULL NULL 5 NULL NULL 6 4 1

代码实现: 

我们之前已经手动创建了一棵树:

2.1.1前序遍历:

void PrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}

 我们这里的逻辑为:首先判断节点是否为空,为空即返回NULL(这里N就代表为NULL),并return,注意:这里的return并不是直接退出,而是返回函数上一层,不为空即直接打印节点data值,然后递归左右子树,满足根左右的性质.

结果如下:

 

递归展开图:

这里怕宝子们还是不理解递归的原理,这里就展示前序的递归展开图,中序和后序是一样的

 由于篇幅有限,没有完全展示,但是原理都是一样的

 2.1.2中序遍历:

void InOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}

 我们这里的逻辑和前序方法差不多,也是使用递归,满足左根右的性质进行后序遍历

结果如下:

 

2.1.3后序遍历:

void postorder(BTNode* root)
{if (root == NULL){printf("N ");return;}postorder(root->left);postorder(root->right);printf("%d ", root->data);
}

结果如下:

 

力扣oj练习

--144. 二叉树的前序遍历 - 力扣(LeetCode)

--94. 二叉树的中序遍历 - 力扣(LeetCode)

--145. 二叉树的后序遍历 - 力扣(LeetCode)

只要能把上面的知识看懂弄明白,这三个题目能很轻松的做出来.

2.2 层序遍历

层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根结点所在 层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层 上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

 

 上图层序遍历的结果为:A B C D E F G H I

代码实现: 

void TreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%d ", front->data);if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);}QueueDestroy(&q);
}

具体步骤:

  1. 初始化队列

    初始化一个队列并将根节点入队。如果根节点为空,则直接跳过。

  2. 层序遍历

    使用队列进行层序遍历。每次取出队列中的节点,打印节点的值,并将该节点的左右子节点分别入队。

  3. 清理资源

    销毁队列,释放资源。

这里需要用到队列的知识,使其能一层一层的遍历二叉树,对队列还不清楚的宝子们,可以看看下面的博客--数据结构之队列的超详细讲解-CSDN博客 

练习:

1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为(A )
A ABDHECFG
B ABCDEFGH
C HDBEAFCG
D HDEBFGCA
2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK; 中序遍历:HFIEJKG.则二叉树根结点为(A)
A E
B F
C G
D H
3.设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为__D__。
A adbce
B decab
C debac
D abcde
4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列A
A FEDCBA
B CBAFED
C DEFCBA
D ABCDEF

3.结点个数以及高度等

3.1二叉树节点个数

这里大家很容易写出错误代码,比如:

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

这段代码看起来非常好理解,相当于进行了一次前序遍历,将遍历的节点进行返回.

在这个函数中,size 是一个局部变量,每次调用 TreeSize 时都会重新初始化为 0。让我们逐步分析一下这个函数的行为。

分析:

  1. root == NULL 时,函数直接返回 0。这是树为空的情况。

  2. 如果 root 不为 NULL

    • int size = 0; 初始化 size 为 0。
    • ++size; 将 size 增加 1,所以 size 现在为 1。
  3. 然后递归调用 TreeSize(root->left)TreeSize(root->right),但是这些调用的返回值并没有被使用或累加到 size 上。

  4. 因此,无论左右子树中有多少节点,这些递归调用的结果都不会影响当前函数中的 size 变量。每次递归调用都会在自己的栈帧中初始化一个新的 size 变量,并且最终返回 1 或 0。

函数递归展开图:

用static进行改进: 

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

static 变量在整个程序运行期间只会被初始化一次,这样就避免了局部变量一直的初始化 

结果如下:

 结果是没有问题的,可是当我们多次调用呢?

这里我重复调用三次:

结果如下:

 

正是因为 static 变量在整个程序运行期间只会被初始化一次,所以如果你多次用 TreeSize 函数,size 会一直累积之前的计算结果,而不会重新开始计算新的树的大小。

正确代码:

int TreeSize(BTNode* root)
{return root == NULL ? 0 :TreeSize(root->left) + TreeSize(root->right) + 1;
}

为了修正这些问题,可以将递归调用的结果累加到 size 中,并将最终的 size 值作为函数的返回值。 

函数递归展开图:

 

这段代码利用递归的方式计算二叉树的节点总数,通过递归遍历所有节点,实现了对整个二叉树的全面统计。递归的基准情况是遇到空节点返回 0,而在非空节点情况下,递归地计算左右子树的大小并进行累加,从而获得整棵树的节点总数。

3.2二叉树叶子节点个数

补充一下什么是叶子节点:

没有子树的节点叫做叶子节点,也称度为0的节点

代码展示:

int TreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;return TreeLeafSize(root->left)+ TreeLeafSize(root->right);
}

分析

  1. 基准情况

    • 如果 root 为 NULL,则树为空,没有叶节点。因此,返回 0。
  2. 叶节点情况

    • 如果 root 的左子节点和右子节点都为 NULL,那么当前节点为叶节点。因此,返回 1。
  3. 递归情况

    • 如果当前节点不是叶节点,则递归计算其左、右子树中的叶节点数量,并将它们加起来。
    • 通过递归调用 TreeLeafSize(root->left) 和 TreeLeafSize(root->right) 来分别计算左、右子树中的叶节点数量,并将结果相加。

递归展开图:

 

 结果如下:

结果为3,没有问题 

3.3二叉树第k层节点个数

代码展示:

int TreeLevelKSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;// 子问题return TreeLevelKSize(root->left, k - 1)+ TreeLevelKSize(root->right, k - 1);
}

 函数递归展开图:

分析

  1. 基础情况处理

    • if (root == NULL) return 0;:如果当前节点 root 为空,则返回 0。这是递归的终止条件之一,表示空节点没有子节点,因此在任何层级上都没有节点。

    • if (k == 1) return 1;:当 k 等于 1 时,表示已经递归到目标层级。根据题设,每个节点自身被视为一个层级,因此返回 1。

  2. 递归调用

    return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);如果当前节点 root 不为空且 k > 1,则递归调用 TreeLevelKSize 函数来计算左子树和右子树中第 k-1 层级的节点数目。最终返回的是左子树第 k-1 层级节点数目和右子树第 k-1 层级节点数目的和。

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

代码展示:

BTNode* TreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->data == x)return root;BTNode* ret1 = TreeFind(root->left, x);if (ret1)return ret1;BTNode* ret2 = TreeFind(root->right, x);if (ret2)return ret2;return NULL;
}

函数递归展开图:

 

分析

  1. 基础情况处理

    • if (root == NULL) return NULL;:如果当前节点 root 为空,则直接返回 NULL,表示未找到目标节点。

    • if (root->data == x) return root;:如果当前节点的值等于 x,则返回当前节点 root,表示已找到目标节点。

  2. 递归查找

    • BTNode* ret1 = TreeFind(root->left, x);

      • 递归在左子树中查找值为 x 的节点。如果找到了(即 ret1 不为 NULL),直接返回 ret1
    • BTNode* ret2 = TreeFind(root->right, x);

      • 如果左子树中未找到目标节点,继续递归在右子树中查找值为 x 的节点。如果找到了(即 ret2 不为 NULL),直接返回 ret2
  3. 返回结果

    • 如果在当前节点及其左右子树中都未找到值为 x 的节点,则返回 NULL,表示整棵树中没有该节点。

 

3.5二叉树的高度

代码一:

int TreeHeight(BTNode* root)
{if (root == NULL)return 0;return TreeHeight(root->left) > TreeHeight(root->right) ?TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
}

 

分析

  1. 基准情况

    • 如果 root 为 NULL,则树为空,高度为 0。因此,返回 0。
  2. 递归情况

    • 如果当前节点不为空,则递归计算其左、右子树的高度,并返回较大的那个值加上 1。
    • 通过递归调用 TreeHeight(root->left) 和 TreeHeight(root->right) 来分别计算左、右子树的高度。
    • 使用条件运算符 ? : 来比较左、右子树的高度,并将较大的那个值加上 1 返回。

这个解法是没有问题的,但是它的时间效率不高,它在递归过程中有的值会重复计算多次

函数递归展开图:

我们可以看到,我们每次计算出左右子树的高度后,并不会进行记录,导致我们比较完成后还需要进行递归计算,导致效率不高

代码二:

int TreeHeight(BTNode* root)
{if (root == NULL)return 0;int leftHeight = TreeHeight(root->left);int rightHeight = TreeHeight(root->right);return leftHeight > rightHeight ?leftHeight + 1 : rightHeight + 1;
}

 函数递归展开图:

可以发现我们用两个变量存储之后,每次我们都只需要一次,避免了重复计算,提高了时间效率 

这里大家可以试一试,在力扣上面右一道计a算二叉树的深度,如果你使用第一种代码,只通过了35个例子,超时了,因为后面的例子数据庞大,如果你使用第一种方案,最低层的值会被重复计算很多次,导致超出时间限制

而我们用第二种方法,能很顺利的通过

下面是原题链接:

--LCR 175. 计算二叉树的深度 - 力扣(LeetCode) 

4.二叉树的创建和销毁

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

代码展示:

BTNode* CreateTree(char* a, int* pi)
{if (a[*pi] == '#'){(*pi)++;return NULL;}BTNode* root = (BTNode*)malloc(sizeof(BTNode));root->data = a[(*pi)++];root->left = CreateTree(a, pi);root->right = CreateTree(a, pi);return root;
}

函数递归展开图:

 

函数分析:

  1. 参数

    • char* a:存储先序遍历序列的字符数组,包含节点的数据和 '#' 表示的空节点。
    • int* pi:指向当前处理的字符在数组 a 中的索引的指针。
  2. 返回值

    • BTNode*:返回值是指向根节点 BTNode 结构体的指针。
  3. 递归过程

    • 如果当前字符是 '#',表示遇到空节点,函数会返回 NULL,并且将指针 pi 向后移动一位。
    • 如果当前字符不是 '#',则创建一个新的节点,将当前字符作为节点的数据,并将指针 pi 向后移动一位。
    • 然后递归调用 CreateTree 函数来构建左子树和右子树,分别将它们赋给当前节点的 left 和 right 指针。
  4. 返回根节点

    • 最后返回根节点的指针。

 

4.2二叉树销毁

代码展示:

// 二叉树销毁
void TreeDestory(BTNode* root)
{if (root == NULL)return;TreeDestory(root->left);TreeDestory(root->right);free(root);
}

函数递归展开图:

 

函数分析:

  1. 检查根节点是否为空

    • 如果传入的根节点 root 为 NULL,说明树为空或者已经到达叶子节点的子节点(即空节点),直接返回,不进行任何操作。
  2. 递归销毁左子树

    • 调用 TreeDestory(root->left) 递归地销毁左子树。此调用会一直递归到最左子节点,然后逐级返回并销毁每个节点。
  3. 递归销毁右子树

    • 调用 TreeDestory(root->right) 递归地销毁右子树。此调用会一直递归到最右子节点,然后逐级返回并销毁每个节点。
  4. 释放当前节点的内存

    • 在左右子树都被销毁后,最后释放当前节点的内存 free(root)

 

4.3判断二叉树是否时完全二叉树

代码展示:

// 判断二叉树是否是完全二叉树
bool TreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root)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){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

 

具体步骤:

  1. 初始化队列

    初始化一个队列并将根节点入队。如果根节点为空,则直接跳过。

  2. 层序遍历

    使用队列进行层序遍历。每次取出队列中的节点,如果遇到空节点(NULL),则退出该循环。否则,将该节点的左右子节点分别入队。

  3. 检测后续节点

    继续检查队列中剩余的节点。如果存在非空节点,则树不是完全二叉树,返回 false。否则,继续检查直到队列为空。

  4. 清理与返回

    销毁队列,释放资源,并返回 true 表示该二叉树是完全二叉树。

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

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

相关文章

展讯-QMI8658和气压传感器驱动调试

1.调试QMI8658 参考demo,添加QMI8610相关内容 当前驱动路径位于:bsp/modules/input/misc/qmi8610/qmi8610.c 编译使用make sockoimage 用fastboot烧录 1.确定驱动被正常加载 代码添加之后,首先确定有没有进入当前驱动文件 dmesg |grep …

如何用GO语言实现冒泡排序算法?

本章教程,介绍一下如何用GO语言实现基础排序算法中的冒泡排序。 一、程序代码 package mainimport ("fmt""math/rand""time" )// bubbleSort 函数实现冒泡排序算法 func bubbleSort(arr []int) {n

32 - 判断三角形(高频 SQL 50 题基础版)

32 - 判断三角形 select *,if(xy>z and xz>y and zy > x,Yes,No) triangle fromTriangle;

Day9 —— 大数据技术之ZooKeeper

ZooKeeper快速入门系列 ZooKeeper的概述什么是ZooKeeper?ZooKeeper的特点和功能使用ZooKeeper的原因 ZooKeeper数据模型ZooKeeper安装ZooKeeper配置ZooKeeper命令行操作常见服务端命令 ZooKeeper的概述 什么是ZooKeeper? ZooKeeper是一个开源的分布式协…

jarvisoj_level2

首先检查checksec 可以看见no canary found 说明可能是栈溢出 运行一下程序 随便输一个,得到hello world(感觉这一步好像没啥用,没有RE那用) IDA 32 打开 后门 漏洞点 buf: 代码思路:利用溢出,把buf填满,然后构造程序,得到获取shell的目的 exp来自 BUUCTF—jarvisoj_level…

【Qt】学习Day1

文章目录 Qt简介创建第一个Qt程序创建过程介绍main函数工程文件头文件控件源文件快捷键按钮控件常用API对象树坐标系 信号和槽自定义信号自定义槽函数触发自定义的信号案例-下课后,老师触发饿了信号,学生响应信号,请客吃饭重载信号连接信号La…

Paxos分布式共识算法

Paxos分布式共识算法 一、简介 Paxos算法是由莱斯利兰伯特(Leslie Lamport)于1990年提出的一种基于消息传递且具有高度容错特性的一致性算法。它主要用于解决分布式系统中如何就某个值达成一致,并保证整个系统的一致性,即使在部分节点发生故障的情况下…

论文浅尝 | 基于生成模型的多模态实体链接

笔记整理:郭凌冰,浙江大学博士,研究方向为知识图谱 链接:https://arxiv.org/abs/2306.12725 1. 动机 多模态实体链接(Multimodal Entity Linking,MEL)旨在将带有多模态上下文的提及映射到知识库…

[SAP ABAP] 运算符

1.算数运算符 算术运算符描述加法-减法*乘法/除法MOD取余 示例1 输出结果: 输出结果: 2.比较运算符 比较运算符描述示例 等于 A B A EQ B <> 不等于 A <> B A NE B >大于 A > B A GT B <小于 A < B A LT B >大于或等于 A > B A GE B <小…

keep-alive实现页面缓存

<!--router-view 实现页面缓存--> <router-view v-slot"{Component}"><keep-alive :max"8"><component :is"Component"></component></keep-alive> </router-view>

2024年6月23日 十二生肖 今日运势

小运播报&#xff1a;2024年6月23日&#xff0c;星期日&#xff0c;农历五月十八 &#xff08;甲辰年庚午月戊午日&#xff09;&#xff0c;法定节假日。今天国际奥林匹克日&#xff0c;坚不可摧的意志&#xff0c;披荆斩棘的豪情&#xff0c;永远值得拥有&#xff01; 红榜生…

算法金 | 统计学的回归和机器学习中的回归有什么差别?

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 统计学中的回归 目标&#xff1a; 主要用于解释和推断自变量&#xff08;independent variables&#xff09;和因变量&#xff08;de…

array_key_exists() expects parameter 2 to be array, null given

公众号获取微信服务器IP地址 错误代码如下 public function getwxIP(){//获取微信服务器IP地址$accessToken $this->getwxoaiAccessToken();$userToken new UserToken();$result $userToken->curl_get("https: //api.weixin.qq.com/cgi-bin/get_api_domain_ip…

Day58 代码随想录打卡|二叉树篇---将有序数组转换为二叉搜索树

题目&#xff08;leecode T108&#xff09;&#xff1a; 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 方法&#xff1a;用有序数组构造平衡二叉搜索树&#xff0c;和我们之前有一题的思路差不多&#xff0c…

遗传算法求解时间窗车辆路径规划问题(附python代码)

摘要 本研究提出了一种基于遗传算法的车辆路径规划&#xff08;VRP&#xff09;问题求解框架&#xff0c;它能够有效地处理一系列复杂约束&#xff0c;包括软时间窗、硬时间窗、行驶距离限制、车辆最大载重量、多个配送中心的协调、特定的配送顺序&#xff0c;以及多种车型的选…

Python学习打卡:day13

day13 笔记来源于&#xff1a;黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了 目录 day1397、初识对象98、类的成员方法类的定义和使用成员变量和成员方法成员方法的定义语法 99、类和对象在程序中通过类来描述基于类创建对象 100、…

Javaweb登录校验

登录校验 JWT令牌的相关操作需要添加相关依赖 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version> </dependency>一、摘要 场景&#xff1a;当我们想要访问一个网站时&am…

cherry 笔记三(启动)

cherry启动很简单 app创建完 直接startup()就好了 func main() {app : cherry.Configure("./examples/config/profile-chat.json","chat-master",false,cherry.Cluster,)app.SetSerializer(cserializer.NewJSON())app.Startup() } Configure()--->N…

c++使用std::function/std::bind

1&#xff09;C入门级小知识&#xff0c;分享给将要学习或者正在学习C开发的同学。 2&#xff09;内容属于原创&#xff0c;若转载&#xff0c;请说明出处。 3&#xff09;提供相关问题有偿答疑和支持。 std::function对象是对C中现有的可调用实体的一种类型安全的包裹&…

多路h265监控录放开发-(9)通过拖拽到窗口完成渲染

xcamera_widget.h class XCameraWidget :public QWidget {Q_OBJECTpublic:XCameraWidget(QWidget* p nullptr);//渲染视频void Draw();//123//清理资源,再一个窗口被覆盖后 清理之前窗口生成的资源1~XCameraWidget();//123 private:XDecodeTask* decode_ nullptr;//123XDemu…