【算法与数据结构】深入二叉树实现超详解

请添加图片描述

文章目录

  • 📝前言
  • 🌠 接口函数
    • ✏️ 实现函数
      • 🌉创建树的新节点
      • 🌠通过前序遍历的数组构建二叉树
      • 🌉包装通过前序遍历的数组构建二叉树
      • 🌠二叉树的销毁
      • 🌠层次遍历
        • 🌠第一种实现:不用数组模拟队列
        • 🌠第二种实现:不用数组模拟队列,创建队列实现
      • 🌉判断二叉树是否是完全二叉树
      • 🌠二叉树节点个数
      • 🌉二叉树叶子节点个数
      • 🌉二叉树第k层节点个数
      • 🌠二叉树查找值为x的节点
    • 🌉 完整代码实现
    • 🌉测试一下效果
  • 🚩总结


📝前言

上节我们学习了二叉树(前中后)序遍历
这节将实现二叉树。

让我们复习一下二叉树,接着就是二叉树的实现了😊,学习起来吧!

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
    在这里插入图片描述
    本节代码举例图
    在这里插入图片描述
    启动!—》

🌠 接口函数

头文件Tree.h,这里封装了树的接口,需要时直接#include"Tree.h"。

#include<stdio.h>
#include<stdlib.h># define _CRT_SECURE_NO_WARNINGS 1
//这里使用char是因为接下来测试用字符,
//当然你也可以改为别的类型哦
typedef char BTDataType;// 二叉树节点结构
typedef struct BTNode {BTDataType data;struct BTNode* left;struct BTNode* right;
}BTNode;//创建树的新节点
BTNode* BuyBTNode(int val);//辅助函数:判断队列是否为空
int QueueIsEmpty(int front, int rear);// 通过前序遍历的数组构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);// 二叉树销毁
void BinaryTreeDestory(BTNode** root);// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(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 LevelOrder(BTNode* root);

✏️ 实现函数

🌉创建树的新节点

//创建新节点
BTNode* BuyBTNode(int val)
{//使用malloc动态申请内存,分配一个BTNode类型的结构体。BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));if (newNode == NULL)//{perror("malloc fail");//检查malloc是否成功return ;}newNode->data = val;//将新节点的data字段设置为传入的val值。newNode->left = NULL;//将新节点的left和right子节点指针设置为NULL。newNode->right = NULL;return newNode;//返回新创建的节点指针。
}

动态创建一个新的二叉树节点,并初始化其data和子节点指针字段。这样就可以在构建二叉树的过程中不断调用这个函数来创建新的节点,然后连接起来形成树形结构。

🌠通过前序遍历的数组构建二叉树

BTNode* BinaryTreeCreateHelper(BTDataType* a, int n, int* pi)
{									//n是数组a的长度//pi是索引指针,用于遍历a数组if (*pi >= n || a[*pi] == 'N')//检查*pi是否越界或当前元素为'N',如果是则跳过该节点,					(*pi)++向后移动。这里的N意思是空{(*pi)++;//return NULL;}//否则,调用BuyBTNode函数创建新的节点,并将a[*pi]的值赋给节点。(*pi)++后移。BTNode* newNode = BuyBTNode(a[(*pi)++]);if (newNode != NULL){newNode->left = BinaryTreeCreateHelper(a, n, pi);//递归创建左子节点newNode->right = BinaryTreeCreateHelper(a, n, pi);//递归创建右子节点}return newNode;//每次递归都返回创建好的节点。}

通过递归和索引下标的递增,就可以按先序遍历顺序依次创建二叉树的每个节点,并建立节点之间的父子关系,最终返回根节点,就完成了整个二叉树的创建。利用了递归的思想,通过不断调用自身函数来实现结构的生成。

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{return BinaryTreeCreateHelper(a, n, pi);
}

🌉包装通过前序遍历的数组构建二叉树

BinaryTreeCreate函数是对BinaryTreeCreateHelper的一个包装。BinaryTreeCreateHelper负责具体创建二叉树的递归操作,BinaryTreeCreate作为入口函数,接收参数,调用辅助函数创建树,并返回根节点指针。

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{return BinaryTreeCreateHelper(a, n, pi);
}

这样设计有利于函数单一职责原则,明确划分主函数和辅助函数的职责,更好地实现二叉树从先序序列的创建。

🌠二叉树的销毁

BinaryTreeDestory函数是用于释放二叉树占用的内存的。

void BinaryTreeDestory(BTNode** root)
{if (*root != NULL){BinaryTreeDestory(&((*root)->left)); //释放左子树BinaryTreeDestory(&((*root)->right)); //释放右子树free(*root);//*root = NULL;}
}

🌠层次遍历

什么是层次遍历呢?
在这里插入图片描述
什么是层次遍历呢?我们先了解下层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

So->

因此我们可以用队列控制,出队入队遍历二叉树

🌠第一种实现:不用数组模拟队列

使用队列queue来模拟层序遍历,front和rear指针分别表示队头和队尾,front指针控制出队,rear指针控制入队,两个指针同时移动,就可以模拟出层序的遍历顺序。

void LevelOrder(BTNode* root) 
{if (root == NULL)//首先判断如果根节点为空,直接返回return;BTNode* queue[1000]; //使用队列queue来模拟层序遍历,front和rear指针分别表示队头和队尾int front = 0, rear = 0;queue[rear++] = root;//根节点入队while (front < rear) //循环结束时,front指针一定会等于rear指针,队列为空,遍历结束{											//关键是front指针每次递增1,实现队首出队BTNode* current = queue[front++];//取出队首节点currentprintf("%c ", current->data); if (current->left != NULL)//如果当前节点有左子节点,将左子节点加入队尾queue[rear++] = current->left;if (current->right != NULL)//如果当前节点有右子节点,将右子节点加入队尾queue[rear++] = current->right;//rear指针每次递增1,实现节点入队}
}

在这里插入图片描述
在这里插入图片描述

🌠第二种实现:不用数组模拟队列,创建队列实现

void LevelOrder(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 ");}}printf("\n");QueueDestroy(&q);//销毁队列
}

在这里插入图片描述

while循环条件是判断队列是否为空,不是front和rear指针比较。每次从队列取出节点front后,直接打印数据,然后入队其子节点。如果front为空,打印一个标识符"N"。

🌉判断二叉树是否是完全二叉树

在这里插入图片描述

//辅助函数:判断队列是否为空
int QueueIsEmpty(int front, int rear)
{return front == rear;
}//判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{if (root == NULL)return 1; //空树是完全二叉树BTNode* queue[1000];//用于存放节点的队列int front = 0, rear = 0;int flag = 0;//用于标记是否遇到空节点//根节点入队queue[rear++] = root;while (!QueueIsEmpty(front, rear)){BTNode* current = queue[front++];//如果遇到空节点后面还有非空节点,说明不是完全二叉树if (current == NULL)flag = 1;else{//左右孩子入队queue[rear++] = current->left;queue[rear++] = current->right;//如果遇到空节点后面还有非空节点,说明不是完全二叉树if (flag)return 0;}}//遍历结束,说明是完全二叉树return 1;
}

首先使用队列来层序遍历二叉树根节点入队,循环取出队首节点current,如果current为空,设置flag标记为1,表示遇到了空节点,如果current非空,将其左右孩子入队,如果flag已经被设置为1,说明之前遇到了空节点,现在又有非空节点,必然不是完全二叉树,直接返回0,遍历结束,说明没有发现flag为1后还有非空节点的情况,即树节点是从左到右依次完整填充每一层的,就是完全二叉树,返回1

🌠二叉树节点个数

在这里插入图片描述

//二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL)return 0;//递归终止条件是空节点,返回0return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);//非空节点返回它本身+左子树+右子树节点个数的和
}

这个函数BinaryTreeSize用来计算一棵二叉树的节点总个数。,如果传入的根节点root为空,说明这棵二叉树没有节点,返回0,如果不是空节点,则:这个节点本身就是1个节点,加上它左子树的节点个数BinaryTreeSize(root->left),加上它右子树的节点个数BinaryTreeSize(root->right),递归计算左右子树节点个数,然后汇总返回。时间复杂度为O(N),只需要一次遍历二叉树。

🌉二叉树叶子节点个数

//二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;//如果root节点的左右子节点都为空,则root就是叶子节点,返回1return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

如果传入的根节点root为空,说明这棵二叉树没有节点,返回0如果root节点的左右子节点都为空,则root就是叶子节点,返回1,否则:计算root的左子树叶子节点个数BinaryTreeLeafSize(root->left),计算root的右子树叶子节点个数BinaryTreeLeafSize(root->right)
汇总返回左右子树叶子节点个数之和

🌉二叉树第k层节点个数

//二叉树滴k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;//计算root的左子树第k-1层节点个数          //计算root的右子树第k-1层节点个数return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

递归基线是查询第一层或空树时返回值,每次递归都将层数k减1,向下查询下一层,从下至上不断累加各层节点个数,时间复杂度为O(N),只需要一次遍历。利用层序遍历思想实现对指定层的统计。

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

//二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)//根节点root为空,直接返回NULLreturn NULL;if (root->data == x)//root节点的数据等于查找值x,返回rootreturn root;BTNode* leftResult = BinaryTreeFind(root->left, x);//在root的左子树中查找if (leftResult != NULL)//如果左子树找到返回结果,直接返回return leftResult;return BinaryTreeFind(root->right, x);//如果左子树没有找到,继续在root的右子树
}

递归终止条件是找到节点或者子树为空,先在左子树查找,如果找到直接返回,如果左子树没有找到,再在右子树查找,采用深度优先搜索的递归方式遍历整棵二叉树,时间复杂度为O(N),最坏情况需要遍历整棵二叉树。利用递归实现二叉树的深度优先搜索。

当然你也可以查找–>

// 查找x所在的节点
BTNode* TreeFind(BTNode* root, int x)
{if (root == NULL)//根节点root为空,直接返回NULLreturn NULL;if (root->val == x)//root节点的值等于x,返回root节点return root;BTNode* ret1 = TreeFind(root->left, x);//在root的左子树中查找TreeFind(root->left, x)if (ret1)//如果左子树找到节点,直接返回return ret1;//如果左子树没有找到,在root的右子树中查找TreeFind(root->right, x)/BTNode* ret2 = TreeFind(root->right, x);if (ret2)return ret2;return NULL;如果左右子树均没有找到,返回NULL
}

用深度优先搜索的递归方式遍历二叉树先在左子树查找,找到则返回,否则查右子树,递归终止条件是找到节点或者子树为空,时间复杂度为O(N),最坏情况需要遍历整棵树。

🌉 完整代码实现

#include<stdio.h>
#include<stdlib.h>
#include<tree.h>
typedef char BTDataType;//二叉树节点结构
typedef struct BTNode
{BTDataType data;struct BTNode* left;struct BTNode* right;
}BTNode;//创建新节点
BTNode* BuyBTNode(int val)
{BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));if (newNode != NULL){newNode->data = val;newNode->left = NULL;newNode->right = NULL;}return newNode;
}//辅助函数:判断队列是否为空
int QueueIsEmpty(int front, int rear)
{return front == rear;
}//通过前序遍历的数组构建二叉树
BTNode* BinaryTreeCreateHelper(BTDataType* a, int n, int* pi)
{if (*pi >= n || a[*pi] == 'N'){(*pi)++;return NULL;}BTNode* newNode = BuyBTNode(a[(*pi)++]);if (newNode != NULL){newNode->left = BinaryTreeCreateHelper(a, n, pi);newNode->right = BinaryTreeCreateHelper(a, n, pi);}return newNode;}BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{return BinaryTreeCreateHelper(a, n, pi);
}//二叉树的销毁
void BinaryTreeDestory(BTNode** root)
{if (*root != NULL){BinaryTreeDestory(&((*root)->left));BinaryTreeDestory(&((*root)->right));free(*root);*root = NULL;}
}//判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{if (root == NULL)return 1; //空树是完全二叉树BTNode* queue[1000];//用于存放节点的队列int front = 0, rear = 0;int flag = 0;//用于标记是否遇到空节点//根节点入队queue[rear++] = root;while (!QueueIsEmpty(front, rear)){BTNode* current = queue[front++];//如果遇到空节点后面还有非空节点,说明不是完全二叉树if (current == NULL)flag = 1;else{//左右孩子入队queue[rear++] = current->left;queue[rear++] = current->right;//如果遇到空节点后面还有非空节点,说明不是完全二叉树if (flag)return 0;}}//遍历结束,说明是完全二叉树return 1;
}//二叉树节点个数
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);
}//二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->data == x)return root;BTNode* leftResult = BinaryTreeFind(root->left, x);if (leftResult != NULL)return leftResult;return BinaryTreeFind(root->right, x);
}//层序遍历
//void LevelOrder(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 ");
//		}
//	}
//	printf("\n");
//
//	QueueDestroy(&q);
//}void LevelOrder(BTNode* root) 
{if (root == NULL)return;BTNode* queue[1000]; int front = 0, rear = 0;queue[rear++] = root;while (front < rear) {BTNode* current = queue[front++];printf("%c ", current->data); if (current->left != NULL)queue[rear++] = current->left;if (current->right != NULL)queue[rear++] = current->right;}
}

🌉测试一下效果

int main()
{BTDataType preOrder[] = { '1','2','3','N','N','N','4','5','N','N','6','N','N' };int index = 0;BTNode* root = BinaryTreeCreate(preOrder, sizeof(preOrder) / sizeof(preOrder[0]), &index);printf("二叉树层序遍历结果为:");LevelOrder(root);printf("\n");printf("二叉树节点个数为:%d\n", BinaryTreeSize(root));printf("二叉树叶子节点个数为:%d\n", BinaryTreeSize(root));printf("二叉树第3层节点个数为:%d\n", BinaryTreeLevelKSize(root, 3));printf("二叉树是否是完全二叉树:%s\n", BinaryTreeComplete(root) ? "是" : "不是,换棵树看看~");BTNode* findNode = BinaryTreeFind(root, '3');if (findNode != NULL)printf("查找值为'3'的节点成功。\n");elseprintf("未找到该节点的值为3。\n");BinaryTreeDestory(&root);return 0;
}

代码前序构建图:
在这里插入图片描述
运行代码图
在这里插入图片描述


🚩总结

感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘,可以点点关注和赞哦 💓 💗 💕 💞
请添加图片描述

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

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

相关文章

Android: Gradle 命令

一、查看整个项目依赖传递关系 x.x.x (*) 该依赖已经有了&#xff0c;将不再重复依赖。x.x.x -> x.x.x 该依赖的版本被箭头所指的版本代替。x.x.x -> x.x.x(*) 该依赖的版本被箭头所指的版本代替&#xff0c;并且该依赖已经有了&#xff0c;不再重复依赖。 1. gradlew ap…

002 高并发内存池_定长内存池设计

​&#x1f308;个人主页&#xff1a;Fan_558 &#x1f525; 系列专栏&#xff1a;高并发内存池 &#x1f339;关注我&#x1f4aa;&#x1f3fb;带你学更多知识 文章目录 前言一、设计整体框架二、New操作&#xff08;申请空间&#xff09;三、Delete操作&#xff08;用自由链…

奇舞周刊第523期:来自 rust 生态的强烈冲击?谈谈 Leptos 在语法设计上的精妙之处...

奇舞推荐 ■ ■ ■ 来自 rust 生态的强烈冲击&#xff1f;谈谈 Leptos 在语法设计上的精妙之处 过去很长一段时间&#xff0c;前端框架们都在往响应式的方向发展。同时又由于 React hooks 的深远影响&#xff0c;函数式 响应式成为了不少前端心中最理想的前端框架模样。Solid …

设计模式-初步认识

目录 &#x1f6fb;1.什么是设计模式 &#x1f69a;2.设计模式的优点 &#x1f68d;3.设计模式6大原则 &#x1f6f4;4.设计模式类型 1.什么是设计模式 设计模式代表了最佳的实践&#xff0c;通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开…

Yocto学习笔记1-下载与首次编译

Yocto学习笔记1-下载与首次编译 1、基础环境介绍2、注意点3、安装依赖3.1 yocto常规系统构建所需依赖库&#xff08;较全&#xff09;3.2 龙芯适配时的最小依赖库&#xff08;最小&#xff09; 4、下载4.1 通过git克隆4.2 查看所有远程分支4.3 签出一个长期支持的稳定版本4.4 查…

从边缘设备丰富你的 Elasticsearch 文档

作者&#xff1a;David Pilato 我们在之前的文章中已经了解了如何丰富 Elasticsearch 本身和 Logstash 中的数据。 但如果我们可以从边缘设备中做到这一点呢&#xff1f; 这将减少 Elasticsearch 要做的工作。 让我们看看如何从具有代理处理器的 Elastic 代理中执行此操作。 E…

Redis如何删除大key

参考阿里云Redis规范 查找大key&#xff1a; redis-cli --bigkeys 1、String类型&#xff1a; Redis 4.0及以后版本提供了UNLINK命令&#xff0c;该命令与DEL命令类似&#xff0c;但它会在后台异步删除key&#xff0c;不会阻塞当前客户端&#xff0c;也不会阻塞Redis服务器的…

【漏洞复现】WordPress Plugin NotificationX 存在sql注入CVE-2024-1698

漏洞描述 WordPress和WordPress plugin都是WordPress基金会的产品。WordPress是一套使用PHP语言开发的博客平台。该平台支持在PHP和MySQL的服务器上架设个人博客网站。WordPress plugin是一个应用插件。 WordPress Plugin NotificationX 存在安全漏洞,该漏洞源于对用户提供的…

MySQL:表的操作

文章目录 创建表查看表结构修改表删除表 前面对于库的操作有了认识后&#xff0c;下面进行表的操作 创建表 以下图为例 创建表其实和定义结构体有点类似&#xff0c;总的来说就是先定义列名&#xff0c;然后后面跟着是列的数据类型&#xff0c;之后在定义结束后可以带上对应的…

【数据挖掘】实验4:数据探索

实验4&#xff1a;数据探索 一&#xff1a;实验目的与要求 1&#xff1a;熟悉和掌握数据探索&#xff0c;学习数据质量分类、数据特征分析和R语言的主要数据探索函数。 二&#xff1a;实验内容 1&#xff1a;数据质量分析 2&#xff1a;统计量分析 3&#xff1a;贡献度分析…

Redis常见数据类型(1)

Redis提供了5种数据结构, 理解每种数据类型的特点对于Redis开发运维非常重要, 同时掌握每种数据类型的常见命令, 会在使用Redis的时候做到游刃有余. 内容如下: 预备知识: 几个全局命令, 数据结构和内部编码, 单线程机制解析. 5种数据类型的特点, 命令使用, 应用场景示例. 键遍历…

uniapp微信小程序_购物车_下单页面

先说下整体逻辑以方便总体理解 1、首先画出下单页面 2、此次画出结算价格页面 3、怎么点击下完单变成结算页面&#xff1f;其实就是把下单页面的信息传递给结算页面就行 问题难点&#xff1f; 点击加号的时候把物品加入一个数组传到下单页面&#xff0c;但是点击的时候不能…

2024-03-24 需求分析-智能问答系统-调研

一. 需求列表 基于本地知识库的问答系统对接外围系统 数字人语音识别二. 待调研的公司 2.1 音视贝 AI智能外呼_大模型智能客服系统_大模型知识库系统_杭州音视贝 (yinshibei.com) 2.2 得助智能 智能AI客服机器人-智能电话机器人客服-电话电销机器人-得助智能 (51ima.com) 2…

LabVIEW比例流量阀自动测试系统

LabVIEW比例流量阀自动测试系统 开发了一套基于LabVIEW编程和PLC控制的比例流量阀自动测试系统。通过引入改进的FCMAC算法至测试回路的压力控制系统&#xff0c;有效提升了压力控制效果&#xff0c;展现了系统的设计理念和实现方法。 项目背景&#xff1a; 比例流量阀在液压…

使用 chezmoi vscode, 管理你的 dotfiles

什么是 dotfiles In Unix-like operating systems, any file or folder that starts with a dot character (for example, /home/user/.config), commonly called a dot file or dotfile. 任何以 . 开头去命名的文件或者目录都可以称为 dotfile, 在 Unix-like 系统一般用的比较…

Mysql数据库深入理解

目录 一、什么是数据库 二、Mysql基本架构图 1.Mysql客户端/服务器架构 2.客户端与服务器的连接过程 3.服务器处理客户端请求 4.一条查询SQL执行顺序 4.1连接器 4.2查询缓存 4.3解析器 4.4执行器 4.4.1预处理阶段 4.4.2优化阶段 4.4.3执行阶段 5.一条记录如何存…

外包干了4年,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;19年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

【十六】MySQL数据库设计篇

MySQL数据库设计篇 概述 做服务端开发离不开数据库设计&#xff0c;虽然说服务端技术一直在革新&#xff0c;但是MySQL一直都是我们首选使用的关系型数据库。服务端开发一直以来都是采用数据驱动研发的思想&#xff0c;可见数据库设计是非常重要的&#xff0c;数据库设计的好坏…

01.重新认识文件(Linux基本概念)

知识引入&#xff1a; 我们经常使用word或者wps写的论文、作业等都是文件&#xff0c;而我们这样对文件的认识也比较片面。我们是否思考过&#xff0c;如果文件里面没有写东西&#xff0c;那么计算机会不会保存呢&#xff1f;答案是&#xff0c;肯定会的。因为我们每次建的空文…

Linux下对线程的认识+生产消费者模型+信号量

线程的概念 线程是进程内部中更加轻量化的一种执行流。线程是CPU调度的基本单位&#xff0c;而进程是承担系统资源的实体。就是说一个进程中可能会有多个线程&#xff0c;而在Linux内核中并没有真正重新的创建线程并重新进行资源分配&#xff0c;因为我们每个线程指向的资源都是…