数据结构之树与二叉树——算法与数据结构入门笔记(五)

CSDNlogoPost

本文是算法与数据结构的学习笔记第五篇,将持续更新,欢迎小伙伴们阅读学习。有不懂的或错误的地方,欢迎交流

引言

前面章节介绍的都是线性存储的数据结构,包括数组、链表、栈、队列。本节带大家学习一种非线性存储的数据结构,即树(tree)。

不管是在面试时,还是日常开发过程中,树都是一种曝光率极高的数据结构。可以说树是数据结构最为承上启下的部分,其可以转化为线性表(通过二叉树的线索化),也是学习图的基础。本文将介绍树的基本概念、常见类型和应用、二叉树以及 C 语言实现,帮助大家深入理解树的本质和用途。

树的基本概念

树的定义

树是 n(n>=0) 个结点的有限集。当 n=0 时,称为空树。在任意一棵非空树中应满足:

  1. 有且仅有一个特定的称为根的结点。
  2. 当 n>1 时,其余节点可分为 m(m>0) 个互不相交的有限集 T1,T2,…,Tm,其中每个集合 Ti(1<=i<=m) 本身又是一棵树,并且称为根的子树。

显然,树的定义是递归的,即在树的定义中又用到了自身,树是一种递归的数据结构。树同时也是一种分层结构,具有以下两个特点:

  1. 树的根结点没有前驱,除根结点外的所有结点有且只有一个前驱。
  2. 树中所有结点可以有零个或多个后继。

因此 n 个结点的树中有 n-1 条边。

基本术语

下面结合图示来说明一下树的一些基本术语和概念。

在这里插入图片描述

结点:和链表类似,树存储结构中也将存储的各个元素称为结点。例如在上图中,元素 A 就是一个结点。对于树中某些特殊位置的结点,还可以进行更细致的划分,比如:
父结点双亲结点)、孩子结点兄弟结点:以上图中的结点 A、B、C、D 为例,A 是 B、C、D 结点的父结点(也称为双亲结点),而 B、C、D 都是 A 结点的孩子结点(也称“子结点”)。对于 B、C、D 来说,它们都有相同的父结点,所以它们互为兄弟结点;
树根结点(简称根结点):特指树中没有双亲(父亲)的结点,一棵树有且仅有一个根结点。上图中,结点 A 就是整棵树的根结点;
祖先子孙:根 A 到结点 K 的唯一路径上的任意结点,称为结点 K 的祖先。如结点 B 是结点 K 的祖先,而结点 K 是结点 B 的子孙。

子树:仍以上图的树为例,A 是整棵树的根结点。但如果单看结点 B、E、F、K、L 组成的部分来说,它们也组成了一棵树,结点 B 是这棵树的根结点。通常,我们将一棵树中几个结点构成的“小树”称为这棵树的“子树”。
知道了子树的概念后,树也可以这样定义:树是由根结点和若干棵子树构成的。例如,上图这棵树就是由根结点 A 和分别以 B、C、D 为根节点的子树构成。
注意:单个结点也可以看作是一棵树,该结点即为根结点。例如上图中,结点 K、L、F 各自就可以看作是一棵树,只不过树中只有一个根节点而已。

结点的度:一个结点拥有子树的个数,就称为该结点的度(Degree)。例如上图中,根结点 A 有3个子树,它们的根节点分别是 B、C、D,因此结点 A 的度为3。
树的度:一棵树中,最大的节点的度称为树的度。如上图:所有结点中最大的度为3,所以整棵树的度就是3。
叶节点终端节点:度为0的节点称为叶节点; 如上图:I、J、F、K、L、M 等节点为叶节点。
分支节点非终端节点:度不为0的节点; 如上图:A、B、C、D 等节点为分支节点。

结点的层次:结点的层次从一棵树的树根开始定义,树根所在层为第一层,根的孩子结点所在的层为第二层,依次类推。对于上图这棵树来说,A 结点在第一层,B、C、D 为第二层,E、F、G、H、I、J 在第三层,K、L、M 在第四层。
树中结点层次的最大值,称为这棵树的深度或者高度。例如上图这棵树的深度为 4。
如果两个结点的父结点不同,但它们父结点的层次相同,那么这两个结点互为堂兄弟。例如上图中,结点 G 和 E、F、H、I、J 的父结点都在第二层,所以它们互为堂兄弟。

有序树无序树:树中结点的各子树从左到右是有次序的,不能互换,称该树为有序树,否则称为无序树。假设图为有序树,若将子结点位置互换,则变成一棵不同的树。
在有序树中,结点最左边的子树称为 “第一个孩子”,最右边的称为 “最后一个孩子”。拿上图这棵树来说,如果它是一棵有序树,那么以结点 B 为根结点的子树为整棵树的第一个孩子,以结点 D 为根结点的子树为整棵树的最后一个孩子。

路径路径长度:树中两个结点之间的路径是由这两个结点之间所经过的结点序列构成的,而路径长度是路径上所经过的边的个数。
注意:由于树中的分支是有向的,即从双亲指向孩子,所以树中的路径是从上向下的,同一层的两个结点之间不存在路径。

森林:森林是 m(m≥0) 棵互不相交的树的集合。森林的概念与树的概念十分相近,因为只要把树的根结点删去就成了森林。反之,只要给 m 棵独立的树加上一个结点,并把这 m 棵树作为该结点的子树,则森林就变成了树。

树的性质

树具有如下最基本的性质:

  1. 树中的结点数等于所有结点的度数加1;
  2. 度为 m m m 的树中第 i i i 层上至多有 m i − 1 m^{i-1} mi1 个结点( i ≥ 1 i\ge1 i1);
  3. 高度为 h h h m m m 叉树至多有 ( m h − 1 ) / ( m − 1 ) (m^h-1)/(m-1) (mh1)/(m1) 个结点;
  4. 具有 n n n 个结点的 m m m 叉树的最小高度为 l o g m ( n ( m − 1 ) + 1 ) log_m(n(m-1)+1) logm(n(m1)+1)

树的其它表示方法

上图中使用的是树状表示法,最基本的逻辑结构表示法,使用一棵树倒置表示,非常直观。除了这种方法之外,还有其它的方式可以表示一棵树:

请添加图片描述
上图左侧是文氏图标表示法:是使用集合以及集合包含关系描述树结构(集合之间绝不能相交,即任意两个圆圈不能有交集)。

上图右侧使用的是凹入表示法,最长条为根结点,相同长度的表示在同一层次。例如 B、C、D 长度相同,都为 A 的子结点,E 和 F 长度相同,为 B 的子结点,K 和 L 长度相同,为 E 的子结点,依此类推。

还可以使用括号表示法:将树的根结点写在括号的左边,除根结点之外的其余结点写在括号中,并用逗号分隔。例如上面的树用括号表示为 ( A , ( B ( E ( K , L ) , F ) , C ( G ) , D ( H ( M ) , I , J ) ) ) (A , ( B ( E ( K , L ) , F ) , C ( G ) , D ( H ( M ) , I , J ) ) ) (A,(B(E(K,L),F),C(G),D(H(M),I,J)))

树的存储结构

说到存储结构,自然就会想到我们前面讲过的顺序存储和链式存储两种结构。

顺序存储结构:树中某个结点的孩子可以有多个,若将树中所有结点存储到数组中,结点的存储位置无法直接反应其逻辑关系,因此简单的顺序存储结构是不能满足树的实现要求的。

链式存储结构:链式存储结构的特点,完全可以实现对树的存储结构的表示。

表示方式:实际中树有很多种表示方式, 如:双亲表示法孩子表示法孩子兄弟表示法等等。我们这里就简单的了解其中最常用的孩子兄弟表示法。

对于树这样的层级结构,我们观察后发现,任意—棵树,它的结点的第一个孩子如果存在就是唯一的,它的右只弟如果存在也是唯一的。因此,我们设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟

结点结构如下表所示:

datafirstchildrightsib

其中 data 是数据域;firstchild 为指针域,存储该结点的第—个孩子结点的存储地址;rightsib是指针域,存储该结点的右兄弟结点的存储地址。

结构定义代码如下:

/* 树的孩子兄弟表示法结构定义 */
typedef struct CSNode
{
	TElemType data;				 // 结点中的数据域
    struct CSNode *firstchild1,*rightsib;    // 指向其第一个孩子结点和下一个兄弟结点       
}CSNode,*CSTree;

对于下图左边的树来说,这种方法实现的示意图如右下图所示:

在这里插入图片描述
其实这个表示法的最大好处是它把一棵复杂的树变成了一棵二叉树,这样就可以充分利用二叉树的特性和算法来处理这棵树了。嗯?有人问,二叉树是什么?哈哈,别急,这正是接下来要重点讲的内容。

常见类型的树

二叉树

二叉树是一种最基本的树型数据结构。简单地理解,度不超过2的有序树就是二叉树。

例如,下图左侧就是一棵二叉树,而右侧则不是。

请添加图片描述
二叉树还可以继续分类,衍生出完全二叉树、满二叉树等:

满二叉树

如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树,如下图所示:

请添加图片描述

完全二叉树

如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。满二叉树是完全二叉树的一种特殊情况。

请添加图片描述

堆也是完全二叉树的一种特殊情况,针对的是节点数据的大小对比。堆中每个节点的值都必须大于等于(或者小于等于)其子树中的每个节点的值。大于等于每个子树节点的值称为大顶堆,反正称为小顶堆,如下图所示。后面将有文章专门分析堆。
请添加图片描述

二叉搜索树

二叉搜索树是一种特殊的二叉树,又称为二叉查找树、二叉排序树等等,它实际上是数据域有序的二叉树,即对树上的每个结点,都满足其左子树上所有结点的数据域均小于根结点的数据域,右子树上所有结点的数据域均大于根结点的数据域。

在这里插入图片描述
二叉排序树意味着二叉树中的数据是排好序的,顺序为左结点<根节点<右结点,这表明二叉排序树的中序遍历结果是有序的(二叉树四种遍历方式[前序遍历、中序遍历、后序遍历、层序遍历]将在下面给出)。

二叉搜索树的特点:

  1. 每个节点包含一个值,每个节点至多有两个子树。
  2. 每个节点左子树节点的值都小于自身的值,每个节点右子树节点的值都大于自身的值。
  3. 二叉搜索树的查询时间复杂度是 O ( log ⁡ N ) O(\log N) O(logN),但是随着不断的插入、删除节点,二叉树的树高可能会不断变大,当一个二叉搜索树所有节点都只有左子树或者都只有右子树时,其查找性能就退化成线性的了。

平衡二叉树

平衡二叉树又被称为 AVL 树,它在符合二叉搜索树的条件下,还满足“高度平衡”,即任何节点的两个子树的高度最大差为1。

在这里插入图片描述
平衡二叉树的产生是为了解决二叉搜索树在插入时发生线性排列的现象。平衡二叉树在插入或删除数据时,采用旋转的调整方式,使得二叉树在插入数据后保持平衡。平衡二叉树的查询时间复杂度最好情况和最坏情况都维持在 O ( log ⁡ N ) O(\log N) O(logN)。但是频繁旋转会使插入和删除牺牲掉 O ( log ⁡ N ) O(\log N) O(logN) 左右的时间,不过相对二叉搜索树来说,时间上稳定了很多。

红黑树

平衡二叉树(AVL)为了追求高度平衡,需要通过平衡处理使得左右子树的高度差必须小于等于1。高度平衡带来的好处是能够提供更高的搜索效率,其最坏的查找时间复杂度都是 O ( log ⁡ N ) O(\log N) O(logN)。但是由于需要维持这份高度平衡,所付出的代价就是当对树种结点进行插入和删除时,需要经过多次旋转实现复衡。这导致 AVL 的插入和删除效率并不高。

为了解决这样的问题,红黑树被提出了。红黑树通过将结点进行红黑着色,使得原本高度平衡的树结构被稍微打乱,平衡程度降低。红黑树不追求完全平衡,只要求达到部分平衡。这是一种折中的方案,大大提高了结点删除和插入的效率,更加实用。

在这里插入图片描述
红黑树VS平衡二叉树

在这里插入图片描述
除了上面所提及的树结构,还有许多广泛应用在数据库、磁盘存储等场景下的树结构。比如B树B+树等。这里就先不介绍了诶。

树的应用

  1. 文件系统:文件系统通常使用树的结构来组织文件和目录。树形结构的特性使得文件系统可以方便地进行文件的查找、插入和删除操作。
  2. 数据库索引:数据库索引是一种数据结构,用于加快数据库中数据的访问速度。常见的数据库索引结构,如B树和B+树,利用树的特性实现高效的数据检索。
  3. 表达式解析:在编译器和解释器中,树结构常用于表示和解析数学表达式。表达式树(Expression Tree)可以将复杂的数学表达式转化为树的形式,便于计算和优化。
  4. 图形算法:许多图形算法,如最短路径算法和最小生成树算法,都可以使用树的结构进行表示和求解。树的特性可以帮助解决图形问题中的路径搜索和优化。

二叉树

二叉树的性质

  1. 在二叉树的第 i i i 层上至多有 2 i − 1 2^{i-1} 2i1 个结点 ( i ≥ 1 ) (i\ge1) (i1)

  2. 深度为 k k k 的二叉树至多有 2 k − 1 2^k-1 2k1 个结点 ( k ≥ 1 ) (k\ge1) (k1)

  3. 对任何一棵二叉树 T,如果度为0,其叶结点个数为 n 0 n_0 n0,度为2的分支结点个数为 n 2 n_2 n2,则有 n 0 = n 2 + 1 n_0=n_2 + 1 n0n2+1

  4. 具有 n n n 个结点的完全二叉树的深度为 ⌊ log ⁡ 2 n ⌋ + 1 \left\lfloor {{\log _2}n} \right\rfloor + 1 log2n+1 ⌊ x ⌋ \left\lfloor x \right\rfloor x表示不大于 x x x 的最大整数)。

  5. 如果对一棵有 n n n 个结点的完全二叉树(其深度为 ⌊ log ⁡ 2 n ⌋ + 1 \left\lfloor {{\log _2}n} \right\rfloor + 1 log2n+1)的结点按层序编号(从第1层到第 ⌊ log ⁡ 2 n ⌋ + 1 \left\lfloor {{\log _2}n} \right\rfloor + 1 log2n+1 层,每层从左到右),则对于任意结点 i i i 1 ≤ i ≤ n 1\le i \le n 1in) 有:
    (1). 如果 i = 1 i=1 i=1,则结点 i i i 是二叉树的根,无双亲;如果 i > 1 i>1 i>1,则其双亲是结点 ⌊ i / 2 ⌋ \left\lfloor i/2 \right\rfloor i/2
    (2). 如果 2 i > n 2i>n 2i>n,则结点 i i i 无左孩子(结点 i i i 为叶子结点);否则其左孩子是结点 2 i 2i 2i
    (3). 如果 2 i < n 2i<n 2i<n,则结点 i i i无右孩子;否则其右孩子是结点 2 i + 1 2i+1 2i+1

二叉树的存储结构

二叉树的顺序存储结构
前面我们已经谈到了树的存储结构,并且谈到顺序存储对树这种—对多的关系结构实现起来是比较困难的。但是二叉树是—种特殊的树,由于它的特殊性,使得用顺序存储结构也可以实现。
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树。因为不是完全二叉树会有空间的浪费。

二叉链表
既然顺序存储适用性不强,我们就要考虑链式存储结构。二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表叫做二叉链表。结点结构图如下表所示:

lchilddatarchild

其中 data 是数据域;lchild 和 rchild 都是指针域,分别存放指向左孩子和右孩子的指针。

以下是我们的二叉链表的结点结构定义代码:

/* 二叉树的二叉链表结点结构定义*/
typedef struct BiTNode			// 结点结构
{
	TElemType data;				 // 结点中的数据域
    struct BiTNode *lchild,*rchild;    // 左右孩子指针       
}BiTNode,*BiTree;

结构示意图如下图所示:

在这里插入图片描述

二叉树的遍历

二叉树的遍历(traversing binary tree)是二叉树的一种重要操作。二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次且仅被访问一次。

这里有两个关键词:访问和次序。访问其实是要根据实际的需要来确定具体做什么,比如对每个结点进行相关计算,输出打印等,它算作是—个抽象操作。在这里我们可以简单地假定访问就是输出结点的数据信息。

二叉树的遍历次序不同干线性结构,最多也就是从头至尾、循环、双向等简单的遍历方式。树的结点之间不存在唯一的前驱和后继关系,在访问一个结点后,下一个被访问的结点面临着不同的选择。

先序遍历

在这里插入图片描述
算法讲解

  • 若二叉树为空,则空操作返回,否则遍历顺序:根结点->左子树->右子树。
  • 动态图解:和上面的动态图一样,先序遍历就像一个小人从根结点开始,围绕二叉树的外圈开始跑(遇到缝隙就钻进去),按照跑的顺序,依次输出序列。

代码演示

void PreOrderTraversal(BiTree BT)
{
    if( BT != NULL ) 
    {
        printf(“%d\n”, BT->Data);        //对节点的数据进行打印          
        PreOrderTraversal(BT->Left);     //访问左子树
        PreOrderTraversal(BT->Right);    //访问右子树
    }
}

中序遍历

在这里插入图片描述
算法讲解

  • 若二叉树为空,则空操作返回,否则遍历顺序:左子树->根结点->右子树。
  • 动态图解:中序遍历就像投影仪一样,将二叉树从最左侧到最右侧依次投影到同一水平线上面,得到的从左到右的相关序列就是二叉树的中序遍历。

代码演示

void InOrderTraversal(BiTree BT)
{
    if(BT != NULL)
    {
        InOrderTraversal(BT->Left);
        printf("%d\n", BT->Data);
        InOrderTraversal(BT->Right);
    }
}

后序遍历

在这里插入图片描述
算法讲解

  • 若二叉树为空,则空操作返回,否则遍历顺序:左子树->右子树->根结点。
  • 动态图解:后序遍历就像剪葡萄,只能一个个剪,不能让超过1个的葡萄一起掉下来,那就错了。例如上图中的 B,剪去 B 后面的 D、E、H、I、J 都会掉下来(达咩),而 H 剪去只会掉下 H,规律就是这个规律。

代码演示

void PostOrderTraversal(BiTree BT)
{
    if (BT != NULL)
    {
        PostOrderTraversal(BT->Left);
        PostOrderTraversal(BT->Right);
        printf("%d\n", BT->Data);
    }
}

层次遍历

层次遍历就是从根节点开始,一层一层,从上到下,每层从左到右,依次取值。

代码演示

void LevelOrder(BiTree T){
	InitQueue(Q);				//初始化辅助队列
	BiTree p;
	EnQueue(Q,T);				//将根结点入队
	while(!IsEmpty(Q))
	{							//队列不空则循环
		DeQueue(Q,p);			//队头结点出队
		visit(p);				//访问出队结点
		if(p->1child!=NULL)
			EnQueue(Q,p->lchild);//左子树不空,则左子树根结点入队
		if(p->rchild!=NULL)
			EnQueue(Q,p->rchild);//右子树不空,则右子树根结点入队
	}
}

C语言

以下是使用C语言实现二叉树(包括创建树、插入结点、删除结点、遍历树、计算树的深度/高度和大小等基础操作)的示例代码:

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

// 定义二叉树节点
typedef struct Node {
    int data;
    struct Node* left;
    struct Node* right;
} Node;

// 创建新节点
Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) {
        printf("Memory allocation failed\n");
        return NULL;
    }
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// 插入节点(递归实现)
Node* insertNode(Node* root, int data) {
    if (root == NULL) {
        return createNode(data);
    } else {
        if (data <= root->data) {
            root->left = insertNode(root->left, data);
        } else {
            root->right = insertNode(root->right, data);
        }
        return root;
    }
}

// 删除节点
Node* deleteNode(Node* root, int data) {
    if (root == NULL) {
        return root;
    } else if (data < root->data) {
        root->left = deleteNode(root->left, data);
    } else if (data > root->data) {
        root->right = deleteNode(root->right, data);
    } else {
        if (root->left == NULL && root->right == NULL) {
            free(root);
            root = NULL;
        } else if (root->left == NULL) {
            Node* temp = root;
            root = root->right;
            free(temp);
        } else if (root->right == NULL) {
            Node* temp = root;
            root = root->left;
            free(temp);
        } else {
            Node* minRight = findMin(root->right);
            root->data = minRight->data;
            root->right = deleteNode(root->right, minRight->data);
        }
    }
    return root;
}

// 查找最小节点
Node* findMin(Node* root) {
    while (root->left != NULL) {
        root = root->left;
    }
    return root;
}

// 计算树的深度/高度
int calculateHeight(Node* root) {
    if (root == NULL) {
        return 0;
    } else {
        int leftHeight = calculateHeight(root->left);
        int rightHeight = calculateHeight(root->right);
        return (leftHeight > rightHeight) ? (leftHeight + 1) : (rightHeight + 1);
    }
}

// 计算树的大小(节点数)
int calculateSize(Node* root) {
    if (root == NULL) {
        return 0;
    } else {
        int leftSize = calculateSize(root->left);
        int rightSize = calculateSize(root->right);
        return leftSize + rightSize + 1;
    }
}

// 先序遍历(根-左-右)
void preorderTraversal(Node* root) {
    if (root != NULL) {
        printf("%d ", root->data);
        preorderTraversal(root->left);
        preorderTraversal(root->right);
    }
}

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

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

int main() {
    Node* root = NULL;

    // 插入节点
    root = insertNode(root, 4);
    root = insertNode(root, 2);
    root = insertNode(root, 6);
    root = insertNode(root, 1);
    root = insertNode(root, 3);
    root = insertNode(root, 5);
    root = insertNode(root, 7);

    // 先序遍历
    printf("Preorder traversal: ");
    preorderTraversal(root);
    printf("\n");

    // 中序遍历
    printf("Inorder traversal: ");
    inorderTraversal(root);
    printf("\n");

    // 后序遍历
    printf("Postorder traversal: ");
    postorderTraversal(root);
    printf("\n");

    // 删除节点
    root = deleteNode(root, 3);

    // 先序遍历(删除节点后)
    printf("Preorder traversal (after deletion): ");
    preorderTraversal(root);
    printf("\n");

    // 计算树的深度/高度
    int height = calculateHeight(root);
    printf("Tree height: %d\n", height);

    // 计算树的大小(节点数)
    int size = calculateSize(root);
    printf("Tree size: %d\n", size);

    return 0;
}

结论

树作为一种重要的数据结构,在计算机科学中有广泛的应用。通过理解树的基本概念、常见类型和应用场景,我们可以更好地理解和运用树结构解决实际问题。通过进一步的学习和实践,我们可以不断深入和拓展树的应用领域,提升算法和数据结构的能力。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

chatgpt赋能python:Python分词:从原理到实践

Python分词&#xff1a;从原理到实践 分词是自然语言处理中的关键步骤之一&#xff0c;它是指将一句话或一段文本分成若干个词语&#xff08;token&#xff09;并进行标注。Python作为一种非常流行的编程语言&#xff0c;具备强大的文本处理能力&#xff0c;而分词也是它的强项…

chatgpt赋能python:Python如何切换中文

Python 如何切换中文 Python 是一种广泛使用的编程语言&#xff0c;被用于多种目的&#xff0c;包括数据分析、机器学习、Web 应用程序等。在使用 Python 进行开发时&#xff0c;需要处理不同的语言&#xff0c;其中中文也是包括在内的。对于需要切换中文的情况&#xff0c;本…

学生考试作弊检测系统 yolov8

学生考试作弊检测系统采用yolov8网络模型人工智能技术&#xff0c;学生考试作弊检测系统过在考场中安装监控设备&#xff0c;对学生的作弊行为进行实时监测。当学生出现作弊行为时&#xff0c;学生考试作弊检测系统将自动识别并记录信息。YOLOv8 算法的核心特性和改动可以归结为…

关于数据生成二维码保存和解密删除二维码

文章目录 前言一、pom配置依赖二、文件引入1.BufferedImageLuminanceSource2.QRCodeUtil3.MyPicConfig4.UploadUtils三、测试前言 所需文件: MyPicConfig 主要解决上传图片实时刷新BufferedImageLuminanceSource 算法文件QRCodeUtil 生成二维码工具类UploadUtils 主要解决上传…

软考A计划-系统架构师-官方考试指定教程-(13/15)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

【Java基础学习打卡03】计算机中数据的表示、存储与处理

目录 前言一、数据的表示1.数据与信息2.计算机中的数据3.计算机中数据的单位 二、数据的存储三、数据的处理1.进位计数值2.进制间转换 四、字符编码总结 前言 本小节主要介绍在计算机中数据的表示、存储与处理。要知道计算机内部使用二进制数据&#xff0c;也就是0和1组成的数…

2.3 YARN伪分布式集群搭建

任务目的 重点掌握 YARN 集群的相关配置学会启动和关闭 YARN 集群的两种方式能够使用 jps 命令查看进程的启动情况能够通过 UI 查看 YARN 集群的运行状态任务清单 任务1:YARN 集群主要配置文件讲解任务2:YARN 集群测试任务步骤 任务1:YARN 集群主要配置文件讲解 1.1 配置环…

【新版】系统架构设计师 - 计算机系统基础知识

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 计算机系统基础知识考点摘要计算机系统计算机硬件组成浮点数Flynn分类法CISC与RISC流水线技术超标量流水线存储系统层次化存储结构CacheCache的命中率Cache的页面淘汰主存编址磁盘管理&#xff08…

Linux 信号

文章目录 1. 信号1.1 前言1.2 信号的位置1.3 接口1.3.1 sigset_t1.3.2 信号集操作接口1.3.3 signal1.3.4 sigprocmask1.3.5 sigpending 2. 信号的处理2.1 内核态和用户态2.2 信号的监测和处理 1. 信号 1.1 前言 在 Linux 中&#xff0c;信号是一种用于进程之间的通信机制&…

地震勘探基础(十一)之水平叠加处理

水平叠加处理 地震资料经过预处理&#xff0c;静校正&#xff0c;反褶积&#xff0c;速度分析和动校正处理后就要进行水平叠加处理。地震水平叠加处理是地震常规处理的重要环节。 假设一个共中心点道集有三个地震道&#xff0c;经过速度分析和动校正以后&#xff0c;水平叠加…

【数据结构】何为数据结构。

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2022博客之星T…

Tik Tok的海外娱乐公会(中亚、巴西、美国、台湾)怎么申请?

TIKTOK 公会海外市场潜力巨大 自 2016 年始&#xff0c;多家直播平台陆续拓展至东南亚、中东、俄罗斯、日韩、 欧美、拉美等地区 海外市场作为直播发展新蓝海&#xff0c;2021 年直播行业整体规模达百亿美元&#xff0c; 并维持高速增长 TikTok 直播市场空间 TikTok 已经成…

【 Python 全栈开发 - WEB开发篇 - 31 】where条件查询

文章目录 一、where条件查询1.关系运算符查询2.IN关键字查询3.BETWEEN AND关键字查询4.空值查询5.AND关键字查询6.OR关键字查询7.LIKE关键字查询普通字符串含有%通配的字符串含有_通配的字符串 一、where条件查询 MySQL 的 where 条件查询是指在查询数据时&#xff0c;通过 wh…

网络安全系统教程+渗透测试+学习路线(自学笔记)

一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

Qt6 C++基础入门3 对话框与MainWindow

目录 对话框MainWindow菜单工具栏 对话框 目前的对话框主要有以下几大类 文件对话框( QFile Dialog)消息对话框( QMessageBox)输入对话框( QInputDialog)颜色对话框( QColorDialog)字体对话框( QFontDialog) 这是七大对话框及其基本用法的实例参考&#xff0c;所有代码都写在…

四、LLC 谐振变换器

变换器实现 ZVS 的限制 全面了解LLC谐振变换器实现ZVS的条件&#xff0c;把变换器主电路变形成图所示形式。其中 Coss1、Coss2分别为开关管 S1、S2 漏-源极间的寄生电容&#xff0c;并且Coss1Coss2 Coss 。Cstray为与谐振网络并联的等效寄生电容&#xff0c;则变换器在 ZVS 条…

万维网服务器

一、域名解析gethostbyname函数 struct hostent {char *h_name; /* 官方域名 */char **h_aliases; /* 别名*/int h_addrtype; /* 地址族&#xff08;地址类型&#xff09; */int h_length; /* 地址长度 */char **h_addr_list; …

QT+OpenGL高级光照 Blinn-Phong和Gamma校正

QTOpenGL高级光照1 本篇完整工程见gitee:QtOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主 Blinn-Phong 冯氏光照&#xff1a;视线与反射方向之间的夹角不小于90度&#xff0c;镜面光分量会变成0.0&#xff08;不是很合理&am…

宝塔面板搭建thinkphp后请求中去除index.php后缀

宝塔面板搭建thinkphp后请求中去除index.php后缀 nginx配置 在宝塔面板网站中绑定thinkphp的public&#xff0c;添加站点 点击站点设置按钮打开项目设置页面 找到配置文件 选项&#xff0c;然后在 22行 后添加一下代码 location / {if (!-f $request_filename) {rewrite …

机器学习算法分类(三)

在机器学习中&#xff0c;又分为监督学习、无监督学习、半监督学习、强化学习和深度学习。 监督、无监督、半监督学习 机器学习根据数据集是否有标签&#xff0c;又分为监督学习、无监督学习、半监督学习。 监督学习&#xff1a;训练数据集全部都有标签无监督学习&#xff1a…
最新文章