C语言:二叉树的构建

目录

一、二叉树的存储

1.1        顺序存储

1.2        链式存储

二、二叉树的顺序结构及实现

2.1堆的概念及结构

2.2堆的构建

2.3堆的插入

2.4堆顶的删除

2.5堆的完整代码

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

3.1链式二叉树的构建

3.2链式二叉树的遍历

3.2.1前序遍历

3.2.2中序遍历

3.2.3后序遍历

3.2.4层序遍历


一、二叉树的存储

二叉树一般用顺序结构存储或链式结构存储。

1.1        顺序存储

顺序存储就是用数组来存储,但一般使用数组只适合用来表示完全二叉树,如果不是完全二叉树,就可能存在空间浪费,甚至极大的空间浪费。二叉树的顺序存储在物理结构上是数组,在逻辑上是一颗二叉树。

例:1、完全二叉树

2、非完全二叉树

由上面两幅图可以看出,如果二叉树是非完全二叉树,则在数组中会存在很多空位,导致空间浪费。

1.2        链式存储

链式存储就是用链表来表示二叉树,一般用结构体表示每一个节点,每个节点中有三个域,数据域和左右指针域。数据域用来存储节点的数据,左指针指向左孩子,右指针指向右孩子。

二、二叉树的顺序结构及实现

在现实中,一般的二叉树用顺序存储可能会存在大量空间浪费,所以一般只用堆来用顺序结构存储。

这里的堆与地址空间中的堆不同,前者是数据结构,后者是操作系统中的一块内存区域分段。

2.1堆的概念及结构

1.概念:如果有一个集合K,将它的所有元素按照完全二叉树的顺序存储方式存储在一个一维数组中,并满足K(i)<=K(2*i+1)且K(i)<=K(2*i+1),则称为小堆,如果大于等于就是大堆。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

2.性质:

堆中某个节点的值总是不大于或不小于其父节点的值。

堆总是一颗完全二叉树。

 

2.2堆的构建

void HeapCreate(Heap* hp, HPDataType* a, int n)
{assert(hp);//断言堆是否存在hp->_a = NULL;//将数据置为空hp->_capacity = 0;//将堆的容量清零hp->_size = 0;//将堆的大小清零
}

2.3堆的插入

void AdjustUp(HPDataType* a, int child)//向上调整堆
{int parent = (child - 1) / 2;//父节点在孩子节点-1除2的位置while (child>0)//如果孩子节点的位置不在最上面{if (a[child] < a[parent])//如果父节点小于孩子节点{swap(&a[child], &a[parent]);//交换值child = parent;parent = (child - 1) / 2;}else{break;}}
}// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{assert(hp);//断言堆是否存在if (hp->_size == hp->_capacity)//如果堆的大小等于容量,需扩容{size_t newcapacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2;HPDataType* tmp = realloc(hp->_a, sizeof(HPDataType) * newcapacity);//定义临时变量扩容if (tmp == NULL)//判断扩容是否成功{perror("realloc fail");//扩容失败return;}hp->_a = tmp;//将变量传给ahp->_capacity = newcapacity;//扩容}hp->_a[hp->_size] = x;//将值插入堆的最后hp->_size++;//堆的大小加1AdjustUp(hp->_a ,hp->_size -1);//用向上调整的方法重新调整堆,防止插入后不再是大堆或小堆
}

2.4堆顶的删除

void AdjustUp(HPDataType* a, int child)//向下调整堆
{int parent = (child - 1) / 2;//父节点在孩子节点-1除2的位置while (child>0)//如果孩子节点的位置不在最上面{if (a[child] < a[parent])//如果父节点小于孩子节点{swap(&a[child], &a[parent]);//交换值child = parent;parent = (child - 1) / 2;}else{break;}}
}
// 堆的删除
void HeapPop(Heap* hp)
{assert(hp);//断言堆是否存在assert(hp->_size > 0);//断言堆的大小是否为0swap(&hp->_a[0], &hp->_a[hp->_size - 1]);//交换堆顶和堆尾的值hp->_size--;//堆的大小减1AdjustDown(hp->_a, hp->_size, 0);//向下调整堆
}

2.5堆的完整代码


// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n)
{assert(hp);//断言堆是否存在hp->_a = NULL;//将数据置为空hp->_capacity = 0;//将堆的容量清零hp->_size = 0;//将堆的大小清零
}// 堆的销毁
void HeapDestory(Heap* hp)
{assert(hp);//断言堆是否存在free(hp->_a);//释放数据hp->_a = NULL;//将指针置为空hp->_capacity = 0;将堆的容量清零hp->_size = 0;//将堆的大小清零
}
void swap(HPDataType* x, HPDataType* y)
{HPDataType tmp = *x;*x = *y;*y = tmp;
}void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;//左孩子节点是父亲节点的2倍加1while (child < n)//如果孩子节点不在最后{//找到左右孩子中较小的if (child + 1 < n && a[child + 1] < a[child])//如果左孩子大于右孩子{++child;}if (a[child] < a[parent])//如果孩子小于父亲{swap(&a[child], &a[parent]);//交换父节点与子节点parent = child;child = parent * 2 + 1;}else{break;}}
}void AdjustUp(HPDataType* a, int child)//向上调整堆
{int parent = (child - 1) / 2;//父节点在孩子节点-1除2的位置while (child>0)//如果孩子节点的位置不在最上面{if (a[child] < a[parent])//如果父节点小于孩子节点{swap(&a[child], &a[parent]);//交换值child = parent;parent = (child - 1) / 2;}else{break;}}
}// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{assert(hp);//断言堆是否存在if (hp->_size == hp->_capacity)//如果堆的大小等于容量,需扩容{size_t newcapacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2;HPDataType* tmp = realloc(hp->_a, sizeof(HPDataType) * newcapacity);//定义临时变量扩容if (tmp == NULL)//判断扩容是否成功{perror("realloc fail");//扩容失败return;}hp->_a = tmp;//将变量传给ahp->_capacity = newcapacity;//扩容}hp->_a[hp->_size] = x;//将值插入堆的最后hp->_size++;//堆的大小加1AdjustUp(hp->_a ,hp->_size -1);//用向上调整的方法重新调整堆,防止插入后不再是大堆或小堆
}// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{assert(hp);//断言堆是否存在return hp->_a[0];
}// 堆的删除
void HeapPop(Heap* hp)
{assert(hp);//断言堆是否存在assert(hp->_size > 0);//断言堆的大小是否为0swap(&hp->_a[0], &hp->_a[hp->_size - 1]);//交换堆顶和堆尾的值hp->_size--;//堆的大小减1AdjustDown(hp->_a, hp->_size, 0);//向下调整堆
}// 堆的数据个数
int HeapSize(Heap* hp)
{assert(hp);return hp->_size;
}
// 堆的判空
int HeapEmpty(Heap* hp)
{assert(hp);return hp->_size == 0;
}// 对数组进行堆排序
void HeapSort(int* a, int n)
{for (int i = (n - 1) / 2; i >= 0; --i){AdjustDown(a, n, i);}
}

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

一颗链式二叉树一般用结构体表示,结构体中一个存储数据,一个链表指向左孩子节点,一个指向右孩子节点。

typedef int BTDataType;//方便后面改变二叉树中的数据类型typedef struct BinaryTreeNode
{BTDataType data;//二叉树存储数据的节点struct BinaryTreeNode* left;//左孩子节点struct BinaryTreeNode* right;//右孩子节点
}BTNode;//重命名

3.1链式二叉树的构建

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)//二叉树一般需要手动扩建
{BTNode* n1 = BuyNode('A');BTNode* n2 = BuyNode('B');BTNode* n3 = BuyNode('C');BTNode* n4 = BuyNode('D');BTNode* n5 = BuyNode('E');BTNode* n6 = BuyNode('F');BTNode* n7 = BuyNode('G');BTNode* n8 = BuyNode('H');n1->left = n2;n1->right = n3;n2->left = n4;n2->right = n5;n3->left = n6;n3->right = n7;n5->left = n8;return n1;
}

3.2链式二叉树的遍历

二叉树的遍历就是按照某种特殊规则,依次对二叉树的节点进行操作,每个节点只能操作一次。

二叉树的遍历一般有4种方式。

3.2.1前序遍历

前序遍历:优先操作每个节点的自身,后操作左节点,最后操作右节点。

例如上图的树就是按照A-B-D-E-H-C-F-G的顺序操作。

从A开始,先操作A的自身,再访问A的左节点B。

B节点先操作自身,再访问B的左节点D。

D节点操作自身,无孩子节点则返回到B,访问B的右节点E。 

E节点操作自身,再访问E的左节点H。

H节点操作自身,无孩子节点则返回到E,E无左孩子节点则返回到B,B的左右孩子都已访问则返回到A,访问A的右节点C。

C节点操作自身,再访问C的左节点F。

F节点操作自身,无孩子节点则返回C,访问C的右节点G

G节点操作自身,无孩子节点,访问结束。

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root) 
{if (root == NULL){printf("N ");//如果节点为空打印Nreturn;}printf("%c ", root->data);//打印节点BinaryTreePrevOrder(root->left);//访问左节点BinaryTreePrevOrder(root->right);//访问右节点
}

3.2.2中序遍历

中序遍历:优先操作每个节点的左节点,后操作自身,最后操作右节点。

上图按照D-B-H-E-A-F-C-G的顺序操作。 

从A开始,先访问A的左节点B,B节点先访问D,D无左节点,操作自身。

D无右节点,则返回B,操作B的自身,再访问B的右节点E。

E节点先访问左节点H,H无左节点,操作自身,H无右节点,则返回E节点。

E节点操作自身,无右节点,返回B节点,再返回A节点。

A节点操作自身,再访问右节点C,C节点访问左节点F。

F无左节点操作自身,无右节点,返回C节点。

C节点操作自身,再访问C的右节点G。

G无左节点,操作自身,无右节点,访问结束。

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("N ");//如果节点为空打印Nreturn;}BinaryTreePrevOrder(root->left);//访问左节点printf("%c ", root->data);//打印节点BinaryTreePrevOrder(root->right);//访问右节点
}

3.2.3后序遍历

后序遍历:优先操作每个节点的左节点,后操作右节点,最后操作自身。

上图按照D-B-H-E-F-C-G-A的顺序操作。

从A开始,访问A的左节点B,再访问B的左节点D,D无左右节点,操作自身,返回B节点。

访问B节点的右节点E,再访问E的左节点H,H无左右节点, 操作自身,返回E节点。

E节点操作自身,返回B节点。

B节点操作自身,返回A节点,再访问A节点的右节点C,访问C的左节点F。

F无左右节点,操作自身,返回C节点,访问C节点的右节点G。

G无左右节点,操作自身,返回C节点。

C节点操作自身,返回A节点。

A节点操作自身,访问结束。

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("N ");//如果节点为空打印Nreturn;}BinaryTreePrevOrder(root->left);//访问左节点BinaryTreePrevOrder(root->right);//访问右节点printf("%c ", root->data);//打印节点
}

3.2.4层序遍历

 层序遍历:按照二叉树的每一层,一层一层的从左向右操作。

实现二叉树的层序遍历需要用到队列的知识。

队列是一个有着先进先出原则的链表,先进的元素永远比后进的元素先出来。通过队列的规则,只要二叉树的每一个节点出队列时将其左右孩子节点入队列即可完成二叉树的层序遍历。

上图的操作顺序是A-B-C-D-E-F-G-H

A节点入队列,A节点出队列,将A的左右子节点BC入队列。

此时队列中有B-C,将B出队列,B的左右子节点DE入队列。

此时队列中有C-D-E,将C出队列,C的左右子节点FG入队列。

此时队列中有D-E-F-G,将D出队列,D无子节点。

此时队列中有E-F-G,将F出队列,F的左节点H入队列。

此时队列中有F-G-H,均无子节点,一个一个出队列即可。

 队列头文件代码

#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
typedef  struct BinTreeNode* QDataType;
// 链式结构:表示队列 
typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QNode;
// 队列的结构 
typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;
// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

队列代码

#include"1.h"// 初始化队列 
void QueueInit(Queue* q)
{assert(q);//断言队列是否存在q->phead = NULL;//将头节点的置为空q->ptail = NULL;//将尾节点置为空q->size = 0;//将队列大小初始化为0
}// 队尾入队列 
void QueuePush(Queue* pq, QDataType x)
{assert(pq);//断言队列是否存在QNode* newnode = (QNode*)malloc(sizeof(QNode));//创建临时变量if (newnode == NULL)//判断变量是否创建成功{perror("malloc fail");return;}newnode->data = x;//将要入队列的值给临时变量newnode->next = NULL;//将临时变量的指向下一个节点的指针置空if (pq->ptail)//如果尾队列存在,则队列中含有数据{pq->ptail->next = newnode;//将原本尾队列指向下一个节点的指针指向临时变量pq->ptail = newnode;//将临时变量设为尾节点}else//尾队列不存在,则队列为空{pq->phead = pq->ptail = newnode;//将头节点和尾节点都设为临时变量}pq->size++;//队列大小加1
}// 队头出队列 
void QueuePop(Queue* q)
{assert(q);//断言队列是否存在assert(q->phead != NULL);//断言队列的头节点是否为空if (q->phead->next == NULL)//队列头节点的下一个节点为空,则队列中只有一个元素{free(q->phead);//释放头节点q->phead = q->ptail = NULL;//将头节点和尾节点置空}else//队列中不止1个元素{QNode* next = q->phead->next;//创建临时变量指向头节点的下一个节点free(q->phead);//是否头节点q->phead = next;//将头节点的下一个节点设为头节点}q->size--;//队列大小减1
}// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{assert(q);//断言队列是否存在assert(q->phead != NULL);//断言队列的头节点是否为空return q->phead->data;//返回队列头节点的数据
}// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{assert(q);//断言队列是否存在assert(q->ptail != NULL);//断言队列的尾节点是否为空return q->ptail->data;//返回队列尾节点的数据
}// 获取队列中有效元素个数 
int QueueSize(Queue* q)
{assert(q);//断言队列是否存在return q->size;//返回队列的大小
}// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{assert(q);//断言队列是否存在return q->size == 0;//判断队列的大小是否为0
}// 销毁队列 
void QueueDestroy(Queue* q)
{assert(q);//断言队列是否存在QNode* cur = q->phead;//定义临时节点等于头节点while (cur)//如果头节点不为空{QNode* next = cur->next;//定义临时节点指向头节点的下一个节点free(cur);//释放头节点cur = next;//将下一个节点当作头节点}q->phead = NULL;//将头节点指针置空q->ptail = NULL;//将尾节点指针置空q->size = 0;//将队列大小置为0
}

 二叉树头文件

#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
typedef int BTDataType;//方便后面改变二叉树中的数据类型typedef struct BinaryTreeNode
{BTDataType data;//二叉树存储数据的节点struct BinaryTreeNode* left;//左孩子节点struct BinaryTreeNode* right;//右孩子节点
}BTNode;//重命名#include"1.h"// 通过前序遍历的数组"ABD##E#H##CF##G##"构建 二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

二叉树代码

#include"tree.h"
BTNode* BuyNode(BTDataType a)//a是要创建的值
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));//定义临时变量扩容if (newnode == NULL){printf("malloc fail");//扩容失败return NULL;}newnode->data = a;newnode->left = NULL;newnode->right = NULL;return newnode;
}
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)//二叉树一般需要手动扩建
{BTNode* n1 = BuyNode('A');BTNode* n2 = BuyNode('B');BTNode* n3 = BuyNode('C');BTNode* n4 = BuyNode('D');BTNode* n5 = BuyNode('E');BTNode* n6 = BuyNode('F');BTNode* n7 = BuyNode('G');BTNode* n8 = BuyNode('H');n1->left = n2;n1->right = n3;n2->left = n4;n2->right = n5;n3->left = n6;n3->right = n7;n5->left = n8;return n1;
}// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{assert(root);free(root);}// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{return root == NULL ? 0 :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);
}// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{assert(k > 0);if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* ret1= BinaryTreeFind(root->right, x);if (ret1){return ret1;}BTNode* ret2=BinaryTreeFind(root->left , x);if (ret2){return ret2;}return NULL;
}// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root) 
{if (root == NULL){printf("N ");//如果节点为空打印Nreturn;}printf("%c ", root->data);//打印节点BinaryTreePrevOrder(root->left);//访问左节点BinaryTreePrevOrder(root->right);//访问右节点
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("N ");//如果节点为空打印Nreturn;}BinaryTreePrevOrder(root->left);//访问左节点printf("%c ", root->data);//打印节点BinaryTreePrevOrder(root->right);//访问右节点
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("N ");//如果节点为空打印Nreturn;}BinaryTreePrevOrder(root->left);//访问左节点BinaryTreePrevOrder(root->right);//访问右节点printf("%c ", root->data);//打印节点
}
// 层序遍历
void TreeLevelOrder(BTNode* root)
{Queue q;//定义临时节点QueueInit(&q);//创建临时节点if (root)//如果节点不为空{QueuePush(&q, root);//节点入队列}while (!QueueEmpty(&q))//如果队列不为空{BTNode* front = QueueFront(&q);//获取队头元素QueuePop(&q);//队头元素出队列if (front)//如果节点存在{printf("%d ", front->data);//打印节点数据// 带入下一层QueuePush(&q, front->left);//左孩子节点入队列QueuePush(&q, front->right);//右孩子节点入队列}else{printf("N ");//不存在打印N}}printf("\n");QueueDestroy(&q);//销毁临时节点,防止空间泄露
}

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

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

相关文章

鸿蒙原OS开发实例:【ArkTS类库单次I/O任务开发】

Promise和async/await提供异步并发能力&#xff0c;适用于单次I/O任务的场景开发&#xff0c;本文以使用异步进行单次文件写入为例来提供指导。 实现单次I/O任务逻辑。 import fs from ohos.file.fs; import common from ohos.app.ability.common;async function write(data:…

深入解析大数据体系中的ETL工作原理及常见组件

** 引言 关联阅读博客文章&#xff1a;探讨在大数据体系中API的通信机制与工作原理 关联阅读博客文章&#xff1a;深入理解HDFS工作原理&#xff1a;大数据存储和容错性机制解析 ** 在当今数字化时代&#xff0c;大数据处理已经成为了企业成功的重要组成部分。而在大数据处…

OpenHarmony实战:小型系统 STM32MP1 芯片移植案例

本文章基于意法半导体 STM32MP157 芯片的小熊派 BearPi-HM Micro 开发板&#xff0c;进行小型带屏开发板的移植&#xff0c;实现了 ace_engine_lite、arkui_ui_lite、aafwk_lite、appexecfwk_lite、HDF 等部件基于 OpenHarmony LiteOS-A 内核的适配。移植架构上采用 Board 与 S…

shopee虾皮怎么分析竞品?这些技巧能帮你提升出单量90%+

“竞品分析”长久以来都是运营所纠结的核心问题&#xff0c;没有分析出对标竞品怎么增长销量&#xff0c;却被其他竞品给町上了&#xff0c;导致自身销量不增反减。这种情况我们是无法直观从自身的店铺及商品本身去找到原因的&#xff0c;本质的问题在竞品身上。我们无法控制有…

基于RDMA的云服务能力实践与探索

01 背景 随着基于大数据大模型构建的数据系统越来越有商业价值&#xff0c;机器学习的玩家也越来越多&#xff0c;数据量越来越大。为解决海量数据在服务器之间的同步效率问题&#xff0c;RDMA(Remote Direct Memory Access) 技术逐渐走进了网络技术人员的视野。RDMA为什么…

鸿鹄工程项目管理系统源码:Spring Boot带来的快速开发与部署体验

随着企业规模的不断扩大和业务的快速发展&#xff0c;传统的工程项目管理方式已经无法满足现代企业的需求。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;企业需要借助先进的数字化技术进行转型。本文将介绍一款采用Spring CloudSpring BootMybat…

蓝桥杯单片机速成6-DS1302

目录 一、电路图 二、底层驱动 三、实际应用 四、时序 一、电路图 上图是寄存器地址定义&#xff0c;时分秒&#xff0c;年月日等等 DS1302有自己的时钟线SCLK&#xff0c;不会跟单总线一样因为没有自己的时钟线而导致温度读不出来 CH&#xff1a;时钟静止&#xff0c;置1时…

2024最新软件测试【测试理论+ 抓包与网络协议】面试题(内附答案)

一、测试理论 3.1 你们原来项目的测试流程是怎么样的? 我们的测试流程主要有三个阶段&#xff1a;需求了解分析、测试准备、测试执行。 1、需求了解分析阶段 我们的 SE 会把需求文档给我们自己先去了解一到两天这样&#xff0c;之后我们会有一个需求澄清会议&#xff0c; …

【Linux】防火墙iptables详解

目录 一、防护墙概述 二、防火墙 2.1名词 2.2使用规则 2.3表与链 2.3.1简介 2.3.2每个表说明 1&#xff09;filter表 2)nat表 2.4环境的配置 2.5iptables的命令参数 2.6 配置filter表规则 2.6.1备份与恢复 2.6.2案例1&#xff1a;禁止访问22端口 2.6.3案例2&…

【微信小程序】流量主-激励视频(激励广告)下发策略,每天三次免费体验,然后再次点击触发激励视频,当日不再触发。

如题&#xff1a; 允许用户有三次体验效果&#xff0c;然后弹出激励视频弹窗&#xff0c;之后当日不再弹出。 体验小程序&#xff1a; /*** 判断当前项目当天是否点击超过3次&#xff0c;触发广告效果。* 若&#xff0c;当天低于三次&#xff0c;则新增&#xff0c;若高于…

图片标注编辑平台搭建系列教程(4)——fabric几何定制渲染

背景 标注的几何&#xff0c;有时需要一些定制化的渲染样式&#xff0c;例如&#xff0c;线中间展示箭头&#xff0c;表示方向。本期教程教大家如何实现fabric几何定制化渲染。 带箭头的线 fabric提供了一些原生的几何&#xff0c;例如Point、Polyline、Polygon。同时提供了…

前端学习<三>CSS进阶——03-网页设计和开发中,那些困扰大神的关于字体的知识

前言 我周围的码农当中&#xff0c;有很多是技术大神&#xff0c;却常常被字体这种简单的东西所困扰。 这篇文章&#xff0c;我们来讲一讲关于字体的常识。这些常识所涉及到的问题&#xff0c;有很强的可操作性&#xff0c;都是在实际业务中真实遇到的&#xff0c;都是需要开…

软件资源分享六:EPLAN Electric P8 2024 | Eplan 2024 中文版软件介绍+保姆级安装教程

原文链接&#xff1a;安装激活教程 EPLAN Electric P8 2024 | Eplan 2024 中文版软件介绍安装教程 EPLAN 2024是一款电气设计软件&#xff0c;它可以用于自动化系统的设计、文档编制和维护。EPLAN可以对电气设计的各个方面进行完整的支持&#xff0c;包括电气控制系统、机械设…

Spring 整合 Log4j2日志框架

1. Log4j2日志概述 在项目开发中&#xff0c;日志十分的重要&#xff0c;不管是记录运行情况还是定位线上问题&#xff0c;都离不开对日志的分析。日志记录了系统行为的时间、地点、状态等相关信息&#xff0c;能够帮助我们了解并监控系统状态&#xff0c;在发生错误或者接近某…

【JavaSE】一维数组和二维数组详解

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 一维数组 基本语法 初始化 遍历和打印 数组是引用型变量 基本类型变量与引用类型变量的区别 null 数组传参和返回 总结 二维数组 基本语法 初始化 遍历和打印 一维数组…

论文精读--GPT4

现有的所有模型都无法做到在线学习&#xff0c;能力有限&#xff0c;而让大模型拥有一个tools工具库&#xff0c;则可以使大模型变成一个交互式的工具去协调调用API完成任务&#xff0c;同时GPT4还联网了&#xff0c;可以不断地更新自己的知识库 多模态模型&#xff0c;接受文…

单V及多V感知在自动驾驶在恶劣环境条件下的感知提升方案

单V及多V感知在自动驾驶在恶劣环境条件下的感知提升方案 附赠自动驾驶学习资料和量产经验&#xff1a;链接 自动驾驶中的视觉感知是车辆在不同交通条件下安全、可持续地行驶的关键部分。然而&#xff0c;在大雨和雾霾等恶劣天气下&#xff0c;视觉感知性能受到多种降级效应的极…

Pygame基础9-射击

简介 玩家用鼠标控制飞机&#xff08;白色方块&#xff09;移动&#xff0c;按下鼠标后&#xff0c;玩家所在位置出现子弹&#xff0c;子弹匀速向右飞行。 代码 没有什么新的东西&#xff0c;使用两个精灵类表示玩家和子弹。 有一个细节需要注意&#xff0c;当子弹飞出屏幕…

RK3568 学习笔记 : 独立修改与编译 u-boot

前言 开发板&#xff1a;【正点原子】ATomPi-CA1 开发板&#xff0c;配置&#xff1a;RK3568&#xff0c;4GB DDRAM 64GB emmc 开发板资料给了 u-boot 与 Linux kernel 源码&#xff0c;尝试手动编译。 本篇记录 收到编译 RK3568 平台 u-boot 的方法 环境搭建 由于 RK 平台…

椋鸟数据结构笔记#5:树、二叉树基础

文章目录 树树的相关概念树的表示 二叉树基础二叉树分类满二叉树完全二叉树 二叉树的性质二叉树的存储结构顺序存储链式存储 萌新的学习笔记&#xff0c;写错了恳请斧正。 树 树是一种非线性的数据结构&#xff0c;它是由 n 个节点组成的一个具有层次关系的数据集合。其大概结…