【初阶数据结构】8.二叉树(3)

news2024/11/16 7:39:59

文章目录

  • 4.实现链式结构二叉树
    • 4.1 前中后序遍历
      • 4.1.1 遍历规则
      • 4.1.2 代码实现
    • 4.2 结点个数以及高度等
    • 4.3 层序遍历
    • 4.4 判断是否为完全二叉树
    • 4.5层序遍历和判断是否为完全二叉树完整代码


4.实现链式结构二叉树

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

typedef int BTDataType;
// 二叉链
typedef struct BinaryTreeNode
{
    struct BinTreeNode* left; // 指向当前结点左孩子 
    struct BinTreeNode* right; // 指向当前结点右孩子 
    BTDataType val; // 当前结点值域 
}BTNode;

二叉树的创建方式比较复杂,为了更好的步入到二叉树内容中,我们先手动创建一棵链式二叉树:

BTNode* BuyBTNode(int val)
{
    BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
    if (newnode == NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    newnode->val = val;
    newnode->left = NULL;
    newnode->right = NULL;
    return newnode;
}
BTNode* CreateTree()
{
    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);
    n1->left = n2;
    n1->right = n4;
    n2->left = n3;
    n4->left = n5;
    n4->right = n6;
    n5->left = n7;
    return n1;
}

回顾二叉树的概念,二叉树分为空树和非空二叉树,非空二叉树由根结点、根结点的左子树、根结点的右子树组成的

img

根结点的左子树和右子树分别又是由子树结点、子树结点的左子树、子树结点的右子树组成的,因此二叉树定义是递归式的,后序链式二叉树的操作中基本都是按照该概念实现的。


4.1 前中后序遍历

二叉树的操作离不开树的遍历,我们先来看看二叉树的遍历有哪些方式

img


4.1.1 遍历规则

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

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

    访问顺序为:根结点、左子树、右子树

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

    访问顺序为:左子树、根结点、右子树

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

    访问顺序为:左子树、右子树、根结点


4.1.2 代码实现

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

以前序遍历为例:

img


4.2 结点个数以及高度等

Tree.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

//定义二叉树的链式结构
//二叉树结点的结构
//Binary 二进制的意思
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 BinaryTreeDestory(BTNode** root);

Tree.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Tree.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)
	{
		//printf("NULL ");
		return;
	}
	PostOrder(root->left);//左
	PostOrder(root->right);//右
	printf("%d ", root->data);//中
}

/*
// ⼆叉树结点个数
//这个会出现错误,因为size是全局变量,如果调用两次,会导致size的值变成两倍
int size = 0;
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	++size;
	BinaryTreeSize(root->left);
	BinaryTreeSize(root->right);
	return size;
}

// ⼆叉树结点个数
//错误的写法
//这个会出现错误,因为size是全局变量,如果调用两次,会导致size的值变成两倍
void BinaryTreeSize(BTNode* root,int* psize)
{
	if (root == NULL)
	{
		return 0;
	}
	++(*psize);
	BinaryTreeSize(root->left,psize);
	BinaryTreeSize(root->right,psize);
}
*/

// ⼆叉树结点个数
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层结点个数
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 leftDep = BinaryTreeDepth(root->left);
	int rightDep = BinaryTreeDepth(root->right);

	return leftDep > rightDep ? leftDep + 1 : rightDep + 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 BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(& ((*root) ->left)  );
	BinaryTreeDestory(& ((*root) ->right) );

	free(*root);
	*root = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#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->left = node4;
	//node2->right = node5;
	//node3->left = node6;

	/*PreOrder(node1);
	printf("\n");
	InOrder(node1);
	printf("\n");
	PostOrder(node1);*/

	printf("size:%d\n", BinaryTreeSize(node1));// ⼆叉树结点个数
	printf("leaf size: %d\n", BinaryTreeLeafSize(node1));// ⼆叉树叶⼦结点个数
	printf("第K层 size:%d\n", BinaryTreeLevelKSize(node1, 2));// ⼆叉树第k层结点个数
	printf("depth/height:%d\n", BinaryTreeDepth(node1));//二叉树深度
	BTNode* find = BinaryTreeFind(node1, 33);
	printf("%s\n", find == NULL ? "未找到!" : "找到了!");// ⼆叉树查找值为x的结点
	BinaryTreeDestory(&node1);// ⼆叉树销毁
}

int main() {
	test01();
	return 0;
}

4.3 层序遍历

除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历

设二叉树的根结点所在层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历.

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

img

// 层序遍历
void LevelOrder(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    QueuePush(&q, root);
    while (!QueueEmpty(&q))
    {
        BTNode* top = QueueFront(&q);
        printf("%c ", top->data);
        QueuePop(&q);
        if (top->
            _left) 
        {
            QueuePush(&q, top->
                      _left);
        }
        if (top->
            _right) 
        {
            QueuePush(&q, top->
                      _right);
        }
    }
    QueueDesTroy(&q);
}

4.4 判断是否为完全二叉树

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

4.5层序遍历和判断是否为完全二叉树完整代码

tree.h

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

//定义二叉树的链式结构
//二叉树结点的结构
//Binary 二进制的意思
typedef int BTDataType;

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

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

//定义队列结构
//typedef int QDataType;
//typedef struct BinaryTreeNode* QDataType;
typedef struct BTNode* QDataType;

typedef struct QueueNode {
	QDataType data;
	struct QueueNode* next;
}QueueNode;

typedef struct Queue {
	QueueNode* phead;//队头:删
	QueueNode* ptail;//队尾:插
	int size;//保存队列有效数据个数
}Queue;

//初始化队列
void QueueInit(Queue* pq);

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

//队列判空
bool QueueEmpty(Queue* pq);

// 出队列,队头
void QueuePop(Queue* pq);

//取队头数据
QDataType QueueFront(Queue* pq);

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


//层序遍历
//借助数据结构---队列
void LevelOrder(BTNode* root);

tree.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "LevelOrder.h"

//初始化队列
void QueueInit(Queue* pq) {
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

// 入队列,队尾
void QueuePush(Queue* pq, QDataType x) {
	assert(pq);
	//申请新结点
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL) {
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->phead == NULL) {//判断队列是否为空
		pq->phead = pq->ptail = newnode;
	}
	else {
		//队列不为空
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;//入一次,size++ 一次
}

//队列判空
bool QueueEmpty(Queue* pq) {
	assert(pq);
	return (pq->phead == NULL) && (pq->ptail == NULL);
}

// 出队列,队头
void QueuePop(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));

	//只有一个结点的情况,避免ptail变成野指针
	if (pq->ptail == pq->phead) {
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else {
		//删除队头元素
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	--pq->size;//出一次,size-- 一次
}

//取队头数据
QDataType QueueFront(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));//判断队列不为空

	return pq->phead->data;
}

//销毁队列
void QueueDestroy(Queue* pq) {
	assert(pq);
    /*注意下面这行在这里要注释掉,不然会报错*/
	//assert(!QueueEmpty(pq));//判断队列不为空,空队列不需要销毁

	QueueNode* pcur = pq->phead;
	while (pcur) {
		QueueNode* Next = pcur->next;
		free(pcur);
		pcur = Next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

// ⼆叉树销毁
void BinaryTreeDestory(BTNode * *root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*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;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "LevelOrder.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);


	node1->left = node2;
	node1->right = node3;
	node2->left = node4;

	LevelOrder(node1);
	bool ret = BinaryTreeComplete(node1);
	printf("%s\n", ret == false ? "不是完全二叉树!" : "是完全二叉树!");
	BinaryTreeDestory(&node1);// ⼆叉树销毁
}

int main() {
	test01();
	return 0;
}

打印:

img

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

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

相关文章

巴斯勒相机(Basler) ACE2 dart 系列说明和软件

巴斯勒相机(Basler) ACE2 dart 系列说明和软件

NeuralGCM:革新气候预测的机器学习新纪元

在地球变暖成为全球关注焦点的今天&#xff0c;精确预测气候变化及其影响成为了科学界亟待解决的重大课题。传统基于物理的气候模型&#xff08;GCM&#xff0c;全球气候模型&#xff09;在预测大气、海洋、冰层等复杂系统时虽已取得显著进展&#xff0c;但计算成本高、耗时长且…

系统模块时序图的重要性:解锁系统模块交互的全景视图

在复杂的系统开发中,理解和管理不同模块之间的交互是成功的关键。时序图是一种有效的工具,可以帮助我们清晰地展示这些交互,提升设计和开发的效率。本文将深入探讨系统模块之间的时序图,并通过实例展示其实际应用。 1. 什么是系统模块之间的时序图? 系统模块之间的时序图…

Eclipse 生成 jar 包

打开 Jar 文件向导 Jar 文件向导可用于将项目导出为可运行的 jar 包。 打开向导的步骤为: 在 Package Explorer 中选择你要导出的项目内容。如果你要导出项目中所有的类和资源&#xff0c;只需选择整个项目即可。点击 File 菜单并选择 Export。在输入框中输入"JAR"…

Robot Operating System——Parameter设置的预处理、校验和成功回调

大纲 预处理校验成功回调完整代码测试总结 在《Robot Operating System——对Parameter设置进行校验》一文中&#xff0c;我们通过Node的add_on_set_parameters_callback方法&#xff0c;设置了一个回调函数&#xff0c;用于校验传递过来的Parameter参数。但是这个方法并不能对…

【UbuntuDebian安装Nginx】在线安装Nginx

云计算&#xff1a;腾讯云轻量服务器 操作系统&#xff1a;Ubuntu-v22 1.更新系统软件包列表 打开终端并运行以下命令来确保你的系统软件包列表是最新的&#xff1a; sudo apt update2.安装 Nginx 使用以下命令安装 Nginx&#xff1a; sudo apt install nginx3.启动 Nginx…

基于python的BP神经网络回归模型

1 导入必要的库 import pandas as pd from sklearn.model_selection import train_test_split, cross_val_score, KFold import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, r2_score …

电脑如何进行录屏?电脑录屏无压力!

在数字时代&#xff0c;屏幕录制已成为我们日常生活和工作中不可或缺的一部分。无论你是想要制作教程、记录游戏过程&#xff0c;还是捕捉在线会议的精彩瞬间&#xff0c;掌握屏幕录制的方法都显得尤为重要。本文将为你详细介绍电脑如何进行录屏&#xff0c;帮助你轻松捕捉屏幕…

音视频入门基础:H.264专题(17)——FFmpeg源码获取H.264裸流文件信息(视频压缩编码格式、色彩格式、视频分辨率、帧率)的总流程

音视频入门基础&#xff1a;H.264专题系列文章&#xff1a; 音视频入门基础&#xff1a;H.264专题&#xff08;1&#xff09;——H.264官方文档下载 音视频入门基础&#xff1a;H.264专题&#xff08;2&#xff09;——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…

科技核心 电力方向

【电力投资】电力体制改革***电量投资风险控制研究 【配电网管理】基于***配电网线损数据精细化管理研究 【电价优化】基于***能源系统电价优化模型研究 【电力营销】基于***电力营销业务数据***

用python程序发送文件(python实例二十六)

目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.文件上传 3.1 代码构思 3.2 服务端代码 3.3 客户端代码 3.4 运行结果 4.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具…

SqlSugar删除没有定义主键的实体类对应的数据库表数据

一般而言&#xff0c;使用SqlSugar的DbFirst功能创建数据库表实体类时&#xff0c;如果数据库表有主键&#xff0c;生成的实体类对应属性也会标识为主键&#xff0c;如下图所示。   但有时候生成的实体类没有自动配置主键&#xff0c;这时可以通过以下方式进行删除操作&…

littlefs文件系统的移植和测试

简介 LittleFS 由ARM官方发布&#xff0c;ARM mbedOS的官方推荐文件系统&#xff0c;具有轻量级&#xff0c;掉电安全的特性。主要用在微控制器和flash上 掉电恢复&#xff0c;在写入时即使复位或者掉电也可以恢复到上一个正确的状态。 擦写均衡&#xff0c;有效延长flash的使…

微软蓝屏事件:网络安全的多维挑战与应对策略

一、引言 1. 事件概述&#xff1a;微软蓝屏事件的影响与范围 微软蓝屏事件&#xff0c;这一近期震动全球科技界的重大事件&#xff0c;起因于一次看似平常的软件更新。美国电脑安全技术公司“众击”发布的更新包中隐藏着一个致命的“缺陷”&#xff0c;这个缺陷如同潜伏的病毒…

goland设置Gin框架中tmpl模板的语法提示的方法

goland设置Gin框架中tmpl模板的语法提示的方法 前言 在Gin中&#xff0c;我们要使用 .tmpl 结尾的模板文件&#xff0c;但是我们在new的时候&#xff0c;发现没有对应的文件&#xff0c;所以它就会被当成普通的文件来进行解析&#xff0c;因此也没有提示&#xff0c;这对我们…

MySQL --- 表的操作

在对表进行操作时&#xff0c;需要先选定操作的表所在的数据库&#xff0c;即先执行 use 数据库名; 一、创建表 create table 表名( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎 ; 说明&#xff1a…

个性化音频生成GPT-SoVits部署使用和API调用

一、训练自己的音色模型步骤 1、准备好要训练的数据&#xff0c;放在Data文件夹中&#xff0c;按照文件模板中的结构进行存放数据 2、双击打开go-webui.bat文件&#xff0c;等待页面跳转 3、页面打开后&#xff0c;开始训练自己的模型 &#xff08;1&#xff09;、人声伴奏分…

Mac文件拷贝到移动硬盘怎么做Mac拷贝之后Win电脑里看不到

在日常使用mac电脑的过程中&#xff0c;我们经常需要将一些重要的文件备份到外部硬盘上&#xff0c;以防止数据丢失或电脑故障。传输文件到硬盘可以通过多种方法实现&#xff0c;比如拖拽或者复制至移动硬盘&#xff0c;但有时也会遇到移动硬盘无法粘贴&#xff0c;或拷贝后无法…

FastAPI(七十五)实战开发《在线课程学习系统》接口开发-- 创建课程

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 上次我们分享了&#xff0c;FastAPI&#xff08;七十四&#xff09;实战开发《在线课程学习系统》接口开发-- 删除留言 从本篇文章开始&#xff0c;…

DVWA中命令执行漏洞细说

在攻击中&#xff0c;命令注入是比较常见的方式&#xff0c;今天我们细说在软件开发中如何避免命令执行漏洞 我们通过DVWA中不同的安全等级来细说命令执行漏洞 1、先调整DVWA的安全等级为Lower,调整等级在DVWA Security页面调整 2、在Command Injection页面输入127.0.0.1&…