当前位置: 首页 > article >正文

数据结构 --- 顺序表

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

顺序表分为:静态顺序表、动态顺序表

一.静态顺序表

#define N 7
typedef int SLDataType;typedef struct Seqlist
{SLDataType array[N];  //定长数组size_t size;          //有效元素个数
}Seqlist;

静态顺序表突出的特点是:少了不够、多了浪费。

二.动态顺序表

typedef int SLDataType;typedef struct Seqlist
{SLDataType* array; //指针指向动态开辟的数组size_t size;       //有效数据个数size_t capacity;   //容量空间大小
}Seqlist;

动态顺序表突出的特点是:按需申请。

三.实现增删查改

基于顺序表的核心思想,参考之前的通讯录程序,我们来用顺序表实现增删查改。

1.准备工作:

欲实现增删查改,首先我们需要分装函数:

test.cSeqList.cSeqList.h
测试模块函数模块声明模块

 2.声明模块(SeqList.h):

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLDataType;
#define INIT_CAPACITY 4typedef struct SeqList
{SLDataType* a;int size;int capacity;
}SL;void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
void SLCheckCapacity(SL* ps);
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);

这段代码定义了一个动态顺序表(动态数组)的接口:

一、结构定义解析

typedef int SLDataType;
#define INIT_CAPACITY 4typedef struct SeqList
{SLDataType* a;      // 动态数组指针int size;           // 当前元素个数int capacity;       // 当前容量
} SL;
关键成员
  1. a
    • 指向动态分配的数组,存储顺序表元素。
  2. size
    • 当前有效元素个数(范围:0 ≤ size ≤ capacity)。
  3. capacity
    • 数组当前的最大容量,扩容时会动态调整。

二、接口功能分析

1. 生命周期管理
  • SLInit(SL* ps):初始化顺序表。
  • SLDestroy(SL* ps):释放内存,销毁顺序表。
2. 元素操作
  • 尾部操作
    SLPushBack(尾插)、SLPopBack(尾删)。
  • 头部操作
    SLPushFront(头插)、SLPopFront(头删)。
  • 任意位置操作
    SLInsert(指定位置插入)、SLErase(指定位置删除)。
3. 辅助功能
  • SLPrint:打印顺序表元素。
  • SLCheckCapacity:检查容量并在需要时扩容。
  • SLFind:查找元素并返回位置。

三、性能分析

操作时间复杂度说明
尾插 PushBackO(1)均摊复杂度(扩容时 O (n))
头插 PushFrontO(n)需要移动所有元素
指定位置插入 SLInsertO(n)平均移动 n/2 个元素
尾删 PopBackO(1)直接减少 size,无需移动元素
头删 PopFrontO(n)需要移动所有元素
指定位置删除 SLEraseO(n)平均移动 n/2 个元素
查找 FindO(n)需遍历数组

3.函数模块(SeqList.c): 

确定了具体函数以后,接下来的任务就是如何将这些函数在.c文件中逐个实现:

<1>.初始化函数:

void SLInit(SL* ps)
{assert(ps);ps->a = (SLDataType*)malloc(sizeof(SLDataType)* INIT_CAPACITY);if (ps->a == NULL){perror("malloc fail");return;}ps->size = 0;ps->capacity = INIT_CAPACITY;
}
一、功能概述

SLInit 函数的核心目的是:

  1. 为顺序表分配初始内存空间(容量为 INIT_CAPACITY,通常为 4)。
  2. 初始化顺序表的状态(size 和 capacity)。
  3. 处理内存分配失败的情况。
二、代码逻辑拆解
void SLInit(SL* ps)
{assert(ps);  // 检查指针有效性// 分配初始内存ps->a = (SLDataType*)malloc(sizeof(SLDataType)* INIT_CAPACITY);// 处理内存分配失败if (ps->a == NULL){perror("malloc fail");return;}// 初始化状态ps->size = 0;ps->capacity = INIT_CAPACITY;
}
三、关键技术细节
① 指针有效性检查
assert(ps);
  • 使用 assert 确保传入的指针非空。
  • 注意assert 在调试模式下生效,发布版本可能被忽略。建议结合运行时检查(如 if (ps == NULL))增强健壮性。
② 内存分配
ps->a = (SLDataType*)malloc(sizeof(SLDataType)* INIT_CAPACITY);
  • 分配 INIT_CAPACITY 个元素的空间。
  • 强制类型转换:在 C 语言中,malloc 返回 void*,可隐式转换为其他指针类型,因此 (SLDataType*) 可省略。但在 C++ 中必须显式转换。
③ 错误处理
if (ps->a == NULL)
{perror("malloc fail");return;
}
  • 当内存分配失败时:
    1. 使用 perror 输出错误信息(包含系统错误描述)。
    2. 函数直接返回,此时 ps->a 为 NULL,后续操作需检查指针有效性。
  • 潜在风险:调用者可能未检查 a 是否为 NULL,导致后续操作崩溃。
④ 状态初始化
ps->size = 0;
ps->capacity = INIT_CAPACITY;
  • size 初始化为 0,表示顺序表为空。
  • capacity 记录当前分配的空间大小。

<2>.尾插函数: 

void SLPushBack(SL* ps, SLDataType x)
{assert(ps);//扩容:if (ps->size == ps->capacity)
{SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity * 2);if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp; ps->capacity *= 2;
}ps->a[ps->size++] = x;
}
一、功能概述

SLPushBack 函数的核心目的是:

  1. 将元素 x 添加到顺序表的尾部。
  2. 若顺序表已满(size == capacity),则自动扩容(容量翻倍)。
  3. 处理内存分配失败的情况。
二、代码逻辑拆解
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);  // 检查指针有效性// 扩容逻辑if (ps->size == ps->capacity){SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity * 2);if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp; ps->capacity *= 2;}// 插入元素ps->a[ps->size++] = x;
}
三、关键技术细节
① 指针有效性检查
assert(ps);
  • 使用 assert 确保传入的指针非空。
  • 注意assert 在调试模式下生效,发布版本可能被忽略。建议结合运行时检查(如 if (ps == NULL))增强健壮性。
② 动态扩容机制
if (ps->size == ps->capacity)
{SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity * 2);// ...
}
  • 扩容条件:当 size 等于 capacity 时触发。
  • 扩容策略:容量翻倍(ps->capacity * 2),均摊时间复杂度为 O (1)。
  • 安全使用 realloc
    1. 使用临时指针 tmp 接收 realloc 的返回值,避免内存泄漏(若 realloc 失败,原指针 ps->a 保持不变)。
    2. 强制类型转换:在 C 语言中可省略,但 C++ 中必须显式转换。
③ 错误处理
if (tmp == NULL)
{perror("realloc fail");return;
}
  • 当内存分配失败时:
    1. 使用 perror 输出错误信息。
    2. 函数直接返回,此时原指针 ps->a 未被修改,但插入操作失败。
  • 潜在风险:调用者可能未检查操作是否成功,误认为元素已插入。
④ 元素插入
ps->a[ps->size++] = x;
  • 将元素 x 放入当前 size 位置,然后 size 自增。
  • 等价于
    ps->a[ps->size] = x;
    ps->size++;
    

<3>.头插函数: 

void SLPushFront(SL* ps, SLDataType x)
{assert(ps);if (ps->size == ps->capacity)
{SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity * 2);if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp; ps->capacity *= 2;
}int end = ps->size - 1;while (end >= 0){ps->a[end + 1] = ps->a[end];--end;}ps->a[0] = x;ps->size++;}
一、功能概述

SLPushFront 函数的核心目的是:

  1. 将元素 x 添加到顺序表的头部。
  2. 若顺序表已满(size == capacity),则自动扩容(容量翻倍)。
  3. 通过元素后移实现头部插入。
二、代码逻辑拆解
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);  // 检查指针有效性// 扩容逻辑(同SLPushBack)if (ps->size == ps->capacity){SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity * 2);if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp; ps->capacity *= 2;}// 元素后移int end = ps->size - 1;while (end >= 0){ps->a[end + 1] = ps->a[end];--end;}// 插入元素ps->a[0] = x;ps->size++;
}
三、关键技术细节
① 扩容机制

与 SLPushBack 相同,当容量不足时进行翻倍扩容,确保有空间插入新元素。

② 元素后移
int end = ps->size - 1;
while (end >= 0)
{ps->a[end + 1] = ps->a[end];--end;
}
  • 移动方向:从最后一个元素开始,依次向后移动一位,直到所有元素后移完毕。
  • 时间复杂度:O (n),需移动全部 size 个元素。
③ 插入操作
ps->a[0] = x;
ps->size++;
  • 将新元素放入数组头部(索引 0),并更新 size

<4>.扩容函数:

观察发现,插入函数需要扩容,为减轻冗长代码,我们可以将扩容代码另外分装:

void SLCheckCapacity(SL* ps)
{assert(ps);//扩容:if (ps->size == ps->capacity){SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity * 2);if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp; ps->capacity *= 2;}
}

<5>.尾删函数:

void SLPopBack(SL* ps)
{assert(ps);  // 检查指针有效性// 处理空表情况if (ps->size == 0)return;// 逻辑删除:减小size,无需清除元素ps->size--;
}

尾删函数较为简单,不加以赘述......

<6>.头删函数:

void SLPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);int begin = 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}ps->size--;
}
一、功能概述

SLPopFront 函数的核心目的是:

  1. 删除顺序表的第一个元素。
  2. 通过元素前移覆盖原头部元素。
  3. 减小 size 以标记有效元素数量减少。
二、代码逻辑拆解
void SLPopFront(SL* ps)
{assert(ps);  // 检查指针有效性assert(ps->size > 0);  // 确保表非空// 元素前移覆盖头部int begin = 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}// 减小sizeps->size--;
}
三、关键技术细节
① 断言检查
assert(ps);
assert(ps->size > 0);
  • 确保传入指针非空且表非空。
  • 注意assert 仅在调试模式生效,发布版本可能忽略,需依赖调用者检查。
② 元素前移
int begin = 1;
while (begin < ps->size)
{ps->a[begin - 1] = ps->a[begin];++begin;
}
  • 移动方向:从第二个元素(索引 1)开始,依次向前覆盖前一个元素。
  • 时间复杂度:O (n),需移动全部 size-1 个元素。
③ 逻辑删除
ps->size--;
  • 通过减小 size 标记有效元素减少,无需物理清除最后一个元素。

<7>.指定位置插入函数:

void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);int end = ps->size - 1;while (end >= pos){ps->a[end + 1] = ps->a[end];--end;}ps->a[pos] = x;ps->size++;
}
一、功能概述

SLInsert 函数的核心目的是:

  1. 将元素 x 插入到顺序表的指定位置 pos
  2. 若顺序表已满,自动扩容。
  3. 通过元素后移为新元素腾出空间。
二、代码逻辑拆解
void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);  // 检查指针有效性assert(pos >= 0 && pos <= ps->size);  // 确保pos合法SLCheckCapacity(ps);  // 检查并扩容(若需要)// 元素后移int end = ps->size - 1;while (end >= pos){ps->a[end + 1] = ps->a[end];--end;}// 插入元素ps->a[pos] = x;ps->size++;
}
三、关键技术细节
① 合法性检查
assert(pos >= 0 && pos <= ps->size);
  • 合法范围0 ≤ pos ≤ size
    • pos == size 时,等价于尾部插入。
    • 若 pos > size,会导致元素间出现空洞(如 [1, 2, pos=5插入3] → [1, 2, ?, ?, ?, 3])。
② 动态扩容
SLCheckCapacity(ps);
  • 调用扩容函数,确保有足够空间插入新元素。
  • 扩容策略:通常为容量翻倍(如从 4→8→16),均摊时间复杂度 O (1)。
③ 元素后移
int end = ps->size - 1;
while (end >= pos)
{ps->a[end + 1] = ps->a[end];--end;
}
  • 移动方向:从最后一个元素开始,逐个后移直到 pos 位置。
  • 时间复杂度:O (n)(最坏情况:插入头部需移动所有元素)。
④ 插入操作
ps->a[pos] = x;
ps->size++;
  • 将元素放入指定位置 pos,更新 size

<8>.指定位置删除函数:

void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);int begin = pos + 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}ps->size--;
}
一、功能概述

SLErase 函数的核心目的是:

  1. 删除顺序表中指定位置 pos 的元素。
  2. 通过元素前移覆盖被删除元素。
  3. 减小 size 以标记有效元素数量减少。
二、代码逻辑拆解
void SLErase(SL* ps, int pos)
{assert(ps);  // 检查指针有效性assert(pos >= 0 && pos < ps->size);  // 确保pos合法// 元素前移覆盖int begin = pos + 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}// 减小sizeps->size--;
}
三、关键技术细节
① 合法性检查
assert(pos >= 0 && pos < ps->size);
  • 合法范围0 ≤ pos < size
    • 若 pos == size,会导致访问越界(如 size=3 时,pos=3 访问 a[4])。
② 元素前移
int begin = pos + 1;
while (begin < ps->size)
{ps->a[begin - 1] = ps->a[begin];++begin;
}
  • 移动方向:从 pos 的下一个位置开始,依次向前覆盖前一个元素。
  • 时间复杂度:O (n)(最坏情况:删除头部需移动所有元素)。
③ 逻辑删除
ps->size--;
  • 通过减小 size 标记有效元素减少,无需物理清除最后一个元素。

基于以上的两个指定位置函数,我们可以进一步简化代码,将头插、尾插、头删、尾删函数均用任意位置函数代替,代码量将大大减少。后续总体展示时可以看出。

<9>.查找函数:

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

 代码较为简单,不加以赘述.....

综上,整个程序基本完成,接下来将总体展示,包括三个模块以及一些代码的简化:

4.整体展示:

<1>.测试模块(test.c):

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"SeqList.h"void TestSeqList1()
{SL s;SLInit(&s);SLPushBack(&s, 1);SLPushBack(&s, 2);SLPushBack(&s, 3);SLPushBack(&s, 4);SLPushBack(&s, 5);SLPushBack(&s, 6);SLPrint(&s);SLPopBack(&s);SLPopBack(&s);SLPrint(&s);SLPopFront(&s);SLPrint(&s);SLPopFront(&s);SLPrint(&s);SLInsert(&s, 2, 1);SLPrint(&s);void* ptr1 = malloc(10);printf("%p\n", ptr1);void* ptr2 = realloc(ptr1,20);printf("%p\n", ptr2);         //两次开辟地址不同,所以是异地扩容SLDestroy(&s);
}int main()
{TestSeqList1();return 0;
}

当然,若想要设置菜单,和通讯录一样也是可以的......

<2>.函数模块(SeqList.c):

#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"void SLInit(SL* ps)
{assert(ps);ps->a = (SLDataType*)malloc(sizeof(SLDataType)* INIT_CAPACITY);if (ps->a == NULL){perror("malloc fail");return;}ps->size = 0;ps->capacity = INIT_CAPACITY;
}void SLDestroy(SL* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->size = 0;
}void SLPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; ++i){printf("%d ", ps->a[i]);}printf("\n");
}void SLCheckCapacity(SL* ps)
{assert(ps);//扩容:if (ps->size == ps->capacity){SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity * 2);if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp; ps->capacity *= 2;}
}void SLPushBack(SL* ps, SLDataType x)
{//assert(ps);扩容://SLCheckCapacity(ps);//ps->a[ps->size++] = x;SLInsert(ps, ps->size, x);
}void SLPopBack(SL* ps)
{/*assert(ps);if (ps->size == 0)return;ps->size--;*/SLErase(ps, ps->size - 1);
}void SLPushFront(SL* ps, SLDataType x)
{/*assert(ps);SLCheckCapacity(ps);int end = ps->size - 1;while (end >= 0){ps->a[end + 1] = ps->a[end];--end;}ps->a[0] = x;ps->size++;*/SLInsert(ps, 0, x);
}void SLPopFront(SL* ps)
{/*assert(ps);assert(ps->size > 0);int begin = 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}ps->size--;*/SLErase(ps, 0);
}void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);int end = ps->size - 1;while (end >= pos){ps->a[end + 1] = ps->a[end];--end;}ps->a[pos] = x;ps->size++;
}void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);int begin = pos + 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}ps->size--;
}int SLFind(SL* ps, SLDataType x)
{assert(ps);for (int i = 0; i < ps->size; ++i){if (ps->a[i] == x){return i;}}return -1;
}

<3>.声明模块(SeqList.h):

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLDataType;
#define INIT_CAPACITY 4typedef struct SeqList
{SLDataType* a;int size;int capacity;
}SL;void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
void SLCheckCapacity(SL* ps);
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);

至此,代码全部结束,欢迎指出错误与不当之处,谢谢!!!
 

相关文章:

数据结构 --- 顺序表

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储&#xff0c;在数组上完成数据的增删查改 顺序表分为&#xff1a;静态顺序表、动态顺序表 一.静态顺序表 #define N 7 typedef int SLDataType;typedef struct Seqlist {…...

MySQL高级查询技巧:分组、聚合、子查询与分页【MySQL系列】

本文将深入探讨 MySQL 高级查询技巧&#xff0c;重点讲解 GROUP BY、HAVING、各种聚合函数、子查询以及分页查询&#xff08;LIMIT 语法&#xff09;的使用。文章内容涵盖实际应用中最常见的报表需求和分页实现技巧&#xff0c;适合有一定 SQL 基础的开发者进一步提升技能。 一…...

无人机多旋翼倾转动力测试系统-适用于(eVTOL开发、缩比模型测试、科研教育)

在倾转旋翼无人机、垂直起降&#xff08;VTOL&#xff09;及混合动力飞行器的研发中&#xff0c;动力系统在垂直-水平模式切换时的动态性能至关重要。LY-QZ-F4多旋翼倾转动力测试系统是全球首款专为倾转四旋翼设计的多自由度动力测试平台&#xff0c;融合高精度传感、动态倾转模…...

.NET8入门:14.ASP.NET Core MVC进阶——Model

上一篇文章我们了解了一下MVC在ASP.NET8中的一些基础概念&#xff0c;接下来深入了解一下ASP.NET Core MVC中Model的一些特性和用法。 Model 职责 Model 代表应用程序的核心数据和业务逻辑部分。它负责&#xff1a; 封装业务数据&#xff1a;表示应用程序中的实体&#xff0c;…...

latex figure Missing number, treated as zero. <to be read again>

\begin{figure}[h] \centering \includegraphics[width\linewidth]{pictures/architecture.pdf} \caption{Typical architecture.} \label{fig:architecture} \end{figure}&#xff0c; 我在编译latex&#xff0c;这段代码报错&#xff0c; Missing number, treated …...

java CompletableFuture创建异步任务(Completable异步+ExecutorService线程池)

文章目录 前置自定义线程池使用 CompletableFuture 创建异步任务 前置 来自 import java.util.concurrent.CompletableFuture; 自定义线程池 推荐根据业务需求配置 ExecutorService pool new ThreadPoolExecutor(10, // 核心线程数20, // 最大线程数60L, TimeUnit.SECONDS…...

LeetCode 高频 SQL 50 题(基础版)之 【聚合函数】部分

题目&#xff1a;620. 有趣的电影 题解&#xff1a; select * from cinema where description !boring and id%21 order by rating desc题目&#xff1a;1251. 平均售价 题解&#xff1a; select p.product_id product_id,round(ifnull(sum(p.price*u.units)/sum(u.units),0)…...

【AI学习】检索增强生成(Retrieval Augmented Generation,RAG)

1&#xff0c;介绍 出自论文《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》&#xff0c;RAG是权宜之计&#xff0c;通过RAG将问题简单化、精简化、剔除噪声&#xff0c;让LLM更容易理解、生成内容。RAG&#xff1a;检索增强技术检索生成&#xff08;重…...

低成本高效图像生成:GPUGeek和ComfyUI的强强联合

一、时代背景 在如今的数字化时代&#xff0c;图像生成技术正不断发展和演变&#xff0c;尤其是在人工智能领域。无论是游戏开发、虚拟现实&#xff0c;还是设计创意&#xff0c;图像生成已成为许多应用的核心技术之一。然而&#xff0c;随着图像质量需求的提升&#xff0c;生成…...

基于Matlab实现卫星轨道模拟仿真

在IT行业中&#xff0c;卫星轨道模拟和仿真程序是航空航天领域的重要工具&#xff0c;用于预测和分析人造卫星的运动轨迹。 我们需要理解卫星轨道的基本原理。地球引力使得卫星围绕地球运动&#xff0c;形成特定的椭圆或圆形轨道。牛顿的万有引力定律和开普勒的行星运动定律为…...

前端使用 spark-md5 实现大文件切片上传

需要计算文件MD5和、分片MD5&#xff1a; 封装公共方法代码如下&#xff1a; import SparkMD5 from "spark-md5"/*** 计算文件MD5* param file* returns*/ export function calculateFileMD5(file) {return new Promise((resolve) > {const reader new FileRea…...

《操作系统真相还原》——进入内核

ELF 按书上的操作来&#xff0c;在现代操作平台编译链接默认生成elf64 格式的文件&#xff0c; 很显然程序头位置发生变化&#xff0c;因为定义elf 结构的类型中有64位&#xff0c;所以我们需要将编译链接出32位格式的 gcc -m32 -c -o main.o main.c ld -m elf_i386 main.o …...

【QQ音乐】sign签名| data参数 | AES-GCM加密 | webpack(上)

1.目标 网址&#xff1a;https://y.qq.com/n/ryqq/toplist/26 切换榜单出现请求&#xff0c;可以看到sign和data是加密的 2.逆向分析 搜索sign: 可以看到sign P(n.data)&#xff0c;而n.data就是请求的加密data参数 data {"comm":{"cv":4747474,&qu…...

【STM32】按键控制LED 光敏传感器控制蜂鸣器

&#x1f50e;【博主简介】&#x1f50e; &#x1f3c5;CSDN博客专家 &#x1f3c5;2021年博客之星物联网与嵌入式开发TOP5 &#x1f3c5;2022年博客之星物联网与嵌入式开发TOP4 &#x1f3c5;2021年2022年C站百大博主 &#x1f3c5;华为云开发…...

M-OFDM模糊函数原理及仿真

文章目录 前言一、M序列二、M-OFDM 信号1、OFDM 信号表达式2、模糊函数表达式 三、MATLAB 仿真1、MATLAB 核心源码2、仿真结果①、m-OFDM 模糊函数②、m-OFDM 距离分辨率③、m-OFDM 速度分辨率④、m-OFDM 等高线图 四、资源自取 前言 本文进行 M-OFDM 的原理讲解及仿真&#x…...

【MySQL】MVCC与Read View

目录 一、数据库并发的三种场景 二、读写场景的MVCC &#xff08;一&#xff09;表中的三个隐藏字段 &#xff08;二&#xff09;undo 日志 &#xff08;三&#xff09;模拟MVCC &#xff08;四&#xff09;Read View &#xff08;五&#xff09;当前读和快照读 三、RC和…...

相机--双目立体相机

教程 链接1 教程汇总 立体匹配算法基础概念 视频讲解摄像机标定和双目立体原理 两个镜头。 双目相机也叫立体相机--Stereo Camera&#xff0c;属于深度相机。 作用 1&#xff0c;获取图像特征&#xff1b; 2&#xff0c;获取图像深度信息&#xff1b; 原理 原理和标定 …...

多目标粒子群优化算法(MOPSO),用于解决无人机三维路径规划问题,Matlab代码实现

多目标粒子群优化算法&#xff08;MOPSO&#xff09;&#xff0c;用于解决无人机三维路径规划问题&#xff0c;Matlab代码实现 目录 多目标粒子群优化算法&#xff08;MOPSO&#xff09;&#xff0c;用于解决无人机三维路径规划问题&#xff0c;Matlab代码实现效果一览基本介绍…...

工厂模式 vs 策略模式:设计模式中的 “创建者” 与 “决策者”

在日常工作里&#xff0c;需求变动或者新增功能是再常见不过的事情了。而面对这种情况时&#xff0c;那些耦合度较高的代码就会给我们带来不少麻烦&#xff0c;因为在这样的代码基础上添加新需求往往困难重重。为了保证系统的稳定性&#xff0c;我们在添加新需求时&#xff0c;…...

23、Swift框架微调实战(3)-Qwen2.5-VL-7B LORA微调OCR数据集

一、模型介绍 Qwen2.5-VL 是阿里通义千问团队开源的视觉语言模型,具有3B、7B和72B三种不同规模,能够识别常见物体、分析图像中的文本、图表等元素,并具备作为视觉Agent的能力。 Qwen2.5-VL 具备作为视觉Agent的能力,可以推理并动态使用工具,初步操作电脑和手机。在视频处…...

37. Sudoku Solver

题目描述 37. Sudoku Solver 回溯 class Solution {vector<vector<bool>> row_used;vector<vector<bool>> col_used;vector<vector<bool>> box_used;public:void solveSudoku(vector<vector<char>>& board) {row_used.r…...

C# Renci.SshNet 登陆 suse配置一粒

C# 调用Renci.SshNet 的SSH类库&#xff0c;登陆 suse linux系统&#xff0c;如果没有配置&#xff0c;会报错&#xff1a; Renci.SshNet.Common.SshAuthenticationException: No suitable authentication method found to complete 1、需要root登陆os,配置 /etc/ssh/sshd_con…...

RV1126-OPENCV 图像叠加

一.功能介绍 图像叠加&#xff1a;就是在一张图片上放上自己想要的图片&#xff0c;如LOGO&#xff0c;时间等。有点像之前提到的OSD原理一样。例如&#xff1a;下图一张图片&#xff0c;在左上角增加其他图片。 二.OPENCV中图像叠加常用的API 1. copyTo方法进行图像叠加 原理…...

修改 vscode 左侧导航栏的文字大小 (更新版)

1. 起因&#xff0c; 目的: 问题&#xff1a; vscode 左侧的文字太小了&#xff01;&#xff01;&#xff01;我最火的一篇文章&#xff0c;写的就是这个问题。 看来这个问题&#xff0c;是很广泛的一个痛点。我最近更新了 vscode&#xff0c; 这个问题又出现了。再来搞一下。…...

从C++编程入手设计模式2——工厂模式

从C编程入手设计模式 工厂模式 ​ 我们马上就要迎来我们的第二个创建型设计模式&#xff1a;工厂方法模式&#xff08;Factory Method Pattern&#xff09;。换而言之&#xff0c;我们希望使用一个这样的接口&#xff0c;使用其他手段而不是直接创建的方式&#xff08;说的有…...

云原生 Cloud Native Build (CNB)使用初体验

云原生 Cloud Native Build&#xff08;CNB&#xff09;使用初体验 引言 当“一切皆可云”成为趋势&#xff0c;传统开发环境正被云原生工具重塑。腾讯云CNB&#xff08;Cloud Native Build&#xff09;作为一站式开发平台&#xff0c;试图解决多环境协作难题。 本文将分享c…...

格式工厂 FormatFactory v5.20.便携版 ——多功能媒体文件转换工具 长期更新

—————【下 载 地 址】——————— 【​本章下载一】&#xff1a;https://pan.xunlei.com/s/VORWF3Q7D0eCVV06LHbzheD-A1?pwdjikz# 【​本章下载二】&#xff1a;https://pan.quark.cn/s/8ee59ed83658 【百款黑科技】&#xff1a;https://ucnygalh6wle.feishu.cn/wiki/…...

数据可视化--使用matplotlib绘制高级图表

目录 一、绘制等高线图 contour() 二、绘制矢量场流线图 streamplot() 三、绘制棉棒图 stem() 四、绘制哑铃图 五、绘制甘特图 六、绘制人口金字塔图 barh() 七、绘制漏斗图 简易版漏斗图 八、绘制桑基图 Sankey()---创建桑基图 add()---添加桑基图的选项 finish()…...

卷积神经网络(CNN)完全指南:从原理到实战

卷积神经网络(CNN)完全指南&#xff1a;从原理到实战 引言&#xff1a;为什么CNN改变了计算机视觉&#xff1f; 2012年&#xff0c;AlexNet在ImageNet竞赛中以压倒性优势获胜&#xff0c;将错误率降低了近10个百分点&#xff0c;这标志着卷积神经网络(CNN)时代的开始。如今&a…...

如何做好一个决策:基于 Excel的决策树+敏感性分析应用

决策点: 开发新产品? (是 / 否) 因素 (如果是): 市场接受度 (高 / 中 / 低);概率: 高(0.3), 中(0.5), 低(0.2) 结果值 (NPV): 高(+$1M), 中(+$0.2M), 低(-$0.5M) 不开发成本/收益: $0 开发计算: EMV(市场接受度) = (0.3 * 1M) + (0.5 * 0.2M) + (0.2 * -0.5M) = $0.3M + $…...