普通视图

Received before yesterday东篱blog

EasyX基础

作者东篱
2025年10月19日 18:24

EasyX环境配置

此处演示的是Clion的EasyX环境配置,如果你使用的是VisualStudio,那么你可以直接使用官网的安装包程序进行安装

  1. 进入EasyX官网

image-20251019124438023

  1. 点击右上角下载

image-20251019124645901

  1. 点击上方标题

image-20251019124724822

  1. 选择支持MinGW的版本

image-20251019124843317

  1. 点击下载

image-20251019124924153

  1. 解压下载好的zip压缩包
  2. 将解压后的文件夹复制,放置在合适的位置
  3. 在Clion中新建C++项目,然后打开项目中的CMakeLists.txt并如下文所示添加代码
#文件包路径,例如D:/Data/easyx/easyx4mingw_25.9.10
set(EasyX_Dir 文件包路径)#此句要放到add_executable前
#下面三句要放到add_executable后
target_include_directories(${PROJECT_NAME} PRIVATE  ${EasyX_Dir})
target_link_libraries(${PROJECT_NAME} ${EasyX_Dir}/lib64/libEasyX.a)
target_link_directories(${PROJECT_NAME} PRIVATE ${EasyX_Dir})

The post EasyX基础 first appeared on 东篱blog.

解决Git相关操作速度非常之慢的问题

作者东篱
2025年8月10日 11:55

在最近开发过程中,发现无论是IDEA还是WebStorm,在使用其内置的git工具系列操作(如更新、拉取、切换分支)时,等待时间非常之长,有时甚至会达到10多分钟,这肯定是不正常的,遂寻求解决方案

经查阅和验证,问题实际上出在一项Windows服务(Microsoft PC Manager Service)上面,将该服务停止并禁用,即可解决问题,使git操作恢复至瞬间完成的状态

解决方案步骤如下

  1. 按下Win+R,输入services.msc打开服务管理器image-20250810113234986
  2. 在服务列表中找到Microsoft PC Manager Serviceimage-20250810114827037
  3. 右键并选择属性,打开该服务的属性配置页image-20250810115007638
  4. 点击启动类型右侧下拉菜单,将其设置为禁用,并点击停止按钮image-20250810115214962
  5. 如图设置好之后,点击应用,而后点击确定image-20250810115344263

The post 解决Git相关操作速度非常之慢的问题 first appeared on 东篱blog.

Redis高可用方案

作者东篱
2025年3月11日 11:49

在使用Redis存储数据时,如果只部署一个Redis服务,可能会出现单点故障,一旦Redis服务寄掉了,整套服务都完蛋,因此我们需要一些Redis的高可用方案

Redis 高可用性方案的核心是通过 数据冗余自动故障切换 来确保在某个 Redis 节点宕机时,服务不会中断。以下是几种常见的高可用方案的 原理 解析:

Redis Sentinel

Redis Sentinel 是 Redis 官方提供的一种高可用性解决方案,用于监控、自动故障转移(failover)和提供通知服务。Sentinel 主要通过监控 Redis 实例的健康状况,自动切换主从节点,保证系统高可用。

一般情况用这个就行,这个也是Redis官方推荐的

原理:

  • 监控功能:Sentinel 会周期性地检查所有 Redis 实例的状态,包括主节点和从节点的健康状态。通过心跳检查,Sentinel 能判断节点是否宕机。
  • 自动故障转移:当 Sentinel 发现主节点宕机时,会从现有的从节点中选举一个新的主节点,并将原来的从节点设置为新的从节点。
  • 通知功能:Sentinel 会通过发布通知的方式告知其他 Redis 客户端主节点的变化。

与持久化(AOF 和 RDB)的关系:

  • AOF 和 RDB:Sentinel 本身不涉及数据持久化,它依赖于 Redis 的持久化机制(如 AOF 和 RDB)。在主节点发生故障切换时,新的主节点会从之前的从节点恢复数据。如果启用了 AOF 或 RDB,数据能够从持久化文件中恢复。
  • AOF(追加文件日志):Redis 以日志的方式将每次写操作追加到磁盘,可以保证数据几乎不丢失,即使在主节点崩溃后,也可以通过 AOF 恢复数据。
  • RDB(Redis 数据库快照):Redis 定期将内存中的数据快照保存到磁盘,如果配置了 RDB 持久化,在主节点崩溃时,会丢失最近的几秒/分钟数据,具体取决于上次保存的快照时间。相对于 AOF,RDB 可能丢失一些未同步到磁盘的操作。

通俗来讲,RDB就是备份一个存档,恢复数据只能恢复到上次备份的存档,而AOF相当于把之前的每一步操作都往磁盘里记录了一遍,恢复数据就是重新执行一遍所有未执行的操作就可以了

优缺点

  • 优点:简单、适用于小型 Redis 集群。
  • 缺点:仅支持单主从架构,扩展性差。

Redis Cluster

Redis Cluster 是 Redis 提供的一个分布式解决方案,能够将数据分片(sharding)存储在多个 Redis 实例上。Cluster 支持自动故障转移,能保证集群内节点的高可用性和分布式存储。

原理:

  • 数据分片:Redis Cluster 会将数据分布在多个节点上,每个节点负责一个数据范围(slot)。数据通过一致性哈希分配到不同的节点,保证负载均衡和高效存储。
  • 自动故障转移:每个数据分片(slot)有一个主节点和多个从节点。当某个主节点发生故障时,Redis Cluster 会自动选择一个从节点升级为新的主节点,并通过 Cluster Manager 实现故障转移。
  • 客户端透明切换:客户端直接连接到 Redis Cluster 中的任一节点,Cluster 会根据数据的 slot 值将请求路由到正确的节点。

与持久化(AOF 和 RDB)的关系:

  • AOF 和 RDB:Redis Cluster 结合持久化(AOF 或 RDB)来保证数据的持久化存储。每个节点都有自己的 AOF 或 RDB 配置,节点发生故障时,从节点会同步 AOF 或 RDB 中的数据来恢复。
  • AOF:适用于需要保证数据不丢失的场景,AOF 记录所有写操作,提供高精度的数据恢复。
  • RDB:适用于性能要求较高的场景,定期进行快照操作,恢复时可能会丢失最新的数据。

优缺点

  • 优点:高可用、高扩展性,适用于大规模应用。
  • 缺点:配置复杂,管理和维护要求较高。

实际上Cluster相比于Sentinel的特点是不是就是数据分片,拿后端举例子,就是分布式单体架构和微服务架构的区别,Sentinel就相当于分布式单体架构,只是通过整体的复制和故障转移保证可靠性,而Cluster就相当于微服务架构,将完整的数据拆分成很多数据分片,分别放在不同节点上维护,可靠性更高且便于横向拓展

Redis with Proxy(Twemproxy 或 Codis)

通过 代理层(如 TwemproxyCodis)来管理 Redis 集群,代理充当客户端和 Redis 实例之间的中介,提供负载均衡和故障转移。

原理:

  • 代理层:代理服务位于客户端和 Redis 实例之间,将客户端请求路由到对应的 Redis 实例。通过代理层实现负载均衡、故障转移和连接池管理。
  • 故障转移和负载均衡:代理层会监控 Redis 实例的健康状态,出现故障时会将请求转发到健康的 Redis 实例。
  • 集群管理:类似于 Redis Cluster 的数据分片,代理层将数据分发到多个 Redis 节点,并负责处理分片操作。

与持久化(AOF 和 RDB)的关系:

  • AOF 和 RDB:每个 Redis 节点依然需要配置 AOF 或 RDB 持久化,代理层只负责请求的路由和负载均衡,数据持久化和故障恢复依赖于 Redis 本身的机制。
  • AOF:适用于对数据一致性要求较高的应用,确保 Redis 服务在宕机后能恢复数据。
  • RDB:适用于性能要求较高的应用,虽然会有数据丢失,但恢复速度较快。

优缺点

  • 优点:通过代理层简化了 Redis 集群的访问,支持负载均衡,适用于中小型应用。
  • 缺点:代理层可能成为性能瓶颈,需要额外维护。

其实Twemproxy类似于nginx,只不过对Redis做了适配,实际上是为Redis提供了一种负载均衡策略,使其支持更高的并发。但是在实际生产中,使用Cluster可能更好,因为Cluster也提供了负载均衡,同时还具备自动故障转移和集群管理能力

总结:

方案 适用场景 可靠性 复杂度
Redis Sentinel 小到中规模应用,单主从结构 ⭐⭐⭐⭐
Redis Cluster 大规模、需要扩展的应用 ⭐⭐⭐⭐⭐
代理(Twemproxy, Codis) 需要负载均衡的应用 ⭐⭐⭐

如果只考虑 Redis 本身的高可用性,最推荐的方案是使用 Redis SentinelRedis Cluster

The post Redis高可用方案 first appeared on 东篱blog.

Celery任务队列的Redis高可用方案

作者东篱
2025年3月11日 11:49

在使用Clelty时,如果Redis 作为 Broker ,容易引发单点故障(SPOF,Single Point of Failure),如果 Redis 挂了,Celery 就无法提交和获取任务了,本文主要介绍一下解决方案。(直接使用RabbitMQ就行)

本文可以参考文章Redis高可用方案

🌟 解决 Redis 单点故障的方法

如果担心 Redis 挂掉影响 Celery,可以使用以下方案:

方案 1:Redis Sentinel(官方推荐)

Redis Sentinel 是 Redis 官方提供的高可用方案,它可以自动切换主节点,保证 Celery 任务队列的稳定性。

✅ 优点:

  • 自动故障转移,如果 Redis 主节点崩溃,Sentinel 会自动选出新的主节点。
  • 应用程序无感知切换,Celery 连接 Sentinel,Sentinel 负责返回当前的主节点地址。

✅ 配置 Celery 使用 Sentinel

1、启动 Redis Sentinel

  • 配置sentinel.conf,假设 Redis 运行在127.0.0.1:6379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
  • 启动 Sentinel:
redis-sentinel /etc/redis/sentinel.conf

2、修改 Celery 配置,使用 Sentinel

from celery import Celery
from kombu import Connection

broker_url = "sentinel://127.0.0.1:26379;sentinel://127.0.0.1:26380"
app = Celery('tasks', broker=broker_url, backend="redis://127.0.0.1:6379/1")

app.conf.broker_transport_options = {
    'sentinel': [('127.0.0.1', 26379), ('127.0.0.1', 26380)],
    'master_name': 'mymaster',
}
  • 这样,Celery 会自动连接 Sentinel 获取当前的 Redis 主节点,主从切换时不会影响任务执行

方案 2:Redis Cluster

Redis Cluster 是 Redis 自带的分布式方案,提供数据分片+高可用

✅ 优点:

  • 数据分片,支持大规模数据存储,适合高并发任务队列。
  • 内置故障转移,节点宕机时,集群会自动选出新的主节点。

✅ 配置 Celery 使用 Redis Cluster

1、启动 Redis Cluster(假设 6 个节点,3 主 3 备):

redis-cli --cluster create 192.168.1.1:6379 192.168.1.2:6379 192.168.1.3:6379 \
192.168.1.4:6379 192.168.1.5:6379 192.168.1.6:6379 --cluster-replicas 1

2、修改 Celery 配置

from celery import Celery

app = Celery('tasks', broker='redis://192.168.1.1:6379/0', backend='redis://192.168.1.1:6379/1')

app.conf.broker_transport_options = {
    'cluster': [
        "redis://192.168.1.1:6379/0",
        "redis://192.168.1.2:6379/0",
        "redis://192.168.1.3:6379/0",
    ]
}
  • 这样 Celery 会自动负载均衡多个 Redis 节点,即使某个节点挂了,任务队列也能继续运行

方案 3:RabbitMQ 代替 Redis(更稳定)

如果不想用 Redis,可以考虑 RabbitMQ,它是 Celery 官方默认的 Broker,提供持久化存储+集群高可用

✅ 优点:

  • RabbitMQ 的消息存储在磁盘中,即使崩溃重启,消息不会丢失
  • 支持 高可用模式(HA),多个节点一起工作,不会有单点故障
  • 任务队列 更稳定,支持事务、消息确认、队列优先级等功能

✅ 配置 Celery 使用 RabbitMQ

app = Celery('tasks', broker='pyamqp://guest@localhost//')
  • 这样 Celery 会连接 RabbitMQ,所有任务都会进入 RabbitMQ 队列,即使 Celery 挂了,任务也不会丢失

🚀 最佳实践(推荐)

方案 适用场景 可靠性 复杂度
Redis Sentinel 中小规模任务队列 ⭐⭐⭐⭐ 中等
Redis Cluster 高并发、大规模任务 ⭐⭐⭐⭐
RabbitMQ 需要强一致性任务(如支付、交易) ⭐⭐⭐⭐⭐

🔹 如果只是简单任务队列,Redis Sentinel 就够用了。
🔹 如果任务量很大,Redis Cluster 更合适。
🔹 如果任务不能丢,RabbitMQ 是最佳选择。


✅ 总结

🚀 Redis 默认是单点故障,但可以用 Sentinel 或 Cluster 解决。
🚀 如果任务特别重要,建议用 RabbitMQ 代替 Redis。
🚀 根据业务需求选择合适的 Broker 方案,避免单点故障! 💡

The post Celery任务队列的Redis高可用方案 first appeared on 东篱blog.

苍穹外卖项目实战

作者东篱
2025年1月25日 12:23

软件开发整体介绍

软件开发流程

image-20250317211931296

需求分析

  • 需求规格说明书:形成文档介绍
  • 产品原型:通过静态网页展示业务功能

设计

  • UI设计:将页面各个方面的细节设计完善
  • 数据库设计:先设计E-R图,然后再具体设计表的字段和类型等详细细节
  • 接口设计:就是设计接口文档(使用Apifox)

编码

  • 项目代码:业务逻辑的代码
  • 单元测试:用于测试项目代码的单元测试(开发人员自测)

测试

  • 测试用例:以接口为单位编写测试用例
  • 测试报告:对测试情况进行报告

上线运维

  • 软件环境安装:安装运行环境
  • 配置:进行一些线上的配置,如Nginx

角色分工

  • 项目经理:对整个项目负责,任务分配、把控进度
  • 产品经理:进行需求调研,输出需求调研文档、产品原型等
  • UI设计师:根据产品原型输出界面效果图
  • 架构师:项目整体架构设计、技术选型等
  • 开发工程师:代码实现
  • 测试工程师:编写测试用例,输出测试报告
  • 运维工程师:软件环境搭建、项目上线

软件环境

  • 开发环境(development):开发人员在开发阶段使用的环境,一般外部用户无法访问
  • 测试环境(testing):专门给测试人员使用的环境,用于测试项目,一般外部用户无法访问
  • 生产环境(production):即线上环境,正式提供对外服务的环境

Apifox等相关应用也应该采用与此统一的三种软件环境

苍穹外卖项目介绍

项目介绍

image-20250125095402962

image-20250125095622802

产品原型

image-20250125100337243

技术选型

image-20250125102924973

开发环境搭建

前端环境搭建

image-20250125105841391

后端环境搭建

注意,此处的搭建方式和目录结构并不是最佳实践,实际目录结构要根据具体项目来进行具体的架构设计

image-20250125105918916

image-20250125110227200

image-20250125110343659

image-20250125110700061

搭建好之后提交git仓库

数据库设计文档

image-20250125112438572

注意,这里的数据库设计并非最佳实践,实际上数据库的表名的最佳实践应该为模块名_功能点,如此更容易后期拓展其他模块

另外,用户名(账号)和密码这里做的是不错的,都应该是varchar,有的项目中账号使用数字,是不正确的做法

序号 数据表名 中文名称
1 employee 员工表
2 category 分类表
3 dish 菜品表
4 dish_flavor 菜品口味表
5 setmeal 套餐表
6 setmeal_dish 套餐菜品关系表
7 user 用户表
8 address_book 地址表
9 shopping_cart 购物车表
10 orders 订单表
11 order_detail 订单明细表

1. employee

employee表为员工表,用于存储商家内部的员工信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 姓名
username varchar(32) 用户名 唯一
password varchar(64) 密码
phone varchar(11) 手机号
sex varchar(2) 性别
id_number varchar(18) 身份证号
status int 账号状态 1正常 0锁定
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

2. category

category表为分类表,用于存储商品的分类信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 分类名称 唯一
type int 分类类型 1菜品分类 2套餐分类
sort int 排序字段 用于分类数据的排序
status int 状态 1启用 0禁用
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

3. dish

dish表为菜品表,用于存储菜品的信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 菜品名称 唯一
category_id bigint 分类id 逻辑外键
price decimal(10,2) 菜品价格
image varchar(255) 图片路径
description varchar(255) 菜品描述
status int 售卖状态 1起售 0停售
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

4. dish_flavor

dish_flavor表为菜品口味表,用于存储菜品的口味信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
dish_id bigint 菜品id 逻辑外键
name varchar(32) 口味名称
value varchar(255) 口味值

5. setmeal

setmeal表为套餐表,用于存储套餐的信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 套餐名称 唯一
category_id bigint 分类id 逻辑外键
price decimal(10,2) 套餐价格
image varchar(255) 图片路径
description varchar(255) 套餐描述
status int 售卖状态 1起售 0停售
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

6. setmeal_dish

setmeal_dish表为套餐菜品关系表,用于存储套餐和菜品的关联关系。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
setmeal_id bigint 套餐id 逻辑外键
dish_id bigint 菜品id 逻辑外键
name varchar(32) 菜品名称 冗余字段
price decimal(10,2) 菜品单价 冗余字段
copies int 菜品份数

7. user

user表为用户表,用于存储C端用户的信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
openid varchar(45) 微信用户的唯一标识
name varchar(32) 用户姓名
phone varchar(11) 手机号
sex varchar(2) 性别
id_number varchar(18) 身份证号
avatar varchar(500) 微信用户头像路径
create_time datetime 注册时间

8. address_book

address_book表为地址表,用于存储C端用户的收货地址信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
user_id bigint 用户id 逻辑外键
consignee varchar(50) 收货人
sex varchar(2) 性别
phone varchar(11) 手机号
province_code varchar(12) 省份编码
province_name varchar(32) 省份名称
city_code varchar(12) 城市编码
city_name varchar(32) 城市名称
district_code varchar(12) 区县编码
district_name varchar(32) 区县名称
detail varchar(200) 详细地址信息 具体到门牌号
label varchar(100) 标签 公司、家、学校
is_default tinyint(1) 是否默认地址 1是 0否

9. shopping_cart

shopping_cart表为购物车表,用于存储C端用户的购物车信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 商品名称
image varchar(255) 商品图片路径
user_id bigint 用户id 逻辑外键
dish_id bigint 菜品id 逻辑外键
setmeal_id bigint 套餐id 逻辑外键
dish_flavor varchar(50) 菜品口味
number int 商品数量
amount decimal(10,2) 商品单价
create_time datetime 创建时间

10. orders

orders表为订单表,用于存储C端用户的订单数据。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
number varchar(50) 订单号
status int 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消
user_id bigint 用户id 逻辑外键
address_book_id bigint 地址id 逻辑外键
order_time datetime 下单时间
checkout_time datetime 付款时间
pay_method int 支付方式 1微信支付 2支付宝支付
pay_status tinyint 支付状态 0未支付 1已支付 2退款
amount decimal(10,2) 订单金额
remark varchar(100) 备注信息
phone varchar(11) 手机号
address varchar(255) 详细地址信息
user_name varchar(32) 用户姓名
consignee varchar(32) 收货人
cancel_reason varchar(255) 订单取消原因
rejection_reason varchar(255) 拒单原因
cancel_time datetime 订单取消时间
estimated_delivery_time datetime 预计送达时间
delivery_status tinyint 配送状态 1立即送出 0选择具体时间
delivery_time datetime 送达时间
pack_amount int 打包费
tableware_number int 餐具数量
tableware_status tinyint 餐具数量状态 1按餐量提供 0选择具体数量

11. order_detail

order_detail表为订单明细表,用于存储C端用户的订单明细数据。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 商品名称
image varchar(255) 商品图片路径
order_id bigint 订单id 逻辑外键
dish_id bigint 菜品id 逻辑外键
setmeal_id bigint 套餐id 逻辑外键
dish_flavor varchar(50) 菜品口味
number int 商品数量
amount decimal(10,2) 商品单价

前后端联调

前后端都搭建好之后,通过实现登录等基础功能,进行前后端的联调,尽早联调防止后续出现问题

image-20250125113042698

Nginx反向代理

正向代理:代理客户端(隐藏用户)

反向代理:代理服务端(隐藏服务器)

image-20250125120554075

image-20250125120845201

image-20250125121040531

image-20250125121244660

image-20250125121444149

image-20250125121533164

补充知识

image-20250126103429705

最好是前端和后端都进行加密,如此一来更加安全

image-20250126120756918

实际开发过程中,最好是结合使用HTTP状态码、业务状态码以及业务信息,HTTP状态码负责划分错误大体信息,业务状态码负责明确错误详细信息,业务信息用来提供用户友好型的提示信息

另外,业务状态码可以采用阿里文档中的状态码,其中没有的状态码可以自己新增,需要遵守文档状态码的分类模式

因此Result枚举中应该如下所示:

image-20250126124937528

全局自定义异常捕获应该如下所示:

image-20250126125800124

前后端分离开发流程

image-20250126153641670

使用Swagger进行接口测试

注意,实际开发过程中使用Apifox进行调试和测试即可,比这个方便很多很多

但是实际开发过程中,Swagger还是需要使用的,因为其放在源码中,可以便捷的生成接口文档,算是一种冗余策略(Swagger注解和注释都要有)

image-20250126155038528

image-20250126155048167

image-20250126155308059

image-20250126155400037

image-20250126161550579

业务开发

JWT令牌完整流程

image-20250128101638833

JWT令牌原理

JWT令牌分为三部分:头部,载荷,签名

头部和载荷是不加密的,而签名是通过不可逆的签名算法获取到的密文

签名=不可逆的签名算法(头部+载荷,密钥)

JWT令牌=头部.载荷.不可逆的签名算法(头部+载荷,密钥)

前端与后端交互时,将JWT令牌给到后端,后端通过令牌中公开的头部和载荷,以及只有后端自己知道的密钥,再次进行签名,将此签名和前端传过来的前面对比一下,一样的话就通过校验,不一样就不通过

注:不可逆的签名算法指的是计算过程只能从左到右,不能从右到左的算法,例如36×78=2808,这个算式从左到右很好算,但是你只知道2808,想要知道算式左侧是啥就很难办)

如何获取到JWT令牌中存储的信息

image-20250128112148972

每个请求的处理都是独立的线程,而ThreadLocal(线程局部存储)提供了一种独立存储每个线程局部变量的方式,其提供的方法可以以线程独立的方式存取当前线程的数据,因此我们可以在解析JWT的时候,可以将JWT信息放到ThreadLocal中,从而使当前线程随时能访问JWT中的信息

public class JwtContext {
    // 使用 ThreadLocal 来存储每个线程的 JWT 信息
    private static ThreadLocal<String> jwtToken = new ThreadLocal<>();

    // 设置当前线程的 JWT 信息
    public static void setJwtToken(String token) {
        jwtToken.set(token);
    }

    // 获取当前线程的 JWT 信息
    public static String getJwtToken() {
        return jwtToken.get();
    }

    // 清理线程的 JWT 信息
    public static void clear() {
        jwtToken.remove();
    }
}

1. ThreadLocal 的工作机制

ThreadLocal 为每个线程提供一个独立的存储空间。每个线程在访问 ThreadLocal 时,都会看到与自己相关的数据,而不会与其他线程共享数据。

  • 当你通过 ThreadLocal.set() 方法存储数据时,数据会被存储在当前线程的局部存储空间中。
  • 通过 ThreadLocal.get() 获取数据时,只能访问当前线程的存储数据。

2. 线程生命周期

  • 当线程结束时,ThreadLocal 中存储的数据会被销毁。这意味着如果某个请求处理结束,线程被回收,那么该线程在 ThreadLocal 中存储的任何数据都会随之销毁。
  • 如果 线程池复用线程(例如在 Web 应用中使用的线程池),则线程并不会完全销毁,而是会在下一次使用时继续工作。在这种情况下,ThreadLocal 数据可能在下一次线程复用时仍然存在,除非你显式地调用 ThreadLocal.remove() 来清除它。

3. 避免内存泄漏

在某些环境中,尤其是线程池复用的情况下,线程的生命周期比请求的生命周期长。如果在请求结束后没有显式地清除 ThreadLocal 中的内容,它可能会导致内存泄漏,因为 ThreadLocal 会保持对数据的引用,而这些线程可能长时间存在于线程池中。

为了避免这种情况,应该显式地清除 ThreadLocal 中的数据

4. 总结

  • 当线程结束时,ThreadLocal 存储的数据会被销毁,不会占用额外的内存
  • 但如果使用 线程池,在复用线程时,ThreadLocal 中的内容不会自动清除,这时需要手动调用 ThreadLocal.remove() 来确保清理,避免内存泄漏。

因此,为了确保每个请求处理完成后 ThreadLocal 中的数据被清理,应该在请求处理的结束阶段调用 ThreadLocal.remove(),尤其是在使用线程池的情况下。

分页查询接口设计

image-20250130114251709

实际开发过程中,分页查询不需要自己实现,直接使用mybits-flex中的即可

image-20250130115001095

统一的消息处理(如日期时间等)

为了符合工程化的统一处理,在项目实践中往往会在WebMvcConfiguration中扩展Spring MVC消息转换器,统一对日期类型进行格式化处理

image-20250130124406103

切面应用场景

一般是用于公共字段填充,以及很多的公共统一场景

image-20250130175354828

image-20250130180733566

文件上传应用场景

对于文件上传的应用场景,我们往往采用对象存储服务,如阿里云的OSS或腾讯云的COS,本地部署对象存储也是个方案,但考虑到云原生的对象存储既便宜又好用,因此使用云原生的对象存储是最佳实践

具体的使用方式和最佳实践可以参考该知乎问答中的内容:上传文件应该经过后端吗,还是直接上传至阿里oss?

1、从服务端获取上传凭证

2、使用上传凭证上传文件到OSS

工程化获取自定义yml属性的最佳实践

1、实现自定义属性类,并提交给Bean管理

2、在yml配置文件或配置类中进行配置

image-20250131222158407

3、在需要使用的位置通过依赖注入进行调用

image-20250131222328412

注意,虽然@value也可以实现读取yml属性值,但是其存在硬编码等问题,不符合工程化最佳实践

多表操作使用事务

如果一个接口(方法)要同时对多个表进行操作,一定要使用事务,以此来保证操作的原子性,也就是说要么保证对所有表的操作全部成功,要么全部失败,不允许只对其中部分表的操作成功,从而保证数据的一致性

1、对SpringBoot应用开启注解方式的事务管理

image-20250201114936532

2、对需要保证数据一致性的方法添加事务注解(保证该方法为原子性操作,要么全成功,要么全失败)

image-20250201115142462

事务注解作用:将当前方法交给Spring进行事务管理,方法执行前,开启事务,成功执行完毕则提交事务;出现异常则回滚事务

The post 苍穹外卖项目实战 first appeared on 东篱blog.

《新生完全进化指南》使用教程

作者东篱
2024年8月27日 11:48

Virtual Judge (VJ) 是一个极好的平台,帮助我们练习代码、打下坚实的编程基础。通过 VJ 平台,你可以练习各种难度和类型的编程题目。此外,你还可以在 VJ 平台上创建比赛,与其他参赛者一较高下,并直观地了解自己在参赛者中的水平。

为了帮助大家测试自己的编程水平,并为有志于提升技术的同学提供支持,我们在 VJ 平台上创建了一场比赛,名为《新生完全进化指南》。欢迎大家踊跃参与,并在群内积极讨论交流。接下来,我将详细介绍如何参与我们的比赛。

使用教程

1.png

  • 填写基本信息完成注册,用户名的格式为AIGC_姓名全拼

2.png

  • 注册完成后登录账号(默认情况注册完应该会自动登录)

  • 接下来在比赛页面输入密码:donglizhiyuan

  • 进入比赛赛题页面,即可开始答题

3.png

  • 进入答题界面后,点击提交代码

4.png

  • 在提交界面进行账户配置
  • 点击Update进入账户配置界面

5.png

  • 点击洛谷进入洛谷平台

6.png

  • 在洛谷平台注册并登录账号

7.png

  • 登录后按F12键打开开发者工具界面

  • 在开发这工具中依次点击:Application(应用程序)->Cookies->https://www.luogu.com.cn

  • 找到Name为client_id,_uid的条目,右键编辑并复制他的值(Value)

8.png

9.png

点击完编辑“值”之后,右键复制

10,png

  • 返回比赛的账户配置界面
  • 将刚才复制的值粘贴到Value文本框中
  • 点击进行确认

11.png

  • 此处显示绿色对钩,账户配置成功

12.png

  • 在语言处选择你想要使用的编程语言

13.png

  • Solution中填写你想要提交的代码
  • 点击右下角Submit提交代码

14

  • Status如果显示为Accepted,那么恭喜你,回答正确!我们通常称之为这道题AC了

15

  • 如果想看看自己的排名,请前往Rank标签页(排名与答题快慢、答对题数有关)

16

恭喜你!读到这里,你已经掌握了如何使用 VJ 平台参加比赛。现在,赶快测试一下,看看你的技术水平达到了什么高度吧!

The post 《新生完全进化指南》使用教程 first appeared on 东篱blog.

乐理经典题目

作者东篱
2024年1月28日 18:08

学习编曲,练习是非常重要的,本篇文章整理了乐理相关的文本经典题目,供日后复习使用。其他题目如实践性质的编曲工程则不便于在此展示,因此请前往百度网盘中查看。

编曲练习题网盘链接:编曲练习题

声音的基本属性

Capture_20240128_165421

Capture_20240128_165435

乐音体系与音的命名

Capture_20240128_165446

Capture_20240128_165458

调式及其标记

Capture_20240130_200709

Capture_20240130_200801

节拍与音符

Capture_20240131_211452

Capture_20240131_211508

The post 乐理经典题目 first appeared on 东篱blog.

高数常用反例及经典错误

作者东篱
2024年1月25日 17:50

高数的学习过程中,反例是相当重要的,对于很多选择类的题目,我们只要能找到反例,很快就能迎刃而解,本篇文章便用于汇总高数学习过程中常见的反例及经典错误。

Capture_20240125_174020

Capture_20240125_174040

Capture_20240125_174059

Capture_20240125_174113

Capture_20240125_174130

Capture_20240125_174144

Capture_20240125_174204

Capture_20240125_174217

Capture_20240125_174226

Capture_20240126_173149

Capture_20240201_201019

Capture_20240201_200145

Capture_20240204_192549

Capture_20240204_192600

Capture_20240204_193738

Capture_20240206_205531

The post 高数常用反例及经典错误 first appeared on 东篱blog.

乐器法

作者东篱
2024年1月18日 14:18

乐器法和配器

109乐器法和配器

软件音源

image-20240118130756322

Kontakt是一个非常常用的采样器,真正编曲中很多采样音源都是通过Kontakt采样器加载的,课程中没有用到Kontakt音源,是因为避免优质音源掩盖编曲本身细节方面的问题。

image-20240123131730572

注意,因为Kontakt中的采样音源都很大,所以一般装在移动硬盘上,用的时候再插上去,但是需要保证盘符一致,否则会找不到。

image-20240123132056642

由上图可见,课程中各种类型的音源都用到了(多通道、单通道、loop音源……)

架子鼓

image-20240118134820296

低音大鼓

  • 整个架子鼓中声音最低最重的一件,用来铺设主要节奏。

  • 音乐中越是低音和低频的部分,对节奏的作用是越强的。

嗵嗵鼓

  • 嗵嗵鼓由小到大,声音由高到低,分为高音嗵鼓-中音嗵鼓-低音嗵鼓
  • 嗵嗵鼓一般不在平稳进行中使用,一般在过度位置和特殊情况使用。
  • 嗵嗵鼓不能和踩镲同时发声,因为不符合实际敲架子鼓的情况,嗵嗵鼓一般是两只手同时敲,此时不可能再让踩镲发出声音。
  • 在使用乐器的时候,应该尽可能贴合现实情况,因为听众们已经听了很多年音乐了,对各种乐器其实有一种惯性,如果乐器脱离现实,那么可能对导致听起来很怪的情况发生。

军鼓

  • 主要作用是与底鼓形成稳定互动,来协调主节奏,频段居中,属于中频鼓。
  • 军鼓下表面有一个响弦,与上表面产生共振,使音色更加响亮,下方的响弦可以通过响弦离合器调整以改变音色。
  • 有敲击鼓面和敲击鼓边两种敲法。

踩镲

  1. 闭合敲击(Closed Hit)- 闭镲:这是最常见的踩镲敲击方法。脚踩住踩镲踏板,使两块钹紧密闭合,然后用鼓棒敲击闭合的钹面。这种方法产生的声音干净、尖锐,常用于各种音乐风格中。
  2. 开放敲击(Open Hit)- 开镲:在这种敲法中,鼓手在敲击钹的同时,稍微松开踩镲的踏板,让两块钹略微分开,产生更加持久和共鸣的声音。开放敲击通常用于添加音乐的动态和变化。
  3. 半开放敲击(Semi-Open Hit)- 半开镲:这是介于开放敲击和闭合敲击之间的一种方式。钹面只是部分分开,产生的声音比闭合敲击稍微开放一些,但又不像完全开放敲击那样有很强的共鸣和持续性。
  4. 脚踏(Foot Splash)- 踏击踩镲:这种技巧涉及迅速并强烈地踩下踩镲踏板,然后迅速松开,使钹片相撞并迅速分开,产生“溅水”般的声音。这种技巧常用于添加特殊的音效或强调某些节奏。

  5. 低音大鼓-军鼓-踩镲分别构成了歌曲中的低频中频高频部分,共同构成了歌曲的主节奏框架。

吊镲

  • 吊镲一般是用鼓棒的棒身直接敲击镲的边缘,使其声音非常有爆发力,一般用在情绪爆发的位置,例如主歌进入副歌的位置,或者加花过渡的位置。

image-20240118141047433

叮叮镲

  • 叮叮镲一般是用鼓棒头部敲击镲面,氛围感很强。

image-20240118142055312

  • 还有一种敲法是用鼓棒点叮叮镲接近中心固定点的区域,延音较少,音头相对较脆亮。
  • 因为叮叮镲用于副歌连点提供氛围,如果发现由于高频,氛围感过强,有些吵的时候,就可以两种敲法交替进行,稀释高频,让其听起来不那么吵。
  • 镲面的面积越小,音高越高,而叮叮镲是面积比较大的镲,所以其音高比较低,比较浑厚。

鼓棒互相敲击

  • 鼓棒相互敲击也可以作为架子鼓的组成成分。
  • 敲击鼓边演奏的部分可以替换为敲击鼓棒来演奏,效果说不定也很不错。

架子鼓常用节奏型

阿舜老师的鼓组键位表

鼓组键位表

只要用到了架子鼓,那么在音乐平稳进行的过程中,强拍上一般必有底鼓。在特殊加花和局部变化的位置不一定要有底鼓,因为这些位置可能出现底鼓的切分节奏(切分节奏就是底鼓不出现在强拍上)。

除了强拍以外,底鼓其他的出现位置就奠定了歌曲不同风格不同类型的节奏型。

常用节奏型示例:

image-20240126143329312

复合拍

复合拍的特点是每个拍子可以被均等地划分为三个更小的单元。例如现在我们的曲子是4/4拍的(即以四分音符为一拍,每小节四拍),我们在每一拍中,再细分3小拍,那么这就是4/4拍复合3/4拍。

我们常见的6/8拍,其实就是2/4拍复合3/4拍。

在我们编配复合拍的架子鼓时,主套鼓(底鼓和军鼓)框架基本还是按照大的节拍类型的一个小节为单位来打的(即强拍底鼓弱拍军鼓),复合的小节拍类型只是影响了个别的底鼓和踩镲这类细分的音符所在的位置。

4/4拍复合4/3拍:

image-20240125142153903

常见符合拍:

image-20240126133650361

如何在Cubase中将每一拍分成3个更小的单元?

image-20240126133839087

注意,复合拍与混合拍不一样,混合拍是不同节拍类型在同一首曲子中混用。

具体节奏型

image-20240126135901320

上面这个将踩镲划分为八分音符来打,还可以划分成十六分音符来打,如下图所示:

image-20240126140056404

具体风格要看我们的曲子对节奏紧凑程度的实际要求。

我们知道踩镲有四种音色,这四种音色都可以作为踩镲平稳进行的打击,但是要注意,镲片的打开程度越大,延音越长,造成的节奏气氛就越强,因此要根据曲子实际情况进行编配。

完全用开镲和叮叮镲的功能就类似了,一般是在副歌高潮部分,主歌一般不会这么打,因为气氛不需要过强。

另外闭镲不断敲击的过程中,个别位置也会用开镲来调节节奏的气氛,使其不再那么沉闷。

image-20240126141017477

常用节奏型资源汇总

我们在编曲过程中,可以复用很多的常用节奏型,即节奏型的最佳实践,这里汇总了一些常用的节奏型,我们可以根据曲风的不同需求,来尝试和选择不同的节奏型进行搭配。

另外要注意的是,这些节奏型的mid文件需要放到对应音源插件的midi轨道上,否则无法正常播放。

常用节奏型网盘链接:常用节奏型

补充

鼓边替代军鼓

很多时候我们使用鼓边替代军鼓,形成一种比军鼓稍弱的架子鼓形式,这样子就可以在切换军鼓后起到一种强调和凸显的作用,使气氛的增强更加明显。

闭镲和半开镲交替出现

很多时候我们使闭镲和开镲交替出现,构成高频处的稳定节奏,同时我们可以将开镲点缀其中,形成一种过渡的感觉。

开镲连续演奏

我们在气氛更强的位置可以用全部用开镲进行连续演奏,形成一种更强的气势,比如在主歌中用闭镲和半开镲,进入副歌后全部用开镲,气势瞬间达到最高点。

开镲替换叮叮镲

叮叮镲与开镲的作用是比较相似的,气氛也是比较浓重的,因此叮叮镲和开镲是可以相互替换的,但是由于叮叮镲和开镲的音色其实不太一样,所以具体用开镲还是叮叮镲,这个要因歌而异,哪种听起来更和谐就使用哪种。

增强气势的方法

比如我们要让副歌的气势凸显出来,营造一种更强的气氛和情感基调,我们可以让主歌部分使用鼓边代替军鼓,并使用闭镲和半开镲,到达副歌部分之后,我们将鼓边换成军鼓,然后全部使用开镲或叮叮镲。如此一来副歌的气势与主歌形成鲜明的对比,让人能够更加直观的感受到副歌的气势以及强烈的情感,本质上是一种先抑后扬的方法。

image-20240204171940200

吊镲的作用

吊镲可以在转折的位置起到一个气氛爆发的效果。

滚镲

由弱到强又自然衰弱的音效,适合用在过渡位置,起到一个情绪缓冲和转折的效果。

电鼓及其他打击乐器

电鼓特点

  • 电鼓的打击密度一般比架子鼓更密一些,因为其延音较短。(非必须)

  • 如果觉得电鼓某部分音色比较薄,气氛比较薄弱,可以多垫上几层让其音色变厚重,如下图所示:

image-20240207173824033

DJ鼓

DJ鼓是一种打法,一般使用电鼓来打。

实际上就是“动次打次”那种类型的编排方式,一般在乐曲平稳进行的每一拍上都有底鼓,开镲一般出现在每一拍反拍位置(即后半拍),所以会产生一个“次”的音效,与每一拍的底鼓连在一起,就产生了“动次打次”的效果。

DJ或电子音乐过渡位置经常会用类似滚镲的音效。

其他打击乐器

一下这些统称为小打,可以点缀于主套鼓之中,起到增添气氛的作用。

手鼓

可将其穿插于架子鼓之间,丰富音色。

image-20240205205857641

民乐套鼓

image-20240205210005176

风铃

可以产生“哗啦啦”的声音,常用在过渡或情绪点缀的位置。

image-20240205210038939

三角铁

用于高频处的点缀,效果很不错。

image-20240205210132691

沙锤

image-20240205210206520

KONTAKT打击类音色推荐

架子鼓推荐

image-20240205210525478

电鼓推荐

image-20240205210736447

贝司

贝司主要负责低音声部,在音乐里面起到十分关键的作用,基本所有流行音乐里面都会添加贝司这件乐器。

对于整个音乐的节奏来说,噪音体系中最重要的就是底鼓,而乐音体系中最重要的就是贝司。

结构

image-20240212144813314

注意事项

  • 每个小节的第一个音,一般都是我们和弦的低音(低音一般选择与和弦根音相同音级的音,用于铺底,同时可以有一些特殊的变化,详见乐理基础
  • 节奏方面,贝司的主节奏一般会与主套鼓中低频鼓(如架子鼓的低音大鼓)的主节奏重叠在一起,形成并行的状态,对主节奏起着决定性的作用。(注意这里突出标记了主节奏,因为在不影响主节奏的情况下,贝司是可以有一些变化的)
  • 贝司要以底鼓为基础进行编写,一般是现有底鼓再有贝司,是用贝司来适应已有的底鼓。
  • 贝司不仅仅能演奏低音,还可以演奏中音,甚至是十分华丽的旋律也是可以的。

image-20240215195926542

  • 贝司还有一种演奏方式叫Slap,Slap技术主要用于电贝司,它通过以手指(通常是拇指)强力击打弦来产生一种特有的节奏性和打击感。由于带有一定的打击感,所以其音头很强,比较适合旋律的编配。

木贝司

  • 木贝司比电贝司音头要弱一些,整体上来说没有电贝司那么强烈,因此其可以在主歌部分配器较弱的时候使用木贝司,当进入副歌的时候替换成电贝司,达到一个更加强烈的听觉效果。
  • 大提琴和低音大提琴如果不用弓去拉它,而是用手指去拨它,那么它就可以替代音乐当中的木贝司,因为其音色比较接近。

合成贝司

image-20240215200949828

因为合成贝司是模拟乐器,所以其没有音高限制,可以出现在任何音高区域,尤其在电音类歌曲中非常常见。

KONTAKT贝司类音色推荐

image-20240215201602036

钢琴

image-20240219135943610

image-20240219135959258

钢琴共有52个白键,36个黑键。

image-20240219140146181

弱音踏板使音色更柔和;延音踏板会使琴的声音自然衰减,如果不踩延音踏板,手指松开的那一刻声音就会停止;中间踏板在立式钢琴起到减小音量的作用,在三角钢琴中起到选择性延音的作用(一般没用)。

The post 乐器法 first appeared on 东篱blog.

高数学习总纲

作者东篱
2024年1月15日 17:04

本文主要对高数总体的大纲以及一些要点和学习方法进行总结,为学习高数提供一个更加宏观的帮助。

高数内容总纲

image-20240115153526585

  • 一元微积分是重点难点和基础,学会了一元微积分,多元也就不难了

章节导航

为了高数相关文章的连贯性,这里给出了大纲中所有笔记的导航目录,点击下方链接就可以进入对应章节。

The post 高数学习总纲 first appeared on 东篱blog.

数据结构基础

作者东篱
2024年1月15日 17:04

程序=数据结构+算法(物体结构+物体行为),数据结构是数字世界模拟现实世界的基础,是一切程序的地基。

本篇文章主要是将数据结构的基础内容过一遍,查漏补缺的同时为考研408做准备。

绪论

信息化世界的组成

image-20240115171153068

  • 由此可见,【计算机组成原理、操作系统、数据结构、计算机网络】共同组成了我们的信息化世界。

数据结构的基本概念

数据

image-20240115171659686

数据元素和数据项

image-20240115172218576

数据对象

image-20240115172523889

数据结构的三要素

image-20240115174231014

物理存储结构

  • 线性存储

image-20240115173526627

  • 链式存储

image-20240115173611010

  • 索引存储

image-20240115173648386

  • 散列存储

image-20240115173817887

数据类型和抽象数据类型

image-20240115174651170

数据结构基本概念总结

image-20240115174840572

算法

image-20240115175838786

时间复杂度

image-20240115181207410

空间复杂度

递归调用算法空间复杂度的示例

image-20240116193219476

总结

image-20240116193314210

线性表

线性表的定义

image-20240116193808199

线性表的基本操作

image-20240116194350121

总结

image-20240116194727459

顺序表

定义

image-20240116200012696

总结

image-20240116201240871

基本操作

image-20240117215756926

image-20240119180511250

链表

image-20240119182535106

注意上述红框中的内容,LinkListLNode*实际上是一样的东西,但是含义有区别。

单链表的定义

image-20240119182831295

单链表的基本操作

指定节点前插

巧妙方法:指定节点前插操作,除了通过遍历找到该节点的前一个节点之外,还有一种更快速的实现方法,就是在指定节点后面插入新节点,然后将新节点与指定节点的数据域互换。

image-20240119193848646

删除指定节点

巧妙方法:与上面说的指定节点前插的方法异曲同工,详细步骤见下图。不过这段代码有Bug,因为如果p结点是最后一个节点的话,p->next->data会发生异常。

image-20240119194421354

插入操作总结

image-20240119194921433

查找操作总结

image-20240121220717648

单链表的建立

尾插法:

image-20240122180913068

头插法:

image-20240122181051759

双链表

image-20240122182041692

循环链表

image-20240122192517784

静态链表

image-20240122192742890

image-20240122193640457

基本概念

image-20240123203537196

栈的顺序存储实现

image-20240124213256269

image-20240124214812219

image-20240124214902399

image-20240124215024321

image-20240124215128910

总结

image-20240124215159869

栈的链式存储实现

image-20240125180609417

队列

基本概念

image-20240125181239461

队列的顺序存储实现

image-20240125194305735

队列的链式存储实现

image-20240125195241669

双端队列

image-20240126180247127

image-20240126191540070

总结

image-20240126195052447

栈的应用

括号匹配算法

image-20240127221255124

实现

image-20240127221530180

总结

image-20240127221727019

表达式求值

表达式详解

中缀、后缀、前缀表达式

其实就是树的三种遍历顺序。

image-20240128203935184

中缀转后缀

image-20240128204540303

后缀表达式计算

image-20240128210228967

中缀转前缀

image-20240128210856898

总结

image-20240128211044670

使用栈进行表达式求值

中缀转后缀(机算)

image-20240129202031866

中缀表达式计算

image-20240129203140830

总结

image-20240129203637720

栈在递归中的应用

image-20240130204803805

image-20240130205038327

队列的应用

树的层序遍历

image-20240131213040551

图的广度优先遍历

image-20240131213115651

CPU先到先服务

image-20240131213153113

缓冲区队列

电脑是快速设备,打印机是慢速设配,通过缓冲区队列解决快速设备和慢速设备之间的速度不匹配问题。

image-20240131213314830

特殊矩阵的压缩存储

二维数组的存储结构

image-20240201202233149

行优先存储计算方法

image-20240201202438882

列优先存储计算方法

image-20240201202601061

对称矩阵的压缩存储

image-20240201202905898

三角矩阵压缩存储

image-20240201203216049

与对称矩阵的存储方式基本一致,只需要多加一个常量存储位置即可。

三对角矩阵的压缩存储

image-20240201204033983

稀疏矩阵的压缩存储

使用三元组

image-20240201204217293

使用三元组有个缺点,就是会使其失去随机存取的特性,每次找数据都要遍历所有三元组。

十字链表法

image-20240201204427887

总结

image-20240201204449274

定义

image-20240203202801242

基本操作

image-20240203203126288

总结

image-20240203203809505

串的存储结构

串的顺序存储

image-20240204202843658

串的链式存储

image-20240204203429880

总结

image-20240204203922364

字符串模式匹配

image-20240205213638721

朴素模式匹配算法

image-20240205213858356

image-20240205213924407

image-20240205214141787

image-20240205214448175

总结

image-20240205214503191

KMP算法

image-20240208203904027

其实就是先对模式串进行处理,找到模式串中重复的部分,比如我们已经匹配到了第五个字母,说明前四个字母goog在主串与模式串中是一样的我们会发现第四个字母与前面是存在重复部分的,即字母g,因此当我们匹配到第五个字母的时候,我们知道主串中在匹配第四个字母的时候已经有一个g了,就不需要再比一次了,所以模式串的指针直接从第二个字母开始比较。

换句话说,当我们匹配到第五个字符时,如果发现不匹配,根据部分匹配表,我们可以知道“goog”(前四个字符)中有多少字符是重复的前缀。在这个例子中,“g”是一个重复的前缀(在第一位和第四位)。如果第五个字符不匹配,我们可以将模式串移动,使模式串的第二个字符与主串中当前位置的字符对齐,而不是重新从“google”的第一个字符开始匹配。

next数组求法

image-20240212204840569

image-20240212205435760

image-20240212205733321

image-20240212205923301

KMP算法总结

image-20240216211915597

next数组的进一步优化

image-20240217171320999

image-20240217171640757

树与二叉树

基本概念

image-20240218195431050

结点、树的属性描述

image-20240218200825151

有序树和无序树

image-20240218200942066

森林

image-20240218201034901

总结

image-20240218201127761

常考性质

image-20240219164304075

image-20240219163114522

image-20240219164216761

image-20240219164152594

image-20240219165337480

image-20240219165900275

总结

image-20240219165922096

二叉树

image-20240219171529120

image-20240219171542108

几个特殊的二叉树

image-20240219183118151

image-20240219195658844

image-20240219195750938

总结

image-20240219195829251

The post 数据结构基础 first appeared on 东篱blog.

函数 极限 连续

作者东篱
2024年1月15日 17:04

函数是高数研究的对象,而极限是研究函数的工具,而本章将通过极限这个工具,研究函数的连续性。

函数

课程要点

image-20240115155616524

函数的概念

image-20240115155638255

  • 因为定义域和对应法则有了,值域也就确定了。

下面是一些常用的函数

image-20240115155737487

复合函数

内层函数值域和外层函数定义域的交集不为空才能复合

image-20240115160337684

例题

image-20240117212243862

反函数

image-20240115160736143

  • 单调函数一定有反函数,但是有反函数不一定单调。
  • 有反函数的充要条件是,定义域内任取两个不相等的数,他们所对应y值也一定不相等,即f是定义域到值域的一一映射。

image-20240115162242644

  • 注意,反函数x和y位置互换的两种写法虽然没有问题,但是其图像是不一样的,是关于x=y对称的,详见第三题。

  • 第四题是映射过去又映射回来了,所以答案是x不变。

image-20240115162725473

  • 上面这个例三讲解了如何求反函数,其实就是将x用y表示出来,我们可以发现式子里面有个e的x次方,我们先将e的x次方用y表示出来,剩下的工作就简单了。
  • 具体的解题步骤如下:

image-20240115164053085

初等函数

image-20240116173310750

Capture_20240116_180048

函数的性质

单调性和奇偶性

image-20240116174404541

Capture_20240116_180131

周期性

image-20240116181810770

有界性

Capture_20240116_183500

Capture_20240117_204000

奇偶性

加法

  • 偶函数+偶函数=偶函数
  • 奇函数+奇函数=奇函数
  • 偶函数+奇函数=非奇非偶函数

乘法

  • 偶函数 × 偶函数 = 偶函数
  • 奇函数 × 奇函数 = 偶函数
  • 偶函数 × 奇函数 = 奇函数

极限

课程要点

image-20240117212522035

image-20240117212938433

数列的极限

概念

微信图片_20240118203410

Capture_20240119_165513

image-20240119170121516

  • 因为该函数奇数列极限=偶数列极限=1,所以原数列极限也等于1

Capture_20240119_172823

常用基本结论

image-20240119173251870

证明:

Capture_20240121_203617

  • 后面很多地方会用到第二条结论,因为取绝对值之后就没有正负号的干扰,进行一些放缩操作就更加容易了。

函数的极限

自变量趋于无穷大

image-20240122162838977

  • 注意,函数极限可以推出数列极限,而数列极限不能推出函数极限(一般可以推特殊,而特殊不能推一般)。
  • 我们有时候求数列极限,就是先求函数极限,然后由此推出数列极限。

Capture_20240122_164947

Capture_20240122_170902

自变量趋于有限值

image-20240123191802871

Capture_20240123_191615

左极限与右极限

image-20240124193056261

image-20240124200401375

典型例题

image-20240124201131861

该例子中很明显考察了左右极限问题的第二条,这种一定要分左右极限来单独讨论。

image-20240124202702447

image-20240124202934495

极限的性质

有界性

注意下面这两条反过来都不成立,证明其不成立,举反例即可,我们学习过程中应该积累一些常用的反例。

我单开了一篇文章专门整理反例和常见错误,详见:高数常用反例及经典错误

image-20240125161502710

保号性

  • 极限值的正负保数列项的正负(不带等号)
  • 数列项的正负保极限值的正负(带等号)

不管是数列还是函数,一定要记住,极限值保数列(函数)项不加等号,数列(函数)项保极限值加等号。

image-20240126171716356

Capture_20240126_173149

经典例题

Capture_20240127_210424

也可以用排除法来解这道题,题目中出现一般函数时,我们就可以使用排除法,何为一般函数?其实就是只告诉哦我们 f 满足什么条件,但没有给出 f 的表达式。比如这道题只知道极限等于-1,但是不知道 f 是啥。

换句话说,一般函数就是一般普适情况下的函数,而非某种具体表达式的特殊情况。

那么出现一般函数的情况下我们该如何使用排除法呢?实际上我们需要找一些特殊函数(具体函数),证明三个选项是错的,那么就能找到正确选项了。

image-20240128183505023

极限值与无穷小的关系

image-20240128183830184

注意,三条性质中最重要的是保号性。

极限存在准则

image-20240129185647512

夹逼准则

image-20240129185902977

image-20240129185933293

单调有界准则

Capture_20240130_203923

无穷小量

image-20240131173500900

例题

Capture_20240131_183205

无穷小的性质

image-20240201200518300

无穷大量

概念

image-20240203194410299

常用无穷大量的比较

无穷大量的比较是相当有用的,通过无穷大量的比较,很多题我们根据函数类型就能直接得出结论。

image-20240203201726611

该例题中f,g,h分别为对数,幂,指数,通过无穷大量比较即可得出答案为C。

无穷大量的性质

image-20240204193010357

无穷大量与无界变量的关系

image-20240204193931768

无穷大量与无穷小量的关系

image-20240206205854449

极限内容总结

  • 概念
  • 性质
  • 存在准则
  • 无穷小
  • 无穷大

The post 函数 极限 连续 first appeared on 东篱blog.

❌