【数据结构】顺序表和链表(上)(附leetcode练习题)

news2024/9/30 7:30:31

☃️个人主页:fighting小泽
🌸作者简介:目前正在学习C语言和数据结构
🌼博客专栏:数据结构
🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪🏻

文章目录

  • 1.线性表
  • 2.顺序表
    • 2.1 概念及结构
    • 2.2 静态顺序表
    • 2.3 动态顺序表
    • 2.4 动态顺序表的实现
      • 2.41顺序表的初始化和销毁
      • 2.42检查顺序表的容量
      • 2.43顺序表的尾插和头插
      • 2.44打印顺序表的元素
      • 2.45顺序表的尾删和头删
      • 2.46顺序表的随机插入和删除
      • 2.47顺序表的修改
  • 3.整体代码
    • 3.1SeqList.h代码
    • 3.2SeqList.c代码
  • 4.leetcode练习题
  • 5.结尾

1.线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

为什么要这样存储呢?
因为有很多的列表需要依次展示我们的数据,比如微信联系人,成员列表。
那展示了以后还存在什么问题呢

增删查改
但是存储这些数据又有一些很不一样的东西,它是什么呢?我们做个简单的比方: 线性表中最简单的存储方式是线性表,大家知不知道顺序表的本质是什么?顺序表的本质非常简单是一个数组,把数据存放到数组里
大家知道数组的缺陷是什么吗?假设我们想要删除数组的某个元素,我们能不能把这块空间释放了?我们只能挪动数据覆盖。所以有一个非常好的结构叫链表,链表是一块一块的小空间,它们之间地址上没有关联,所以想要找到下一个空间的地址就得用一个指针,上一个空间里存放一个指向下一个空间的指针,这样就把它们链接起来了。当我们想删除某个元素的时候该怎么办?这个时候就简单了,我们把这个空间释放掉,上一个空间的指针指向下一个空间就可以了,不需要挪动数据

所以不同的结构有不同的特点,就像公交车可以拉很多人,小轿车坐着很方便一样
那既然顺序表,数组如此的不方便,我们还为什么要学习呢?
数组有一个它绝对的优势,是下标的随机访问,非常方便。比如二分查找能不能在链表上玩?不可以。包括后面我们学习排序,也是需要大量使用下标访问,因为它们的物理空间连续。

在这里插入图片描述

2.顺序表

2.1 概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为:

  1. 静态顺序表:使用定长数组存储元素。
    在这里插入图片描述
  2. 动态顺序表:使用动态开辟的数组存储。
    在这里插入图片描述

2.2 静态顺序表

一般情况下我们想创建一个静态顺序表只需要一个数组还不行,还得告诉我们数组中有多少数据是有效数据,所以我们需要创建一个结构体,定义一个数组和它有效数据的个数

struct SeqList
{
	int arr[10];
	int size;
};

但是当前这种定义方式在实践中是不太好的,第一是因为它只能存放有限个数据,第二是它只能存放整形变量,所以我们可以对它进行改进:

用一个#define定义的宏来表示它存放的个数,用typedef对它的类型重命名。如果我们想进行修改,只需要改变#define定义的数值,或者改变typedef的类型。
由于结构体的名字太长了,我们也可以把它改成一个较短的名字。

#define N 10
typedef int SLDatatype;

typedef struct SeqList
{
	SLDatatype arr[N];
	int sz;
}SL;

这样就定义了一个静态的顺序表了,大家想想它有没有什么缺点?

做个简单的比方:如果我们定义了数组的大小是10,我们想存11个数据就存不下了,它的空间是固定的。假如我们给了10000个空间,我们想存10001个数据还是不够用。就算大多数情况够用了,但是我就存5个或者100个呢,空间是不是就浪费了。所以静态顺序表有一个特点,给小了不够用,给多了浪费。

所以一般情况下不推荐大家写静态的顺序表,推荐大家写动态的顺序表。

2.3 动态顺序表

静态顺序表只适用于确定知道需要存多少数据的场景,所以现实中基本都是使用动态顺序表,动态顺序表的实现其实跟我们的通讯录的实现差不多,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

动态的顺序表就是我们去malloc它的空间,空间满了后我们可以用realloc扩容,不够了扩就可以了,所以我们还要创建一个变量定义它的容量大小

typedef int SLDataType;
// 顺序表的动态存储
typedef struct SeqList
{
  SLDataType* array;  // 指向动态开辟的数组
  size_t size ;       // 有效数据个数
  size_t capicity ;   // 容量空间的大小
}SeqList;

然后就是动态顺序表的初始化和增删查改了:

// 基本增删查改接口
// 顺序表初始化
void SeqListInit(SeqList* psl);
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl);
// 顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
// 顺序表尾删
void SeqListPopBack(SeqList* psl);
// 顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);
// 顺序表头删
void SeqListPopFront(SeqList* psl);
// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x); 
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
// 顺序表销毁
void SeqListDestory(SeqList* psl);
// 顺序表打印
void SeqListPrint(SeqList* psl);

2.4 动态顺序表的实现

我们同样需要创建三个文件夹来实现动态顺序表,SeqList.h头文件定义函数,SeqList.c文件对函数进行实现,test.c文件实现动态顺序表。

2.41顺序表的初始化和销毁

首先就是初始化我们的顺序表的初始化:
注意:由于我们要改变顺序表,所以我们要传顺序表的地址过去。
我们对它进行初始化只需要把每个变量置为空即可。

void SeqListInit(SL* psl)
{
	psl->a = NULL;
	psl->sz = 0;
	psl->capicity = 0;
}

上面只是一种方法,其实还有一种更好的方法,我们可以先开辟一点空间,不一定开特别大,开小一点就行,当空间不够了我们再重新开辟空间

void SeqListInit(SL* psl)
{
	assert(psl);
	SLDatatype* tmp = (SLDatatype*)malloc(N * sizeof(SLDatatype));
	if (tmp == NULL)
	{
		perror("SL_malloc");
		return;
	}
	psl->a = tmp;
	psl->sz = 0;
	psl->capicity = N;
}

由于顺序表是我们动态开辟的空间,所以结束时我们要把它释放掉,否则会出现内存泄漏。同时还有可能访问到这块空间,所以我们要把指向它的指针置空。

void SLDestroy(SL* psl)
{
	assert(psl);
	free(psl->a);
	psl->a = NULL;
	psl->sz = 0;
	psl->capicity = 0;
}

2.42检查顺序表的容量

当我们对顺序表增加元素的时候要确保顺序表的容量是足够大的,如果不够,我们可以把它的容量扩大两倍。而判断顺序表容量满的方式就是看它的有效变量size和容量capicity是否相等

void CheckCapicity(SL* psl)
{
	assert(psl);
	if (psl->capicity == psl->sz)
	{
		SLDatatype* tmp = (SLDatatype*)realloc(psl->a, 2 * psl->capicity * sizeof(SLDatatype));
		if (tmp == NULL)
		{
			perror("SL_realloc");
			return;
		}
		psl->a = tmp;
		psl->capicity *= 2;
	}
}

2.43顺序表的尾插和头插

我们想在尾部插入一个元素特别特别简单,从下标的角度数据个数就是最后一个元素的下标加1,我们只需要在size的位置放数据,然后再size++ 即可,注意判断顺序表的容量

void SLPushBack(SL* psl,SLDatatype x)
{
	assert(psl);
	CheckCapicity(psl);
	psl->a[psl->sz] = x;
	psl->sz++;
}

而我们想进行头部插入,只能从后向前每一个元素向后移动一位,在头部存放我们想存放的数据,然后size++即可。

void SLPushFront(SL* psl, SLDatatype x)
{
	assert(psl);
	CheckCapicity(psl);
	for (int i = psl->sz; i > 0; i--)
	{
		psl->a[i] = psl->a[i - 1];
	}
	psl->a[0] = x;
	psl->sz++;
}

2.44打印顺序表的元素

打印其实也非常简单,因为顺序表本质上是一个数组,打印数组的每一个元素即可,我们也可以打印出它的size和capicity。

void SLPrint(SL* psl)
{
	assert(psl);
	for (int i = 0; i < psl->sz; i++)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\nsz=%d\n", psl->sz);
	printf("capicity=%d\n", psl->capicity);
}

现在让我们来测试刚刚的头插和尾插
在这里插入图片描述

2.45顺序表的尾删和头删

我们想进行顺序表的尾删该怎样做呢?把最后一位赋值为0,赋值为-1?要是它本身就是0或-1该怎么办?其实有一种简单的方法,我们直接把size 减1就可以了,之后又新元素再把它覆盖就行了

void SLPopBack(SL* psl)
{
	assert(psl->sz > 0);
	psl->sz -= 1;
}

我们想进行头删时,也是把每一个元素向前移动一位,然后size减1即可

void SLPopFront(SL* psl)
{
	assert(psl->sz > 0);
	for (int i = 0; i < psl->sz - 1; i++)
	{
		psl->a[i] = psl->a[i + 1];
	}
	psl->sz--;
}

测试一下:

在这里插入图片描述

2.46顺序表的随机插入和删除

我们想要在任意位置插入元素,只需要将此位置和之后的元素都向后移动一位,然后在这个位置插入元素,然后size加1即可。注意pos的值要在范围之内。

void SLInsert(SL* psl, int pos, SLDatatype x)
{
	assert(psl);
	assert(pos > 0 && pos <= psl->sz);
	CheckCapicity(psl);
	for (int i = psl->sz; i > pos-1; i--)
	{
		psl->a[i] = psl->a[i-1];
	}
	psl->a[pos-1] = x;
	psl->sz++;
}

删除也是将这个位置后的元素都向前移动一位,然后size减1。注意pos的值要在范围之内。

void SLErase(SL* psl, int pos)
{
	assert(psl);
	assert(pos > 0 && pos < psl->sz);
	CheckCapicity(psl);
	for (int i = pos - 1; i < psl->sz-1; i++)
	{
		psl->a[i] = psl->a[i + 1];
	}
	psl->sz--;
}

我们前面的头插头删,尾插尾删也可以使用这两个函数,只需要改成特定的位置就行。
在这里插入图片描述

2.47顺序表的修改

修改也非常简单,只需要将这个位置的元素修改即可,不够要注意位置的范围要合理

void SLModify(SL* psl, int pos, SLDatatype x)
{
	assert(psl);
	assert(pos > 0 && pos < psl->sz);
	psl->a[pos - 1] = x;
}

在这里插入图片描述

3.整体代码

3.1SeqList.h代码

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

#define N 4
typedef int SLDatatype;

//typedef struct SeqList
//{
//	SLDatatype arr[N];
//	int sz;
//}SL;

//struct SeqList
//{
//	int arr[10];
//	int sz;
//};
typedef struct SeqList
{
	SLDatatype* a;
	int sz;
	int capicity;
}SL;

void SeqListInit(SL* psl);

void CheckCapicity(SL* psl);

void SLPushBack(SL* psl,SLDatatype x);//尾插

void SLPopBack(SL* psl);//尾删

void SLPrint(SL* psl);

void SLDestroy(SL* psl);

void SLPushFront(SL* psl, SLDatatype x);//头插

void SLPopFront(SL* psl);//头删

int SLFind(SL* psl, SLDatatype x);

void SLInsert(SL* psl, int pos, SLDatatype x);

void SLErase(SL* psl, int pos);

void SLModify(SL* psl, int pos, SLDatatype x);

3.2SeqList.c代码

#include"SeqList.h"

void SeqListInit(SL* psl)
{
	assert(psl);
	SLDatatype* tmp = (SLDatatype*)malloc(N * sizeof(SLDatatype));
	if (tmp == NULL)
	{
		perror("SL_malloc");
		return;
	}
	psl->a = tmp;
	psl->sz = 0;
	psl->capicity = N;
}

void CheckCapicity(SL* psl)
{
	assert(psl);
	if (psl->capicity == psl->sz)
	{
		SLDatatype* tmp = (SLDatatype*)realloc(psl->a, 2 * psl->capicity * sizeof(SLDatatype));
		if (tmp == NULL)
		{
			perror("SL_realloc");
			return;
		}
		psl->a = tmp;
		psl->capicity *= 2;
	}

}

void SLPushBack(SL* psl,SLDatatype x)
{
	assert(psl);
	CheckCapicity(psl);
	psl->a[psl->sz] = x;
	psl->sz++;
}

void SLPopBack(SL* psl)
{
	assert(psl->sz > 0);
	psl->sz -= 1;
}

void SLPrint(SL* psl)
{
	assert(psl);
	for (int i = 0; i < psl->sz; i++)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\nsz=%d\n", psl->sz);
	printf("capicity=%d\n", psl->capicity);
}

void SLDestroy(SL* psl)
{
	assert(psl);
	free(psl->a);
	psl->a = NULL;
	psl->sz = 0;
	psl->capicity = 0;
}

void SLPushFront(SL* psl, SLDatatype x)
{
	assert(psl);
	CheckCapicity(psl);
	for (int i = psl->sz; i > 0; i--)
	{
		psl->a[i] = psl->a[i - 1];
	}
	psl->a[0] = x;
	psl->sz++;
}

void SLPopFront(SL* psl)
{
	assert(psl->sz > 0);
	for (int i = 0; i < psl->sz - 1; i++)
	{
		psl->a[i] = psl->a[i + 1];
	}
	psl->sz--;
}

int SLFind(SL* psl, SLDatatype x)
{
	assert(psl);
	for (int i = 0; i < psl->sz; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

void SLInsert(SL* psl, int pos, SLDatatype x)
{
	assert(psl);
	assert(pos > 0 && pos <= psl->sz);
	CheckCapicity(psl);
	for (int i = psl->sz; i > pos-1; i--)
	{
		psl->a[i] = psl->a[i-1];
	}
	psl->a[pos-1] = x;
	psl->sz++;
}

void SLErase(SL* psl, int pos)
{
	assert(psl);
	assert(pos > 0 && pos < psl->sz);
	CheckCapicity(psl);
	for (int i = pos - 1; i < psl->sz-1; i++)
	{
		psl->a[i] = psl->a[i + 1];
	}
	psl->sz--;
}

void SLModify(SL* psl, int pos, SLDatatype x)
{
	assert(psl);
	assert(pos > 0 && pos < psl->sz);
	psl->a[pos - 1] = x;

}

4.leetcode练习题

力扣26,删除有序数组中的重复项
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

我们可以通过利用双指针的方法来解决这道题,我们定义两个指针同时指向第二个元素,快指针向前遍历,如果两个指针指向的内容相同,快指针加1,慢指针不变。如果两个指针指向的内容不同,就将快指针的值赋值给慢指针,两个指针同时加1,最后返回慢指针的个数就可以了。

int removeDuplicates(int* nums, int numsSize) {
    if (numsSize == 0) {
        return 0;
    }
    int fast = 1, slow = 1;
    while (fast < numsSize) {
        if (nums[fast] != nums[fast - 1]) {
            nums[slow] = nums[fast];
            ++slow;
        }
        ++fast;
    }
    return slow;
}

5.结尾

这些就是我给大家分享的关于顺序表的知识啦,希望我们都能有所收获!
先赞后看,养成习惯!!^ _ ^
码字不易,大家的支持就是我坚持下去的动力,点赞后不要忘了关注我哦!

如有错误,还请您批评改正(。ì _ í。)

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

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

相关文章

春秋云境:CVE-2022-30887(文件上传漏洞)

目录 一.题目 二.蚁剑方式 三.POC方式 一.题目 该CMS中php_action/editProductImage.php存在任意文件上传漏洞&#xff0c;进而导致任意代码执行。 进入页面&#xff1a;登录页面 随意输入用户名和密码&#xff1a;admingmail.com admin 用于burp抓包&#xff1a; burp抓包…

基于PCL抓取Velodyne激光雷达数据包可视化

Velodyne 高清激光雷达 (HDL) 采集器 Velodyne HDL 是一种基于网络的 3D LiDAR 系统&#xff0c;每秒生成包含超过 700,000 个点的 360 度点云。 PCL 中提供的 HDL Grabber 模仿其他 Grabbers&#xff0c;使其几乎可以即插即用。 然而&#xff0c;由于 HDL 设备是基于网络的&…

线性表总结

线性表 目录&#xff1a; 文章目录 线性表概念一、线性表的定义二、线性表的基本操作三、线性表的实现方式四、线性表的应用总结 概念 线性表是一种基本的数据结构&#xff0c;由一系列具有相同类型的数据元素构成&#xff0c;这些元素之间具有线性的顺序关系&#xff0c;每个…

python+vue+django旅游景点酒店线路套餐管理系统

1.查询旅游信息:主要查询景点和酒店的信息&#xff0c;可以进行分类查询也可以用关键在字来查询用户需要的信息。 2.留言功能:用户可以查看别人以前的留言&#xff0c;也可以发表新的留言。. 3.酒店预定:当用户查询到相关酒店&#xff0c;并且可以能够申请相关酒店预定。 4.信息…

【人工智能】— 一阶逻辑、量词的推理规则、一般化分离规则、合一、前向/反向链接算法、归结算法

这里写自定义目录标题 量词的推理规则全称量词实例化存在量词实例化 简化到命题逻辑推理Generalized Modus Ponens&#xff08;一般化分离规则&#xff09;举例 合一Forward chaining 前向链接算法示例 Backward chaining algorithm 反向链接算法一般FOL的FC/BC的完整性 归结算…

系统分析师选择题笔记

目录 1、知识产权与标准化 1.1 保护范围与对象(★★★★) 1.2 保护期限(★) 1.3 知识产权人确定(★★★) 1.4 侵权判断(★★★) 1.5 标准的分类(★) 1.6 标准代号的识别(★) 2、系统配置与性能评价 2.1 系统性能概述 2.2 系统性能&#xff08;性能指标&#xff09;(★…

8款数据迁移工具选型,主流且实用

前言&#xff1a;ETL(是Extract-Transform-Load的缩写&#xff0c;即数据抽取、转换、装载的过程)&#xff0c;对于企业应用来说&#xff0c;我们经常会遇到各种数据的处理、转换、迁移的场景。今天特地给大家汇总了一些目前市面上比较常用的ETL数据迁移工具&#xff0c;希望对…

什么是B/S架构?

目录 一.什么是B/S架构&#xff1f; 二.B/S架构与C/S架构有什么区别&#xff1f; 三.B/S架构的优缺点 四.B/S架构的几种形式 1&#xff1a;客户端-服务器-数据库 2&#xff1a;客户端&#xff0d;web服务器&#xff0d;应用服务器&#xff0d;数据库 3&#xff1a;客户端…

4个方法助你从戴尔笔记本电脑D盘中恢复丢失的文件

用户案例&#xff1a; 案例1&#xff1a;我有一台戴尔笔记本电脑&#xff0c;D盘上存储了很多个人照片和视频&#xff0c;但最近不知道什么原因&#xff0c;这些数据突然全部消失了。因为这些数据是我珍贵的回忆。所以想问问大家戴尔电脑丢失d盘文件怎么恢复&#xff1f; 案例2…

网络中的网关和物联网的网关区别 局域网 路由器 交换机 服务器

网关&#xff1a;是个概念。连接两种不同的网络。例如局域网要与外部通信&#xff0c;需要经过网关。 设备和设备之间的通信&#xff0c;转换协议需要网关 路由器里有功能是对网关这个概念的实现。 所以网关它可以是路由器&#xff0c;交换机或者是PC。 路由器有网关功能&a…

Flink-Scala版学习——转换算子

目录 一、基本转换算子 1.map 2.filter 3.flatMap 3.聚合算子Aggregation (1)keyBy (2)简单聚合&#xff1a;sum、min、minBy、max、maxBy (3)归约聚合&#xff1a;reduce 二、UDF 三、富函数类 四、物理分区 1.随机分区(shuffle) 2. 轮询分区&#xff08;Round-R…

4月20日第壹简报,星期四,农历三月初一,谷雨

4月20日第壹简报&#xff0c;星期四&#xff0c;农历三月初一&#xff0c;谷雨坚持阅读&#xff0c;静待花开1. 已致29人死亡&#xff0c;26人为患者&#xff01;北京长峰医院火灾事故因院内施工作业火花引发&#xff0c;院长王某玲等12人被刑拘。2. 海南发布旅游产品参考价格&…

第二十三章 案例TodoList之数据更新

本小节&#xff0c;我们要实现点击复选框&#xff0c;修改任务项的实时状态&#xff0c;但是Item组件和App组件是祖孙关系&#xff0c;不是父子关系&#xff0c;我们还能使用props进行通信吗&#xff1f;答案是可以的。 在App组件定义一个更新的函数并传递给Item组件 1、定义…

车载网络 - Autosar网络管理 - 网络管理报文

三、网络管理报文 NM报文的ID一般定义为&#xff1a;基础ID源地址&#xff0c;每个节点应分配一个唯一的标识符&#xff08;ECU地址&#xff09;Node_ID&#xff0c;网络管理报文一般会统一一个基地址&#xff0c;这个是根据主机厂不同而不同&#xff0c;有些是用0x400 - 0x4FF…

多通道振弦传感器无线采集仪如何外接数字传感器

多通道振弦传感器无线采集仪如何外接数字传感器 数字传感器的数据接入逻辑 VS 设备支持在 RS485 接口外接数字传感器&#xff0c; 可进行单类型、多类型数字传感器接入。 单类型数字传感器&#xff1a;使用寄存器 DS_SENSOR(282)来设置单类型数字传感器的类型和数量&#xff08…

用Flutter开发一款企业级App(开眼Flutter-OpenEye)

先贴项目地址&#xff1a;WinWang/open_eye: Flutter 开眼APP&#xff1a;整体项目架构基于Getx搭建&#xff0c;完成路由&#xff0c;依赖注入&#xff1b;网络请求框架基于RetrofitDio实现&#xff0c;配合官方JsonSerialize实现解析&#xff1b;封装项目页面多状态&#xff…

新媒体运营团队如何协同工作?

随着媒体形式的不断丰富和演变&#xff0c;社交、短视频等软件的盛行&#xff0c;新媒体运营成为近几年新兴的行业。 在新媒体团队工作过程中可能会遇到以下问题&#xff1a; 1&#xff09;热点不断更迭&#xff0c;文档资料需要快速流通&#xff1b; 2&#xff09;通过传统的…

US-DAPQ比例放大器指令接线

序号 端子名称 功能 1 CMD_P 压力阀指令 2 CMD_P- 压力阀指令- 3 CMD_Q 流量阀指令 4 CMD_Q- 流量阀指令- 5/6 N.C. 不接 7 VREF_5V 参考电压5V 8 VREF_0V 参考电压0V 9 SOL_P 压力阀线圈 10 SOL_P- 压力阀线圈 11 PWR 电源 12 PWR- 电源- 13 SOL_Q- 流量阀线圈 1…

mysql知识

1.执行步骤 2.Write-Ahead Logging 技术 这两种日志有以下三点不同。 ①redo log 是 InnoDB 引擎特有的&#xff1b;binlog 是 MySQL 的 Server 层实现的&#xff0c;所有引擎都可以使用。 ②redo log 是物理日志&#xff0c;记录的是“在某个数据页上做了什么修改”&#x…

【Linux】项目自动化构建工具 —— make/Makefile

前言&#xff1a; 在上一期的博文中&#xff0c;我们对 Linux 下的编译器 - gcc/g的使用进行了详细的讲解&#xff0c;今天我将给大家讲解的是关于 【Linux】项目自动化构建工具 —— make/Makefile 的详细使用教程&#xff01;&#xff01; 本文目录 &#xff08;一&#x…