<?xml version='1.0' encoding='UTF-8'?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><channel><title>jasper</title><link>https://iamdurant.github.io</link><description>make programming great again</description><copyright>jasper</copyright><docs>http://www.rssboard.org/rss-specification</docs><generator>python-feedgen</generator><image><url>https://assets.leetcode.cn/aliyun-lc-upload/users/tender-satoshino0/avatar_1730782842.png?x-oss-process=image%2Fformat%2Cwebp</url><title>avatar</title><link>https://iamdurant.github.io</link></image><lastBuildDate>Sat, 28 Mar 2026 21:03:33 +0000</lastBuildDate><managingEditor>jasper</managingEditor><ttl>60</ttl><webMaster>jasper</webMaster><item><title>拓扑排序</title><link>https://iamdurant.github.io/post/tuo-pu-pai-xu.html</link><description>[原理](https://jingsam.github.io/2020/08/11/topological-sort.html)

##### kahn实现

- 采用入度，以循环迭代方式实现

##### 算法逻辑

- 使用队列记录indegree为0的节点
- 依次出队，遍历更新其outdegree节点，若其indegree为0,则入队
- 最后，若存在环，则idx &lt; n

```java
int[] topoSort(int n, int[][] edges) {
    List&lt;List&lt;Integer&gt;&gt; graph = new ArrayList&lt;&gt;();
    for(int i = 0;i &lt; n;i++) {
        graph.add(new ArrayList&lt;&gt;());
    }

    int[] indegree = new int[n];
    for(int[] e : edges) {
        graph.get(e[0]).add(e[1]);
        indegree[e[1]]++;
    }

    Queue&lt;Integer&gt; q = new ArrayDeque&lt;&gt;();
    for(int i = 0;i &lt; n;i++) {
        if(indegree[i] == 0) q.offer(i);
    }

    int[] ans = new int[n];
    int idx = 0;
    while(!q.isEmpty()) {
        int c = q.poll();
        ans[idx++] = c;
        for(int next : graph.get(c)) {
            indegree[next]--;
            if(indegree[next] == 0) q.offer(next);
        }
    }

    if(idx &lt; n) return new int[]{};
    return ans;
}
```



##### dfs实现(未学)

- 采用出度，以递归实现

```java
```

。</description><guid isPermaLink="true">https://iamdurant.github.io/post/tuo-pu-pai-xu.html</guid><pubDate>Sat, 28 Mar 2026 21:03:05 +0000</pubDate></item><item><title>辗转相除法</title><link>https://iamdurant.github.io/post/zhan-zhuan-xiang-chu-fa.html</link><description>### 算法步骤: (Know-How)

1. 设有两个正整数a和b，且a &gt; b。</description><guid isPermaLink="true">https://iamdurant.github.io/post/zhan-zhuan-xiang-chu-fa.html</guid><pubDate>Fri, 01 Aug 2025 09:31:02 +0000</pubDate></item><item><title>mysql主从配置</title><link>https://iamdurant.github.io/post/mysql-zhu-cong-pei-zhi.html</link><description>##### 一主多从

主库my.cnf配置:

```mysql
[mysqld]
bind-address = 0.0.0.0
port = 3306

server-id = 1
log-bin = mysql-bin

binlog-do-db = sharding_jdbc_demo
binlog-do-db = sharding_jdbc_demo2
binlog-do-db = sharding_jdbc_userdb
```



从库my.cnf配置

```sql
[mysqld]
# 基础配置
basedir=/home/jasper/桌面/apps/mysql/mysql-8.0.42-linux-glibc2.28-x86_64         # 修改为你的MySQL解压目录（即bin的上级）
datadir=/home/jasper/桌面/apps/mysql/mysql-8.0.42-linux-glibc2.28-x86_64/mysql_data
socket=/home/jasper/桌面/apps/mysql/mysql-8.0.42-linux-glibc2.28-x86_64/mysql.sock
pid-file=/home/jasper/桌面/apps/mysql/mysql-8.0.42-linux-glibc2.28-x86_64/mysql.pid
bind-address = 0.0.0.0
port=3307                           # 避免与主库冲突

# 日志配置
log-error=/home/jasper/桌面/apps/mysql/mysql-8.0.42-linux-glibc2.28-x86_64/mysql.err
log-bin=/home/jasper/桌面/apps/mysql/mysql-8.0.42-linux-glibc2.28-x86_64/mysql-bin.log

# server id 必须唯一
server-id=2

# 复制配置
relay-log=/home/jasper/桌面/apps/mysql/mysql-8.0.42-linux-glibc2.28-x86_64/mysql-relay-bin
read_only=1

# 只复制主库中的某个数据库（可选）
replicate-do-db=sharding_jdbc_demo
replicate-do-db=sharding_jdbc_demo2
replicate-do-db=sharding_jdbc_userdb
```



初始化从库

```shell
./mysqld --defaults-file=./my.cnf --initialize-insecure
```

&gt; **error:**
&gt;
&gt; error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory
&gt;
&gt; **解决:**
&gt;
&gt; 安装并创建软链接
&gt;
&gt; ```shell
&gt; sudo apt install libaio-dev
&gt; ldconfig -p | grep libaio # 找到此lib
&gt; sudo ln -s /lib/x86_64-linux-gnu/libaio.so.1t64 /lib/x86_64-linux-gnu/libaio.so.1
&gt; ```
&gt;
&gt; 

启动从库

```shell
./mysqld_safe --defaults-file=./my.cnf
```

配置master信息并启动replica

```sql
# 在主库中获取LOG_File以及LOG_POS
# 低版本: show master status;
# 高版本: show binary log status;

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='127.0.0.1',
  SOURCE_PORT=3306,
  SOURCE_USER='root',
  SOURCE_PASSWORD='pubgM666',
  SOURCE_LOG_FILE='mysql-bin.000006',
  SOURCE_LOG_POS=158;
  
start replica;
```

。</description><guid isPermaLink="true">https://iamdurant.github.io/post/mysql-zhu-cong-pei-zhi.html</guid><pubDate>Fri, 09 May 2025 22:21:16 +0000</pubDate></item><item><title>sharding-jdbc分库分表</title><link>https://iamdurant.github.io/post/sharding-jdbc-fen-ku-fen-biao.html</link><description>##### 分库分表

- 垂直分表

  &gt; 将频繁访问的属性与访问率较低的属性,拆分到两张表

- 垂直分库

  &gt; 将不相关的业务实体,拆分到两个数据库

- 水平分库

  &gt; 将表数据划分到不同的数据库

- 水平分表

  &gt; 将表水平拆分成多个表



#####  sharding-jdbc-sql执行过程

1. 解析
2. 改写
3. 路由
4. 执行
5. 归并



##### 水平分表demo

**水平分表**

`sql`准备,创建两张表: `t_order_1`,`t_order_2`

```sql
create table t_order_2(
	`id` bigint primary key comment '订单id',
    `product_id` bigint not null comment '商品id',
    `amount` int not null comment '购买数量',
    `buyer_id` bigint not null comment '购买者id',
    `create_date` datetime not null comment '下单时间',
    `status` int not null default 0 comment '订单状态 0: 未支付, 1: 已支付, 2: 超时, 3: 取消, 4: 其他',
    `pay_time` datetime null comment '支付时间'
) engine=innodb default charset=utf8mb4 comment='订单表2';
```

`maven`依赖

```xml
&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;version&gt;2.7.15&lt;/version&gt;
        &lt;exclusions&gt;
            &lt;exclusion&gt;
                &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
                &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
            &lt;/exclusion&gt;
            &lt;exclusion&gt;
                &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                &lt;artifactId&gt;spring-boot-starter-logging&lt;/artifactId&gt;
            &lt;/exclusion&gt;
        &lt;/exclusions&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
        &lt;artifactId&gt;lombok&lt;/artifactId&gt;
        &lt;version&gt;1.18.34&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;mysql&lt;/groupId&gt;
        &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
        &lt;version&gt;8.0.30&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;com.baomidou&lt;/groupId&gt;
        &lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;
        &lt;version&gt;3.5.5&lt;/version&gt;
        &lt;exclusions&gt;
            &lt;exclusion&gt;
                &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
                &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
            &lt;/exclusion&gt;
        &lt;/exclusions&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;com.github.xiaoymin&lt;/groupId&gt;
        &lt;artifactId&gt;knife4j-openapi2-spring-boot-starter&lt;/artifactId&gt;
        &lt;version&gt;4.2.0&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.apache.shardingsphere&lt;/groupId&gt;
        &lt;artifactId&gt;sharding-jdbc-spring-boot-starter&lt;/artifactId&gt;
        &lt;version&gt;4.1.1&lt;/version&gt;
    &lt;/dependency&gt;


    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-logging&lt;/artifactId&gt;
        &lt;version&gt;2.7.15&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;properties&gt;
    &lt;maven.compiler.source&gt;17&lt;/maven.compiler.source&gt;
    &lt;maven.compiler.target&gt;17&lt;/maven.compiler.target&gt;
    &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
&lt;/properties&gt;
```

`yaml`配置

```yaml
server:
  port: 8888

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true

spring:
  # sharding-jdbc 配置
  shardingsphere:
    datasource:
      names: m1
      m1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/sharding_jdbc_demo?serverTimezone=UTC
        username: root
        password: pubgM666
    sharding:
      tables:
        t_order:
          actual-data-nodes: m1.t_order_$-&gt;{1..2}
          table-strategy:
            inline:
              sharding-column: id
              algorithm-expression: t_order_$-&gt;{id % 2 + 1}
          key-generator:
            column: id
            type: SNOWFLAKE
            props:
              worker:
                id: 100
              max:
                tolerate:
                  time:
                    difference:
                      milliseconds: 20
```

**细节**

1. 雪花算法生成的字段,在insert时,需要自定义`sql`,将次字段从`sql`插入字段中去除:

   &gt; 比如:
   &gt;
   &gt; 定义id字段为雪花算法自动生成,则`insert sql`为:
   &gt;
   &gt; ```java
   &gt; @Insert('insert into t_order(product_id, amount, buyer_id, create_date, status, pay_time) values(#{productId}, #{amount}, #{buyerId}, #{createDate}, #{status}, #{payTime})')
   &gt;  int insert(TOrder tOrder);
   &gt; ```

2. 依赖不能导错,`sharding-jdbc`依赖:

   ```xml
   &lt;dependency&gt;
       &lt;groupId&gt;org.apache.shardingsphere&lt;/groupId&gt;
       &lt;artifactId&gt;sharding-jdbc-spring-boot-starter&lt;/artifactId&gt;
       &lt;version&gt;4.1.1&lt;/version&gt;
   &lt;/dependency&gt;
   ```


###### 碰到的bug

1. `sharding-jdbc-spring-boot-starter`,无法解析`LocalDateTime`以及`LocalDate`

   &gt; 修复: 
   &gt;
   &gt; - 依赖升级
   &gt;
   &gt; ```xml
   &gt; &lt;dependency&gt;
   &gt;     &lt;groupId&gt;org.apache.shardingsphere&lt;/groupId&gt;
   &gt;     &lt;artifactId&gt;shardingsphere-jdbc-core-spring-boot-starter&lt;/artifactId&gt;
   &gt;     &lt;version&gt;5.2.1&lt;/version&gt;
   &gt;     &lt;exclusions&gt;
   &gt;         &lt;exclusion&gt;
   &gt;             &lt;groupId&gt;org.yaml&lt;/groupId&gt;
   &gt;             &lt;artifactId&gt;snakeyaml&lt;/artifactId&gt;
   &gt;         &lt;/exclusion&gt;
   &gt;     &lt;/exclusions&gt;
   &gt; &lt;/dependency&gt;
   &gt; ```
   &gt;
   &gt; - `5.x.x`版本yml配置不同,更改为:
   &gt;
   &gt; ```yaml
   &gt; spring:
   &gt;   shardingsphere:
   &gt;     datasource:
   &gt;       names: m1
   &gt;       m1:
   &gt;         type: com.zaxxer.hikari.HikariDataSource
   &gt;         driver-class-name: com.mysql.cj.jdbc.Driver
   &gt;         jdbc-url: jdbc:mysql://localhost:3306/sharding_jdbc_demo?serverTimezone=UTC
   &gt;         username: root
   &gt;         password: pubgM666
   &gt; 
   &gt;     rules:
   &gt;       sharding:
   &gt;         tables:
   &gt;           t_order:
   &gt;             actual-data-nodes: m1.t_order_$-&gt;{1..2}
   &gt;             table-strategy:
   &gt;               standard:
   &gt;                 sharding-column: id
   &gt;                 sharding-algorithm-name: t_order_inline
   &gt;             key-generate-strategy:
   &gt;               column: id
   &gt;               key-generator-name: snowflake
   &gt; 
   &gt;         sharding-algorithms:
   &gt;           t_order_inline:
   &gt;             type: INLINE
   &gt;             props:
   &gt;               algorithm-expression: t_order_$-&gt;{id % 2 + 1}
   &gt; 
   &gt;         key-generators:
   &gt;           snowflake:
   &gt;             type: SNOWFLAKE
   &gt;             props:
   &gt;               worker-id: 100
   &gt;               max-tolerate-time-difference-milliseconds: 20
   &gt; 
   &gt;     props:
   &gt;       sql-show: true   # 开启SQL打印，方便调试
   &gt; ```

2. `mybatis-plus`与`sharding-jdbc`的不兼容

   &gt;  解决: `sql`都使用`mapper`方式定义,不使用`mybatis-plus`


#####  水平分库demo

**`yml`配置:**

```yaml
spring:
  shardingsphere:
    datasource:
      names: m1, m2
      m1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/sharding_jdbc_demo?serverTimezone=UTC
        username: root
        password: pubgM666
      m2:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/sharding_jdbc_demo2?serverTimezone=UTC
        username: root
        password: pubgM666

    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: m$-&gt;{1..2}.t_order_$-&gt;{1..2}
            database-strategy:
              standard:
                sharding-column: product_id
                sharding-algorithm-name: t_order_database_inline
            table-strategy:
              standard:
                sharding-column: id
                sharding-algorithm-name: t_order_inline
            key-generate-strategy:
              column: id
              key-generator-name: snowflake

        sharding-algorithms:
          t_order_inline:
            type: INLINE
            props:
              algorithm-expression: t_order_$-&gt;{id % 2 + 1}
          t_order_database_inline:
            type: INLINE
            props:
              algorithm-expression: m$-&gt;{product_id % 2 + 1}

        key-generators:
          snowflake:
            type: SNOWFLAKE
            props:
              worker-id: 100
              max-tolerate-time-difference-milliseconds: 20

    props:
      sql-show: true   # 开启SQL打印，方便调试
```



##### 垂直分库

**数据库准备**

```sql
create database sharding_jdbc_userdb;

create table `user`(
	`id` bigint auto_increment primary key comment 'id,主键',
    `username` varchar(16) not null comment '用户名',
    `encoded_password` varchar(256) not null comment '加密后的密码',
    `salt` char(16) not null comment '随机盐',
    `create_date` datetime not null comment '创建时间'
)engine=innodb default charset=utf8mb4 comment='用户表';
```



##### 广播表

若垂直分库后,部分库都需要对某个表join,并且此表数据读u多写少,可以将其作为为公告表

```sql
create table common(
	`id` bigint primary key auto_increment comment '主键',
    `name` varchar(32) null comment '名字'
)engine=innodb default charset=utf8mb4 comment='公共表';
```

**`yml`配置:**

```yaml
spring:
  shardingsphere:
    rules:
      sharding:
        # 广播表
        broadcast-tables:
          common
```

##### sharding-jdbc读写分离

&gt; `sharding-jdbc`根据`sql`语义,将写操作路由至`master`,将读操作路由至`slave`.提供透明化读写操作,像使用一个数据库一样使用主从数据库集群.
&gt;
&gt; ![image-20250429231159360](http://192.168.1.101:9000/kijjqvbsoduu/imgs/image-20250429231159431.png)

`yaml`配置

```yaml
    rules:
      readwrite-splitting:
        data-sources:
          m0_group:
            static-strategy:
              write-data-source-name: m0
              read-data-source-names:
                - s0
            load-balancer-name: round_robin
        load-balancers:
          round_robin:
            type: ROUND_ROBIN
      sharding:
        # 广播表
        broadcast-tables:
          common
        tables:
          user:
            actual-data-nodes: m0_group.user
```

##### 实践

**描述:**

以商品为例,进行实践.

###### 数据库设计

&gt;  *entity:*
&gt;
&gt;  - 商品
&gt;  - 店铺
&gt;  - 地理区域
&gt;
&gt;  *垂直分库:*
&gt;
&gt;  将商品以及店铺划分到product_db,store_db
&gt;
&gt;  *垂直分表:*
&gt;
&gt;  将商品信息划分为product_info,product_desc
&gt;
&gt;  *广播表:*
&gt;
&gt;  region
&gt;
&gt;  *水平分库:*
&gt;
&gt;  将product_db根据store_id分库
&gt;
&gt;  *水平分表:*
&gt;
&gt;  将product_info根据product_id分表

###### sql准备

product_info

```sql
create database product_db_1;
create database product_db_2;

create table product_info_1(
	`id` bigint primary key comment '商品id',
    `store_id` bigint not null comment '所属店铺id',
    `name` varchar(64) not null comment '商品名称',
    `specification` varchar(8) not null comment '规格',
    `source_place` varchar(16) null comment '产地',
    `price` decimal not null comment '价格',
    `pic_url` varchar(200) not null comment '图片url'
) engine=innodb default charset=utf8mb4 comment='商品信息表';
```

product_desc

```sql
create table product_desc_1(
	`id` bigint auto_increment primary key comment 'id',
    `product_id` bigint not null comment '商品id',
    `store_id` bigint not null comment '店铺id',
    `desc` text not null comment '商品描述'
) engine=innodb default charset=utf8mb4 comment '商品描述表';
```

store

```sql
create databse store_db;

create table store(
	`id` bigint primary key auto_increment comment '店铺id',
    `name` varchar(32) not null comment '店铺名称',
    `score` float null default 0 comment '店铺评分',
    `source_place_id` bigint not null comment '店铺所在地'
) engine=innodb default charset=utf8mb4 comment '店铺信息表';
```

region

```sql
create table region(
	`id` bigint primary key auto_increment comment 'id',
    `code` bigint not null comment '地域编码',
    `name` varchar(32) not null comment '地理区域名称',
    `level` int not null comment '级别,省:1 市:2 县:3 镇:4',
    `parent` bigint null comment '上级地理区域'
) engine=innodb default charset=utf8mb4 comment '地域信息表';
```

。</description><guid isPermaLink="true">https://iamdurant.github.io/post/sharding-jdbc-fen-ku-fen-biao.html</guid><pubDate>Fri, 09 May 2025 21:52:35 +0000</pubDate></item><item><title>LIS优化(nlogn)</title><link>https://iamdurant.github.io/post/LIS-you-hua-%28nlogn%29.html</link><description>**示例：**$[3,1,5,6,4,2]$

**定义：**$f[i]$定义为长度为$i$的子序列右端点最小值

**模拟：**

$i=0$：

&gt; $f[1]$ = 3

$i=1$：

&gt; $f[1]=1$

$i=2$：

&gt; $f[1]=1$，$f[2]=5$

$i=3$：

&gt; $f[1]=1$，$f[2]=5$，$f[3]=6$

$i=4$：

&gt; $f[1]=1$，$f[2]=4$，$f[3]=6$

$i=5$：

&gt; $f[1]=1$，$f[2]=2$，$f[3]=6$

**疑惑：**

- 为什么是$f[i]$是递增的？

  &gt; 观察模拟过程，贪心地维护$f[i]$的最小值，
  &gt;
  &gt; 考虑$nums[i]$，若$nums[i]&gt;f[k]$ 且$nums[i]&lt;f[k+1]$，
  &gt;
  &gt; 则可以替换$f[k+1]$为$nums[i]$，
  &gt;
  &gt; 因为$f[k+1]$是基于$f[k]$构建的长度为$k+1$的序列。</description><guid isPermaLink="true">https://iamdurant.github.io/post/LIS-you-hua-%28nlogn%29.html</guid><pubDate>Thu, 03 Apr 2025 21:06:48 +0000</pubDate></item><item><title>素数判定</title><link>https://iamdurant.github.io/post/su-shu-pan-ding.html</link><description>**四大判定法**
![Image](https://github.com/user-attachments/assets/e2849b70-739e-46b1-90df-16eb1d9dfecd)

1. 试除法

   ```java
   boolean prime(int v) {
       if(v &lt; 2) return false;
       if(v == 2) return true;
       if(v % 2 == 0) return false;
       for(int i = 3;i * i &lt;= v;i += 2) {
           if(v % i == 0) return false;
       }
       return true;
   }
   ```

2. 埃氏筛

   **[[详解](https://zh.wikipedia.org/wiki/%E5%9F%83%E6%8B%89%E6%89%98%E6%96%AF%E7%89%B9%E5%B0%BC%E7%AD%9B%E6%B3%95)](https://zh.wikipedia.org/wiki/%E5%9F%83%E6%8B%89%E6%89%98%E6%96%AF%E7%89%B9%E5%B0%BC%E7%AD%9B%E6%B3%95)**

   ```java
   /***
    * 
    * @param n 求[2 - n]范围内的素数
    * @return boolean数组，false则为素数
    */
   boolean[] eratosthenes(int n) {
       int g = (int) Math.sqrt(n);
       boolean[] b = new boolean[n + 1];
       for(int i = 2;i &lt;= n;++i) {
           if(!b[i]) {
               if(i &gt; g) break;
               for(int k = i * i;k &lt;= n;k += i) {
                   b[k] = true;
               }
           }
       }
       return b;
   }
   ```

3. 欧拉筛

   ```java
   static List&lt;Integer&gt; ps = new ArrayList&lt;&gt;();
   static boolean[] b = new boolean[10001];
   static {
       b[0] = b[1] = true;
       for(int i = 2;i &lt;= 10000;i++) {
           if(!b[i]) ps.add(i);
           for(int p : ps) {
               if(i * p &gt; 10000) break;
               b[i * p] = true;
               if(i % p == 0) break;
           }
       }
   }
   ```

4. Miller-Rabin。</description><guid isPermaLink="true">https://iamdurant.github.io/post/su-shu-pan-ding.html</guid><pubDate>Tue, 18 Mar 2025 07:29:41 +0000</pubDate></item><item><title>rabbitmq_spring_amqp</title><link>https://iamdurant.github.io/post/rabbitmq_spring_amqp.html</link><description>##### 依赖

```xml
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-amqp&lt;/artifactId&gt;
    &lt;version&gt;3.2.10&lt;/version&gt;
    &lt;exclusions&gt;
        &lt;exclusion&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-logging&lt;/artifactId&gt;
        &lt;/exclusion&gt;
    &lt;/exclusions&gt;
&lt;/dependency&gt;
```

##### 如何声明队列、如何创建消费者？

**由配置类完成：**

- 声明队列，，注入容器
- 创建consumer类，@Bean注入容器

对于consumer，也可直接@component注入容器

**consumer涉及注解：**

- @RabbitListener
- @RabbitHandler

```java
@Configuration
@Profile({'tut2', 'work-queues'})
public class Tut2Config {
    @Bean
    public Queue queue() {
        return new Queue('hello_a');
    }

    @Profile({'receiver'})
    private static class ReceiverConfig {
        @Bean
        public Tut2Receiver receiver1() {
            return new Tut2Receiver(1);
        }

        @Bean
        public Tut2Receiver receiver2() {
            return new Tut2Receiver(2);
        }
    }

    @Profile({'sender'})
    @Bean
    public Tut2Sender sender() {
        return new Tut2Sender();
    }
}
```

##### 如何发送消息？

- RabbitTemplate对底层代码做了封装，可直接注入使用

- 对RabbiTemplate再做一层封装

##### fanout使用示例

- 配置类

  ```java
  @Configuration
  @Profile({'tut3', 'publish-subscribe'})
  public class Tut3Config {
      @Bean
      public FanoutExchange fanoutExchange() {
          return new FanoutExchange('tut.fanout');
      }
  
      @Profile({'receiver'})
      private static class Receiver {
          @Bean
          public Queue autoDeleteQueue1() {
              return new AnonymousQueue();
          }
  
          @Bean
          public Queue autoDeleteQueue2() {
              return new AnonymousQueue();
          }
  
          @Bean
          public Binding binding1(@Autowired @Qualifier('autoDeleteQueue1') Queue autoDeleteQueue1, FanoutExchange fanoutExchange) {
              return BindingBuilder.bind(autoDeleteQueue1).to(fanoutExchange);
          }
  
          @Bean
          public Binding binding2(@Autowired @Qualifier('autoDeleteQueue2') Queue autoDeleteQueue2, FanoutExchange fanoutExchange) {
              return BindingBuilder.bind(autoDeleteQueue2).to(fanoutExchange);
          }
  
          @Bean
          public Tut3Receiver tut3Receiver() {
              return new Tut3Receiver();
          }
      }
  
      @Profile({'sender'})
      @Bean
      public Tut3Sender tut3Sender() {
          return new Tut3Sender();
      }
  }
  ```

- producer

  ```java
  public class Tut3Sender {
      @Autowired
      private RabbitTemplate rabbit;
  
      @Autowired
      private FanoutExchange fanoutExchange;
  
      @Scheduled(fixedDelay = 1000, initialDelay = 500)
      public void send() {
          Random random = new Random();
          String message = '广播消息' + random.nextInt(Integer.MAX_VALUE);
          rabbit.convertAndSend(fanoutExchange.getName(), '', message);
      }
  }
  ```

- consumer

  ```java
  public class Tut3Receiver {
      @RabbitListener(queues = {'#{autoDeleteQueue1.name}'})
      public void receive1(String body) {
          System.out.println('worker-1消费消息：' + body);
      }
  
      @RabbitListener(queues = {'#{autoDeleteQueue2.name}'})
      public void receive2(String body) {
          System.out.println('worker-2消费消息：' + body);
      }
  }
  ```

##### direct使用示例

- 配置类

  ```java
  @Configuration
  @Profile({'tut4'})
  public class Tut4Config {
      @Bean
      public DirectExchange directExchange() {
          return new DirectExchange('tut.direct');
      }
  
      @Profile({'receiver'})
      private static class Receiver {
          @Bean
          public Queue q1() {
              return new AnonymousQueue();
          }
  
          @Bean
          public Queue q2() {
              return new AnonymousQueue();
          }
  
          @Bean
          public Binding b1(@Autowired @Qualifier('q1') AnonymousQueue q1, DirectExchange exchange) {
              return BindingBuilder.bind(q1).to(exchange).with('1');
          }
  
          @Bean
          public Binding b2(@Autowired @Qualifier('q1')AnonymousQueue q1, DirectExchange exchange) {
              return BindingBuilder.bind(q1).to(exchange).with('2');
          }
  
          @Bean
          public Binding b3(@Autowired @Qualifier('q2')AnonymousQueue q2, DirectExchange exchange) {
              return BindingBuilder.bind(q2).to(exchange).with('3');
          }
  
          @Bean
          public Binding b4(@Autowired @Qualifier('q2')AnonymousQueue q2, DirectExchange exchange) {
              return BindingBuilder.bind(q2).to(exchange).with('4');
          }
  
          @Bean
          public Tut4Receiver receiver() {
              return new Tut4Receiver();
          }
      }
  
      @Profile({'sender'})
      @Bean
      public Tut4Sender sender() {
          return new Tut4Sender();
      }
  }
  ```

- producer

  ```java
  public class Tut4Sender {
      @Autowired
      private RabbitTemplate rabbit;
  
      @Autowired
      private DirectExchange exchange;
  
      @Scheduled(fixedDelay = 1000, initialDelay = 500)
      public void send() {
          Random random = new Random();
          int v = random.nextInt(5);
          rabbit.convertAndSend(exchange.getName(),
                                String.valueOf(v),
                                v);
      }
  }
  ```

- consumer

  ```java
  public class Tut4Receiver {
      @RabbitListener(queues = {'#{q1.name}'})
      public void receive1(String body) {
          System.out.println('1-消费了消息：' + body);
      }
  
      @RabbitListener(queues = {'#{q2.name}'})
      public void receive2(String body) {
          System.out.println('2-消费了消息：' + body);
      }
  }
  ```

##### topic使用示例

- 配置类

  ```java
  @Configuration
  @Profile({'tut5'})
  public class Tut5Config {
      @Bean
      public TopicExchange topicExchange() {
          return new TopicExchange('tut.topic_exchange');
      }
  
      @Profile({'receiver'})
      private static class Receiver {
          @Bean
          public Queue q1() {
              return new AnonymousQueue();
          }
  
          @Bean
          public Queue q2() {
              return new AnonymousQueue();
          }
  
          @Bean
          public Binding b1(@Autowired @Qualifier('q1') Queue q1, TopicExchange exchange) {
              return BindingBuilder.bind(q1).to(exchange).with('*.orange.*');
          }
  
          @Bean
          public Binding b2(@Autowired @Qualifier('q2') Queue q2, TopicExchange exchange) {
              return BindingBuilder.bind(q2).to(exchange).with('*.*.rabbit');
          }
  
          @Bean
          public Binding b3(@Autowired @Qualifier('q2') Queue q2, TopicExchange exchange) {
              return BindingBuilder.bind(q2).to(exchange).with('lazy.#');
          }
  
          @Bean
          public Tut5Receiver receiver() {
              return  new Tut5Receiver();
          }
      }
  
      @Profile({'sender'})
      @Bean
      public Tut5Sender sender() {
          return new Tut5Sender();
      }
  }
  ```

- producer

  ```java
  public class Tut5Sender {
      @Autowired
      private RabbitTemplate rabbit;
  
      @Autowired
      private TopicExchange exchange;
  
      private final String[] routingKeys = {'quick.orange.rabbit', 'lazy.orange.elephant', 'quick.orange.fox',
              'lazy.brown.fox', 'lazy.pink.rabbit', 'quick.brown.fox'};
  
      @Scheduled(fixedDelay = 2000, initialDelay = 500)
      public void send() {
          Random random = new Random();
          String routingKey = routingKeys[random.nextInt(routingKeys.length)];
          rabbit.convertAndSend(exchange.getName(), routingKey, routingKey);
      }
  }
  ```

- consumer

  ```java
  public class Tut5Receiver {
      @RabbitListener(queues = {'#{q1.name}'})
      public void receive1(String body)  {
          System.out.println('*.orange.* 消费消息：' + body);
      }
  
      @RabbitListener(queues = {'#{q2.name}'})
      public void receive2(String body)  {
          System.out.println('*.*.rabbit | lazy.# 消费消息：' + body);
      }
  }
  ```

  

。</description><guid isPermaLink="true">https://iamdurant.github.io/post/rabbitmq_spring_amqp.html</guid><pubDate>Thu, 06 Mar 2025 05:02:15 +0000</pubDate></item><item><title>rabbitmq_java</title><link>https://iamdurant.github.io/post/rabbitmq_java.html</link><description>##### rabbitmq安装教程

**环境：ubuntu**

1. 直接使用[官网](https://www.rabbitmq.com/docs/install-debian#apt-quick-start-cloudsmith)提供的一键脚本，会安装较新的erlang以及rabbit-server

2. 启用插件管理，开启web端

   ```shell
   rabbitmq-plugins enable rabbitmq_management
   ```

3. 默认web端只支持localhost访问，额外配置：

   ```shell
   # 添加用户
   rabbitmqctl add_user jasper pubgM666
   # 设置为管理员
   rabbitmqctl set_user_tags jasper administrator
   # 授予权限
   rabbitmqctl set_permissions -p / jasper '.*' '.*' '.*'
   # restart
   systemctl restart rabbitmq-server
   ```



##### tabbitmq简单使用

###### 依赖

```xml
&lt;dependency&gt;
    &lt;groupId&gt;com.rabbitmq&lt;/groupId&gt;
    &lt;artifactId&gt;amqp-client&lt;/artifactId&gt;
    &lt;version&gt;5.19.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;!-- slf4j日志门面 --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
    &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
    &lt;version&gt;2.0.11&lt;/version&gt;
&lt;/dependency&gt;

&lt;!-- slf4j内部实现 --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
    &lt;artifactId&gt;slf4j-simple&lt;/artifactId&gt;
    &lt;version&gt;2.0.11&lt;/version&gt;
&lt;/dependency&gt;
```



###### hello world

1. producer

   ```java
   package wwb.rabbitmq.producer;
   
   import com.rabbitmq.client.Channel;
   import com.rabbitmq.client.Connection;
   import com.rabbitmq.client.ConnectionFactory;
   
   import java.nio.charset.StandardCharsets;
   
   public class HelloProducer {
       public static void main(String[] args) {
           ConnectionFactory factory = new ConnectionFactory();
           factory.setHost('172.24.192.134');
           factory.setPort(5672);
           factory.setUsername('jasper');
           factory.setPassword('pubgM666');
           factory.setVirtualHost('/');
   
           try (Connection connection = factory.newConnection();
                Channel channel = connection.createChannel()) {
               channel.queueDeclare('hello', false, false, false, null);
               channel.basicPublish('', 'hello', null, '你好'.getBytes(StandardCharsets.UTF_8));
           } catch (Exception e) {
   
           }
       }
   }
   ```

   

2. consumer

   ```java
   package wwb.rabbitmq.consumer;
   
   import com.rabbitmq.client.Channel;
   import com.rabbitmq.client.Connection;
   import com.rabbitmq.client.ConnectionFactory;
   import com.rabbitmq.client.DeliverCallback;
   
   public class HelloConsumer {
       public static void main(String[] args) throws Exception{
           ConnectionFactory factory = new ConnectionFactory();
           factory.setHost('172.24.192.134');
           factory.setPort(5672);
           factory.setUsername('jasper');
           factory.setPassword('pubgM666');
           factory.setVirtualHost('/');
   
           Connection connection = factory.newConnection();
           Channel channel = connection.createChannel();
   
           channel.queueDeclare('hello', false, false, false, null);
           DeliverCallback deliverCallback = (consumerTag, mess) -&gt; {
               String message = new String(mess.getBody(), 'UTF-8');
               System.out.println(message);
           };
           channel.basicConsume('hello', true, deliverCallback, tag -&gt; {});
       }
   }
   ```

###### acknowlegement

```java
public static void main(String[] args) throws Exception{
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost('172.24.192.134');
    factory.setPort(5672);
    factory.setUsername('jasper');
    factory.setPassword('pubgM666');

    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();

    channel.queueDeclare('workqueues', false, false, false, null);
    // 设置一次只接受一个未消费消息
    channel.basicQos(1); 

    Random random = new Random();
    StringBuilder sb = new StringBuilder();
    for(int i = 0;i &lt; 4;i++) {
        sb.append((char) (random.nextInt(26) + 97));
    }
    String workerName = sb.toString();

    DeliverCallback callback = (tag, entity) -&gt; {
        String body = new String(entity.getBody(), StandardCharsets.UTF_8);
        for(int i = 0;i &lt; body.length();++i) {
            if(body.charAt(i) == '.') {
                try {
                    TimeUnit.MILLISECONDS.sleep(300);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        // 手动返回ack
        channel.basicAck(entity.getEnvelope().getDeliveryTag(), false);
        System.out.println(workerName + '消费了：' + body);
    };

    channel.basicConsume('workqueues', false, callback, tag -&gt; {});
}
```

###### message durability

如何确保消息不会丢失：

1. 队列持久化，声明队列时，第二个参数

   ```java
   channel.queueDeclare('workqueues', true, false, false, null);
   ```

2. 消息持久化，发送消息时声明

   ```java
   channel.basicPublish('', 'workqueues', MessageProperties.PERSISTENT_TEXT_PLAIN, body.getBytes(StandardCharsets.UTF_8));
   ```

如此过后消息可靠性大幅提升，已经可以满足大部分情况，但是还是存在一个小的窗口导致消息丢失，因为rabbitmq收到消息时，并不会保证将每条消息都刷盘，而是存在缓冲区。</description><guid isPermaLink="true">https://iamdurant.github.io/post/rabbitmq_java.html</guid><pubDate>Tue, 04 Mar 2025 22:10:07 +0000</pubDate></item><item><title>tarjan算法</title><link>https://iamdurant.github.io/post/tarjan-suan-fa.html</link><description>#### tarjan算法&#13;
&#13;
[tarjan学习](https://oi-wiki.org/graph/scc/)&#13;
&#13;
&gt; Robert E. Tarjan（计算机科学家） 发明了很多算法和数据结构，有：&#13;
&gt;&#13;
&gt; &gt; 已学习：✓&#13;
&gt; &gt;&#13;
&gt; &gt; 未学习：×&#13;
&gt;&#13;
&gt; - 并查集（✓）&#13;
&gt; - Splay（×）&#13;
&gt; - Toptree（×）&#13;
&gt; - Tarjan（dfs求强连通分量、dfs求割点、dfs求割边）(✓)&#13;
&#13;
##### 1.在有向图中求强连通分量&#13;
&#13;
**强连通分量（Strongly Connected Components）**：极大的强连通子图&#13;
&#13;
tarjan算法基于dfs，并且维护一个栈，m每次把尚未处理的节点加入栈中&#13;
&#13;
并且，每个节点还维护了以下两个变量：&#13;
&#13;
- dfn：dfs时遍历节点的次序&#13;
- low(low link value)：子树中能够回溯到的最早的在stack中的dfn&#13;
&#13;
对于一个连通分量，第一个dfs到的节点的dfn = low，这时将stack里 &gt;= dfn的节点pop&#13;
&#13;
##### 2.在无相图中求割点&#13;
&#13;
基于1进行变形&#13;
&#13;
- 对于根节点，若子树大于等于2，则为割点&#13;
&#13;
- 对于某个节点u的子节点，若存在一个节点v的low值大于等于dfn(u)，则u为割点&#13;
&#13;
  &gt; why？：因为若去掉点u，则点v无法回到祖先节点（因为点v能够回到祖先节点是依赖点u），形成新的连通分量&#13;
&#13;
##### 3.在无向图中求割边&#13;
&#13;
基于1进行变形&#13;
&#13;
- 若某个节点v的dfn == low(v) ，则代表其无法回到祖先节点，也无法通过另一条边回到父亲节点，则其父节点u到v的这条边即为割边&#13;
&#13;
割边例题：[1192.查找集群内的关键连接](https://leetcode.cn/problems/critical-connections-in-a-network/description/?envType=study-plan-v2&amp;envId=graph-theory)&#13;
&#13;
实现：&#13;
&#13;
```java&#13;
class Solution {&#13;
    // tarjan 割边&#13;
    int p = 0;&#13;
    List&lt;List&lt;Integer&gt;&gt; ans;&#13;
    public List&lt;List&lt;Integer&gt;&gt; criticalConnections(int n, List&lt;List&lt;Integer&gt;&gt; connections) {&#13;
        Map&lt;Integer, List&lt;Integer&gt;&gt; map = new HashMap&lt;&gt;();&#13;
        for(List&lt;Integer&gt; e : connections) {&#13;
            map.computeIfAbsent(e.get(0), k -&gt; new ArrayList&lt;&gt;()).add(e.get(1));&#13;
            map.computeIfAbsent(e.get(1), k -&gt; new ArrayList&lt;&gt;()).add(e.get(0));&#13;
        }&#13;
&#13;
        int[] dfn = new int[n];&#13;
        int[] low = new int[n];&#13;
        boolean[] v = new boolean[n];&#13;
        Set&lt;Integer&gt; set = new HashSet&lt;&gt;();&#13;
        ArrayDeque&lt;Integer&gt; stack = new ArrayDeque&lt;&gt;();&#13;
        ans = new ArrayList&lt;&gt;();&#13;
        dfs(map, stack, set, v, dfn, low, 0, -1);&#13;
&#13;
        return ans;&#13;
    }&#13;
&#13;
    private void dfs(Map&lt;Integer, List&lt;Integer&gt;&gt; map, ArrayDeque&lt;Integer&gt; stack, Set&lt;Integer&gt; set, boolean[] v, int[] dfn, int[] low, int cur, int pare) {&#13;
        dfn[cur] = p;&#13;
        low[cur] = dfn[cur];&#13;
        set.add(cur);&#13;
        stack.push(cur);&#13;
        v[cur] = true;&#13;
        List&lt;Integer&gt; ns = map.get(cur);&#13;
        if(ns != null) {&#13;
            for(int next : ns) {&#13;
                if(!v[next]) {&#13;
                    ++p;&#13;
                    dfs(map, stack, set, v, dfn, low, next, cur);&#13;
                    low[cur] = Math.min(low[cur], low[next]);&#13;
                    if(low[next] &gt; dfn[cur]) {&#13;
                        // 割边&#13;
                        ans.add(List.of(cur, next));&#13;
                    }&#13;
                } else {&#13;
                    if(set.contains(next) &amp;&amp; next != pare) {&#13;
                        low[cur] = Math.min(low[cur], low[next]);&#13;
                    }&#13;
                }&#13;
            }&#13;
        }&#13;
&#13;
        if(dfn[cur] == low[cur]) {&#13;
            while(!stack.isEmpty() &amp;&amp; stack.peek() != cur) {&#13;
                int pop = stack.pop();&#13;
                set.remove(pop);&#13;
            }&#13;
            if(!stack.isEmpty()) {&#13;
                int pop = stack.pop();&#13;
                set.remove(pop);&#13;
            }&#13;
        }&#13;
    }&#13;
}&#13;
```&#13;
&#13;
。</description><guid isPermaLink="true">https://iamdurant.github.io/post/tarjan-suan-fa.html</guid><pubDate>Thu, 02 Jan 2025 16:54:25 +0000</pubDate></item><item><title>并查集</title><link>https://iamdurant.github.io/post/bing-cha-ji.html</link><description>#### 通用版&#13;
```java&#13;
class UnionFind {&#13;
    int[] s;&#13;
&#13;
    public UnionFind(int size) {&#13;
        s = new int[size];&#13;
        for (int i = 0; i &lt; size; i++) {&#13;
            s[i] = i;&#13;
        }&#13;
    }&#13;
&#13;
    public int find(int x) {&#13;
        int r = s[x];&#13;
        if (r != x) {&#13;
            r = find(s[x]);&#13;
        }&#13;
&#13;
        return r;&#13;
    }&#13;
&#13;
    public void union(int i, int j) {&#13;
        int iBoss = find(i);&#13;
        int jBoss = find(j);&#13;
&#13;
        if (iBoss != jBoss) {&#13;
            if(iBoss &lt; jBoss) {&#13;
                s[jBoss] = iBoss;&#13;
            } else {&#13;
                s[iBoss] = jBoss;&#13;
            }&#13;
        }&#13;
    }&#13;
}&#13;
```&#13;
&#13;
#### hash表版（没有初始化节点(i -&gt; i)，需要在外部主动初始化）&#13;
```java&#13;
class UnionFind {&#13;
    Map&lt;Integer, Integer&gt; map = new HashMap&lt;&gt;();&#13;
&#13;
    public UnionFind() {}&#13;
&#13;
    public int find(int x) {&#13;
        int r = map.get(x);&#13;
        if(map.get(x) != x) {&#13;
            r = find(r);&#13;
        }&#13;
&#13;
        return r;&#13;
    }&#13;
&#13;
    public void union(int i, int j) {&#13;
        int iBoss = find(i);&#13;
        int jBoss = find(j);&#13;
&#13;
        if (iBoss != jBoss) {&#13;
            if (iBoss &lt; jBoss) {&#13;
                map.put(jBoss, iBoss);&#13;
            } else {&#13;
                map.put(iBoss, jBoss);&#13;
            }&#13;
        }&#13;
    }&#13;
}&#13;
```&#13;
&#13;
#### 完全版&#13;
- 有path compressed&#13;
- 有 union by rank&#13;
```java&#13;
class DisjointSet {&#13;
    int[] s;&#13;
    int[] rank;&#13;
&#13;
    public DisjointSet(int size) {&#13;
        s = new int[size];&#13;
        rank = new int[size];&#13;
        for (int i = 0; i &lt; size; i++) {&#13;
            s[i] = i;&#13;
        }&#13;
    }&#13;
&#13;
    public int find(int x) {&#13;
        // path compression&#13;
        if (s[x] != x) {&#13;
            s[x] = find(s[x]);&#13;
        }&#13;
&#13;
        return s[x];&#13;
    }&#13;
&#13;
    public void union(int i, int j) {&#13;
        int iBoss = find(i);&#13;
        int jBoss = find(j);&#13;
&#13;
        if (iBoss != jBoss) {&#13;
            // union by rank&#13;
            if (rank[iBoss] &lt; rank[jBoss]) {&#13;
                s[iBoss] = jBoss;&#13;
            } else if (rank[iBoss] &gt; rank[jBoss]) {&#13;
                s[jBoss] = iBoss;&#13;
            } else {&#13;
                s[iBoss] = jBoss;&#13;
                rank[jBoss]++;&#13;
            }&#13;
        }&#13;
    }&#13;
&#13;
    @Override&#13;
    public String toString() {&#13;
        return 'DisjointSet{' +&#13;
            's=' + Arrays.toString(s) +&#13;
            '}';&#13;
    }&#13;
}&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/bing-cha-ji.html</guid><pubDate>Wed, 01 Jan 2025 02:21:14 +0000</pubDate></item><item><title>二进制枚举</title><link>https://iamdurant.github.io/post/er-jin-zhi-mei-ju.html</link><description>#### 二进制枚举&#13;
&#13;
##### 1.枚举所有集合&#13;
&#13;
&gt; 假设有三个元素的集合z：{0, 1, 2}，那么每种元素可以选或者不选，只有三个元素，有`2^3`个子集&#13;
&gt;&#13;
&gt; {0，1，10，11，100，101，110，111}，刚好小于`1 &lt;&lt; |z|`的数有`2^3`个，从小开始枚举，然后取二进制位即可表示某个元素的状态&#13;
&#13;
```java&#13;
for(int i = 0;i &lt; (1 &lt;&lt; n);i++) {&#13;
    // 枚举二进制位，即能得到某个元素的取舍状态&#13;
}&#13;
```&#13;
&#13;
&#13;
&#13;
##### 2.从大到小枚举某个集合s的非空子集&#13;
&#13;
&gt; 假设对于集合`10101`，若想要枚举全部非空集合，暴力做法是：不断减1，得到的所有子集中包含了`s`的子集，其中会有集合不是s的子集&#13;
&gt;&#13;
&gt; 那么，如何跳过这些非s的子集的集合。</description><guid isPermaLink="true">https://iamdurant.github.io/post/er-jin-zhi-mei-ju.html</guid><pubDate>Tue, 31 Dec 2024 19:41:52 +0000</pubDate></item><item><title>floyd</title><link>https://iamdurant.github.io/post/floyd.html</link><description>#### [原理](https://blog.csdn.net/qq_35644234/article/details/60875818)&#13;
&#13;
#### 实现&#13;
&#13;
- 数据&#13;
&#13;
  ```text&#13;
  12&#13;
  1 2 12&#13;
  1 6 16&#13;
  1 7 14&#13;
  2 3 10&#13;
  2 6 7&#13;
  3 4 3&#13;
  3 5 5&#13;
  3 6 6&#13;
  4 5 4&#13;
  5 6 2&#13;
  5 7 8&#13;
  6 7 9&#13;
  ```&#13;
&#13;
```java&#13;
public static void floyd() {&#13;
    Scanner sc = new Scanner(System.in);&#13;
    System.out.print('请输入图的边数量: ');&#13;
    int edgesAmount = sc.nextInt();&#13;
    List&lt;int[]&gt; edges = new ArrayList&lt;&gt;();&#13;
    sc.nextLine();&#13;
&#13;
    System.out.println('请输入所有边（格式为: 点x 点y 权重）: ');&#13;
    Set&lt;Integer&gt; points = new HashSet&lt;&gt;();&#13;
    for(int i = 0;i &lt; edgesAmount;i++) {&#13;
        String line = sc.nextLine();&#13;
        String[] edge = line.split(' ');&#13;
        edges.add(new int[]{Integer.parseInt(edge[0]), Integer.parseInt(edge[1]), Integer.parseInt(edge[2])});&#13;
&#13;
        points.add(Integer.parseInt(edge[0]));&#13;
        points.add(Integer.parseInt(edge[1]));&#13;
    }&#13;
    int pointSize = points.size() + 1;&#13;
    int[][] d = new int[pointSize][pointSize];&#13;
    int[][] p = new int[pointSize][pointSize];&#13;
    // 初始化d数组&#13;
    for(int i = 0;i &lt; pointSize;i++) {&#13;
        Arrays.fill(d[i], Integer.MAX_VALUE);&#13;
    }&#13;
    for(int[] e : edges) {&#13;
        d[e[0]][e[1]] = e[2];&#13;
        d[e[1]][e[0]] = e[2];&#13;
    }&#13;
&#13;
    // 初始化path数组，假设全都可达&#13;
    for(int i = 0;i &lt; pointSize;i++) {&#13;
        for(int j = 0;j &lt; pointSize;j++) {&#13;
            p[i][j] = j;&#13;
        }&#13;
    }&#13;
&#13;
    // O(n^3)&#13;
    for(int k = 0;k &lt; pointSize;k++) {&#13;
        for(int i = 0;i &lt; pointSize;i++) {&#13;
            for(int j = 0;j &lt; pointSize;j++) {&#13;
                if(d[i][k] == Integer.MAX_VALUE || d[k][j] == Integer.MAX_VALUE) continue;&#13;
                int dis = d[i][k] + d[k][j];&#13;
                if(dis &lt; d[i][j]) {&#13;
                    d[i][j] = dis;&#13;
                    p[i][j] = k;&#13;
                }&#13;
            }&#13;
        }&#13;
    }&#13;
&#13;
    // 输出&#13;
    for(int i = 0;i &lt; pointSize;i++) {&#13;
        for(int j = 0;j &lt; pointSize;j++) {&#13;
            if(i == j || d[i][j] == Integer.MAX_VALUE) continue;&#13;
            System.out.println(i + '-&gt;' + j + '的最短路径值: ' + d[i][j]);&#13;
            List&lt;Integer&gt; paths = new ArrayList&lt;&gt;();&#13;
            path(p, i, j, -1, paths);&#13;
            System.out.println('路径为: ' + buildPath(paths));&#13;
        }&#13;
    }&#13;
}&#13;
&#13;
private static void path(int[][] p, int s, int t, int lr, List&lt;Integer&gt; paths) {&#13;
    int m = p[s][t];&#13;
&#13;
    if(lr != 1) paths.add(s);&#13;
    // 处理左边&#13;
    if(!(m == s || m == t)) path(p, s, m, 1, paths);&#13;
    // 中点&#13;
    if(!(m == s || m == t)) paths.add(m);&#13;
    // 处理右边&#13;
    if(!(m == s || m == t)) path(p, m, t, 1, paths);&#13;
    if(lr != 1) paths.add(t);&#13;
}&#13;
&#13;
private static String buildPath(List&lt;Integer&gt; paths) {&#13;
    StringBuilder sb = new StringBuilder();&#13;
    for(int i = 0;i &lt; paths.size();i++) {&#13;
        sb.append(paths.get(i));&#13;
        if(i != paths.size() - 1) sb.append('-&gt;');&#13;
    }&#13;
&#13;
    return sb.toString();&#13;
}&#13;
```&#13;
&#13;
。</description><guid isPermaLink="true">https://iamdurant.github.io/post/floyd.html</guid><pubDate>Sun, 29 Dec 2024 22:30:57 +0000</pubDate></item><item><title>前缀函数（kmp）</title><link>https://iamdurant.github.io/post/qian-zhui-han-shu-%EF%BC%88kmp%EF%BC%89.html</link><description>#### 前缀函数（kmp）&#13;
&#13;
##### [原理](https://oi-wiki.org/string/kmp/#__tabbed_3_3)&#13;
&#13;
##### 1.求前缀函数，普通写法&#13;
   - 时间复杂度：O(n^3)&#13;
&#13;
```java&#13;
/**&#13;
     *&#13;
     * 时间复杂度: O(n^3)&#13;
     * @param s 字符串&#13;
     * @return  pai&#13;
     */&#13;
public int[] pf(String s) {&#13;
    char[] c = s.toCharArray();&#13;
    int n = c.length;&#13;
    int[] pi = new int[n];&#13;
    for(int i = 1;i &lt; n;i++) {&#13;
        for(int j = 1;j &lt;= i;j++) {&#13;
            int l = 0;&#13;
            int r = j;&#13;
            while(r &lt; n &amp;&amp; c[l] == c[r]) {&#13;
                l++;&#13;
                r++;&#13;
            }&#13;
            if(r &gt; i) {&#13;
                pi[i] = i - j + 1;&#13;
                break;&#13;
            }&#13;
        }&#13;
    }&#13;
&#13;
    return pi;&#13;
}&#13;
```&#13;
&#13;
##### 2.求前缀函数，第一个优化&#13;
&#13;
   - 时间复杂度：O(n^2)&#13;
   - 原理：&#13;
&#13;
   &gt; 观察相邻的`pai[i]`、`pai[i + 1]`，发现`pai[i + 1] &lt;= pai[i] + 1`，那么对于每个`pai[i]`都能跳过`i - pai[i + 1]`个前缀，大大提高效率&#13;
&#13;
```java&#13;
/**&#13;
     *&#13;
     * 时间复杂度: O(n^2)&#13;
     * @param s 字符串&#13;
     * @return  pi&#13;
     */&#13;
public int[] pf(String s) {&#13;
    char[] c = s.toCharArray();&#13;
    int n = c.length;&#13;
    int[] pi = new int[n];&#13;
    for(int i = 1;i &lt; n;i++) {&#13;
        if(c[i] == c[pi[i - 1]]) {&#13;
            pi[i] = pi[i - 1] + 1;&#13;
            continue;&#13;
        }&#13;
        for(int j = i - pi[i - 1] + 1;j &lt;= i;j++) {&#13;
            int l = 0;&#13;
            int r = j;&#13;
            while(r &lt; n &amp;&amp; c[l] == c[r]) {&#13;
                l++;&#13;
                r++;&#13;
            }&#13;
            if(r &gt; i) {&#13;
                pi[i] = i - j + 1;&#13;
                break;&#13;
            }&#13;
        }&#13;
    }&#13;
&#13;
    return pi;&#13;
}&#13;
```&#13;
&#13;
##### 3.求前缀函数，第二个优化&#13;
&#13;
- 时间复杂度：O(n)&#13;
&#13;
- 原理：&#13;
&#13;
  &gt; 求`pi[i]`，`len = pi[i - 1]`，`len`为`c[i - 1]`的相等真前后缀长度，若`s[len] != s[i]`，则减少，直到`0`。</description><guid isPermaLink="true">https://iamdurant.github.io/post/qian-zhui-han-shu-%EF%BC%88kmp%EF%BC%89.html</guid><pubDate>Wed, 18 Dec 2024 09:21:52 +0000</pubDate></item><item><title>Z函数（扩展kmp）</title><link>https://iamdurant.github.io/post/Z-han-shu-%EF%BC%88-kuo-zhan-kmp%EF%BC%89.html</link><description>[原理](https://oi-wiki.org/string/z-func/#__tabbed_2_1)&#13;
&#13;
```java&#13;
public int[] zFunction(String s) {&#13;
        int n = s.length();&#13;
        char[] c = s.toCharArray();&#13;
        int[] z = new int[n];&#13;
        int l = 0;&#13;
        int r = 0;&#13;
        for(int i = 1;i &lt; n;i++) {&#13;
            if(i &gt; r) {&#13;
                // 暴力匹配&#13;
                while(i + z[i] &lt; n &amp;&amp; c[i + z[i]] == c[z[i]]) z[i]++;&#13;
            } else {&#13;
                if(z[i - l] &lt; r - i + 1) z[i] = z[i - l];&#13;
                else {&#13;
                    // 跳过前r - i + 1个后继续匹配&#13;
                    while(r + z[i] + 1 &lt; n &amp;&amp; c[r + z[i] + 1] == c[r - i + 1 + z[i]]) z[i]++;&#13;
                    z[i] += r - i + 1;&#13;
                }&#13;
            }&#13;
            if(i + z[i] &gt; r &amp;&amp; z[i] != 0) {&#13;
                // 更新l、r&#13;
                l = i;&#13;
                r = i + z[i] - 1;&#13;
            }&#13;
        }&#13;
&#13;
        return z;&#13;
    }&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/Z-han-shu-%EF%BC%88-kuo-zhan-kmp%EF%BC%89.html</guid><pubDate>Tue, 17 Dec 2024 10:32:15 +0000</pubDate></item><item><title>dijkstra</title><link>https://iamdurant.github.io/post/dijkstra.html</link><description>1. 普通dijkstra&#13;
&#13;
```java&#13;
import java.util.*;&#13;
&#13;
public class Dijkstra {&#13;
    static class Node implements Comparable&lt;Node&gt; {&#13;
        int vertex;&#13;
        int distance;&#13;
&#13;
        public Node(int vertex, int distance) {&#13;
            this.vertex = vertex;&#13;
            this.distance = distance;&#13;
        }&#13;
&#13;
        @Override&#13;
        public int compareTo(Node other) {&#13;
            return Integer.compare(this.distance, other.distance);&#13;
        }&#13;
    }&#13;
&#13;
    public static int[] dijkstra(int[][] graph, int start) {&#13;
        int n = graph.length;&#13;
        int[] distances = new int[n];&#13;
        boolean[] visited = new boolean[n];&#13;
&#13;
        // 初始化距离数组&#13;
        Arrays.fill(distances, Integer.MAX_VALUE);&#13;
        distances[start] = 0;&#13;
&#13;
        // 使用优先队列优化&#13;
        PriorityQueue&lt;Node&gt; pq = new PriorityQueue&lt;&gt;();&#13;
        pq.offer(new Node(start, 0));&#13;
&#13;
        while (!pq.isEmpty()) {&#13;
            Node current = pq.poll();&#13;
            int u = current.vertex;&#13;
&#13;
            // 如果已经访问过，跳过&#13;
            if (visited[u]) continue;&#13;
            visited[u] = true;&#13;
&#13;
            // 更新相邻节点的距离&#13;
            for (int v = 0; v &lt; n; v++) {&#13;
                if (graph[u][v] != 0) {  // 存在边&#13;
                    int newDist = distances[u] + graph[u][v];&#13;
                    if (newDist &lt; distances[v]) {&#13;
                        distances[v] = newDist;&#13;
                        pq.offer(new Node(v, distances[v]));&#13;
                    }&#13;
                }&#13;
            }&#13;
        }&#13;
&#13;
        return distances;&#13;
    }&#13;
&#13;
    // 测试代码&#13;
    public static void main(String[] args) {&#13;
        int[][] graph = {&#13;
            {0, 4, 2, 0, 0},&#13;
            {4, 0, 1, 5, 0},&#13;
            {2, 1, 0, 8, 10},&#13;
            {0, 5, 8, 0, 2},&#13;
            {0, 0, 10, 2, 0}&#13;
        };&#13;
&#13;
        int start = 0;  // 起点为A（0号顶点）&#13;
        int[] distances = dijkstra(graph, start);&#13;
&#13;
        // 打印结果&#13;
        System.out.println('从顶点 ' + start + ' 到各个顶点的最短距离：');&#13;
        for (int i = 0; i &lt; distances.length; i++) {&#13;
            System.out.println('到顶点 ' + i + ' 的距离: ' + distances[i]);&#13;
        }&#13;
    }&#13;
}&#13;
```&#13;
&#13;
2. 带路径记录的dijkstra&#13;
&#13;
```java&#13;
import java.util.*;&#13;
&#13;
public class DijkstraWithPath {&#13;
    static class Node implements Comparable&lt;Node&gt; {&#13;
        int vertex;&#13;
        int distance;&#13;
&#13;
        public Node(int vertex, int distance) {&#13;
            this.vertex = vertex;&#13;
            this.distance = distance;&#13;
        }&#13;
&#13;
        @Override&#13;
        public int compareTo(Node other) {&#13;
            return Integer.compare(this.distance, other.distance);&#13;
        }&#13;
    }&#13;
&#13;
    static class Result {&#13;
        int[] distances;&#13;
        int[] previousVertices;&#13;
&#13;
        public Result(int[] distances, int[] previousVertices) {&#13;
            this.distances = distances;&#13;
            this.previousVertices = previousVertices;&#13;
        }&#13;
&#13;
        // 获取从起点到目标点的路径&#13;
        public List&lt;Integer&gt; getPath(int target) {&#13;
            List&lt;Integer&gt; path = new ArrayList&lt;&gt;();&#13;
            int current = target;&#13;
&#13;
            // 如果没有路径到达目标点&#13;
            if (distances[target] == Integer.MAX_VALUE) {&#13;
                return path;&#13;
            }&#13;
&#13;
            while (current != -1) {&#13;
                path.add(0, current);&#13;
                current = previousVertices[current];&#13;
            }&#13;
&#13;
            return path;&#13;
        }&#13;
    }&#13;
&#13;
    public static Result dijkstra(int[][] graph, int start) {&#13;
        int n = graph.length;&#13;
        int[] distances = new int[n];&#13;
        int[] previousVertices = new int[n];&#13;
        boolean[] visited = new boolean[n];&#13;
&#13;
        // 初始化&#13;
        Arrays.fill(distances, Integer.MAX_VALUE);&#13;
        Arrays.fill(previousVertices, -1);&#13;
        distances[start] = 0;&#13;
&#13;
        PriorityQueue&lt;Node&gt; pq = new PriorityQueue&lt;&gt;();&#13;
        pq.offer(new Node(start, 0));&#13;
&#13;
        while (!pq.isEmpty()) {&#13;
            Node current = pq.poll();&#13;
            int u = current.vertex;&#13;
&#13;
            if (visited[u]) continue;&#13;
            visited[u] = true;&#13;
&#13;
            for (int v = 0; v &lt; n; v++) {&#13;
                if (graph[u][v] != 0) {&#13;
                    int newDist = distances[u] + graph[u][v];&#13;
                    if (newDist &lt; distances[v]) {&#13;
                        distances[v] = newDist;&#13;
                        previousVertices[v] = u;&#13;
                        pq.offer(new Node(v, distances[v]));&#13;
                    }&#13;
                }&#13;
            }&#13;
        }&#13;
&#13;
        return new Result(distances, previousVertices);&#13;
    }&#13;
&#13;
    public static void main(String[] args) {&#13;
        // 示例图（邻接矩阵表示）&#13;
        int[][] graph = {&#13;
            {0, 4, 2, 0, 0},&#13;
            {4, 0, 1, 5, 0},&#13;
            {2, 1, 0, 8, 10},&#13;
            {0, 5, 8, 0, 2},&#13;
            {0, 0, 10, 2, 0}&#13;
        };&#13;
&#13;
        int start = 0;&#13;
        Result result = dijkstra(graph, start);&#13;
&#13;
        // 打印所有最短距离&#13;
        System.out.println('从顶点 ' + start + ' 到各个顶点的最短距离：');&#13;
        for (int i = 0; i &lt; result.distances.length; i++) {&#13;
            System.out.println('到顶点 ' + i + ' 的距离: ' + result.distances[i]);&#13;
&#13;
            // 打印路径&#13;
            List&lt;Integer&gt; path = result.getPath(i);&#13;
            System.out.println('路径: ' + path);&#13;
        }&#13;
    }&#13;
}&#13;
```&#13;
&#13;
3. 解释&#13;
   1. Node类&#13;
      - 实现了Comparator接口，用于优先队列的排序&#13;
      - 存储顶点编号和到起点的距离&#13;
   2. 数据结构&#13;
      - `distances[]`：存储从起点到各个顶点的最短距离&#13;
      - `visited[]`：记录顶点是否被访问过&#13;
      - `previousVertices[]`：记录最短路径中每个顶点的前驱顶点&#13;
      - `PriorityQueue`：优先队列，用于获取当前最短距离的顶点&#13;
   3. 核心步骤&#13;
      - 初始化距离数组，起点距离为0，其他为无穷大&#13;
      - 使用优先队列选择当前最短距离的顶点&#13;
      - 更新选中顶点的邻接点的距离&#13;
      - 记录路径信息&#13;
4. 性能优化建议&#13;
   1. 对于稀疏图，使用邻接表代替邻接矩阵&#13;
   2. 使用索引优先队列可以进一步优化性能&#13;
   3. 如果只需要找到到特定目标的最短路径，可以在找到后提前终止&#13;
5. 使用示例&#13;
&#13;
```java&#13;
// 创建图（邻接矩阵）&#13;
int[][] graph = new int[5][5];&#13;
// 添加边&#13;
graph[0][1] = 4;  // A到B的距离为4&#13;
graph[1][0] = 4;  // B到A的距离为4&#13;
// ... 添加其他边&#13;
&#13;
// 计算最短路径&#13;
Result result = dijkstra(graph, 0);  // 从顶点0开始&#13;
&#13;
// 获取到某个顶点的最短路径&#13;
List&lt;Integer&gt; pathTo3 = result.getPath(3);&#13;
System.out.println('到顶点3的路径: ' + pathTo3);&#13;
System.out.println('距离: ' + result.distances[3]);&#13;
```&#13;
&#13;
&#13;
&#13;
。</description><guid isPermaLink="true">https://iamdurant.github.io/post/dijkstra.html</guid><pubDate>Fri, 13 Dec 2024 22:58:14 +0000</pubDate></item><item><title>java日志框架</title><link>https://iamdurant.github.io/post/java-ri-zhi-kuang-jia.html</link><description>#### java现有的日志框架有哪些&#13;
&#13;
- JUL(java util logging)&#13;
- logback&#13;
- log4j&#13;
- log4j2&#13;
- JCL(jakarta commons logging)&#13;
- slf4j(simple logging facade for java)&#13;
&#13;
&#13;
&#13;
#### 日志门面技术的优点&#13;
&#13;
1. 面向接口开发，不再依赖具体的实现类&#13;
2. 通过导入不同的日志实现类，可以灵活的切换日志框架&#13;
3. 统一api，方便开发者学习和使用&#13;
4. 统一配置便于项目日志的管理&#13;
&#13;
&#13;
&#13;
#### JUL(java util logging)&#13;
&#13;
JUL，java原生日志框架，不需要引入第三方依赖包，使用简单方便，一般在小型应用中使用，主流项目中很少使用&#13;
&#13;
##### JUL架构&#13;
&#13;
- Application：java应用程序&#13;
- loggers：记录器，application通过获取logger对象，调用其api来发布日志消息&#13;
- appenders：也称为handlers，每个logger都会关联一组handlers，logger会将日志交给handlers处理handlers是一个抽象，其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等&#13;
- layouts：也称为formatters，负责对日志进行转换或者格式化&#13;
- level：每条日志都有一个关联的日志级别&#13;
- filters：过滤器，根据需要自定义哪些消息会被记录，哪些消息会被放过&#13;
&#13;
##### 日志级别(由高到低)&#13;
&#13;
1. severe&#13;
2. warning&#13;
3. info(默认的日志级别)&#13;
4. config&#13;
5. fine&#13;
6. finer&#13;
7. finest&#13;
&#13;
##### 为什么要对日志进行分级&#13;
&#13;
无论是将日志输出到`console`还是文件，都会带来消耗，降低程序的运行效率，但是，日志又是调试或者了解程序运行的不可或缺的手段，通过对日志进行分级处理，在一个配置文件中进行统一的管理，这样代码中日志部分就可以不用删除，又可以控制输出哪些日志&#13;
&#13;
```java&#13;
public static void main(String[] args) {&#13;
    Logger logger = Logger.getLogger('multi');&#13;
&#13;
    logger.setUseParentHandlers(false);&#13;
&#13;
    ConsoleHandler consoleHandler = new ConsoleHandler();&#13;
    consoleHandler.setLevel(Level.ALL);&#13;
    SimpleFormatter simpleFormatter = new SimpleFormatter();&#13;
    consoleHandler.setFormatter(simpleFormatter);&#13;
    logger.addHandler(consoleHandler);&#13;
&#13;
    logger.setLevel(Level.ALL);&#13;
&#13;
    logger.severe('&gt;&gt;&gt;&gt; severe');&#13;
    logger.warning('&gt;&gt;&gt;&gt; warning');&#13;
    logger.info('&gt;&gt;&gt;&gt; info');&#13;
    logger.config('&gt;&gt;&gt;&gt; config');&#13;
    logger.fine('&gt;&gt;&gt;&gt; fine');&#13;
    logger.finer('&gt;&gt;&gt;&gt; finer');&#13;
    logger.finest('&gt;&gt;&gt;&gt; finest');&#13;
&#13;
    logger.log(Level.WARNING, '&gt;&gt;&gt;&gt; waring!!!!');&#13;
&#13;
    String name = 'wwb';&#13;
    int age = 23;&#13;
    logger.log(Level.INFO, '&gt;&gt;&gt;&gt; name: {0}, age: {1}', new Object[]{name, age});&#13;
&#13;
    //logger.log(Level.SEVERE, 'NPE: ', new NullPointerException());&#13;
}&#13;
```&#13;
&#13;
##### logger的继承关系&#13;
&#13;
创建`logger`时，若未指定父`logger`，则默认继承`RootLogger`。</description><guid isPermaLink="true">https://iamdurant.github.io/post/java-ri-zhi-kuang-jia.html</guid><pubDate>Wed, 06 Nov 2024 09:50:38 +0000</pubDate></item><item><title>minio基础</title><link>https://iamdurant.github.io/post/minio-ji-chu.html</link><description>**官网**：[官网](https://min.io)&#13;
&#13;
**文档**：[文档](https://minio-java.min.io)&#13;
&#13;
**快速开始**：[quickStart](https://www.minio.org.cn/docs/minio/linux/developers/java/minio-java.html#minio-java-quickstart)&#13;
&#13;
&#13;
&#13;
**安装**&#13;
&#13;
```shell&#13;
wget https://dl.min.io/server/minio/release/linux-amd64/minio&#13;
chmod +x minio&#13;
MINIO_ROOT_USER=wwb MINIO_ROOT_PASSWORD=pubgM666 ./minio server /mnt/min_data --console-address ':9001'&#13;
```&#13;
&#13;
**access_key and secret_key**&#13;
&#13;
```text&#13;
access_key: Um89W6wboxiiVeLUiYBn&#13;
secret_key: 4MmSQrEy5t8xpIbACZLmnYhidaOcIEt246GiSPRM&#13;
```&#13;
&#13;
**上传文件demo**&#13;
&#13;
```java&#13;
package com.minio;&#13;
&#13;
import io.minio.*;&#13;
&#13;
public class Exec {&#13;
    public static void main(String[] args) throws Exception{&#13;
        MinioClient client = new MinioClient.Builder().endpoint('http://172.24.192.134:9000')&#13;
                .credentials('Um89W6wboxiiVeLUiYBn', '4MmSQrEy5t8xpIbACZLmnYhidaOcIEt246GiSPRM')&#13;
                .build();&#13;
&#13;
        boolean exists = client.bucketExists(BucketExistsArgs.builder().bucket('test').build());&#13;
        if(!exists) {&#13;
            client.makeBucket(MakeBucketArgs.builder().bucket('test').build());&#13;
        }&#13;
&#13;
        client.uploadObject(UploadObjectArgs.builder()&#13;
                .object('RocketMQ api实战.md')&#13;
                .filename('C:\\Users\\wwb\\Desktop\\typora_record\\RocketMQ api实战.md')&#13;
                .bucket('test')&#13;
                .build());&#13;
&#13;
        client.close();&#13;
    }&#13;
}&#13;
```&#13;
&#13;
&#13;
&#13;
**minio对象属性：**&#13;
&#13;
- object name：对象的唯一名称&#13;
&#13;
- bucket name：对象所在的存储桶名称&#13;
&#13;
- content type：对象的mime类型，例如：image/jpeg、application//pdf&#13;
&#13;
- content length：对象的大小，以byte为单位&#13;
&#13;
- last modified：对象的最后修改时间&#13;
&#13;
- user-defined metadata：对象的自定义元数据，例如作者、创建日期等&#13;
&#13;
- etag：对象的唯一标识符，通常是一个哈希值&#13;
&#13;
  &#13;
&#13;
**spring-boot对文件上传大小默认大小为1MB，需要覆盖配置**&#13;
&#13;
```yaml&#13;
spring:&#13;
  servlet:&#13;
    multipart:&#13;
      max-file-size: 1TB&#13;
      max-request-size: 1TB&#13;
```&#13;
&#13;
&#13;
&#13;
**获取对象链接**&#13;
&#13;
```java&#13;
String url = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs&#13;
                .builder()&#13;
                .bucket('test')&#13;
                .object('RocketMQ api实战.md')&#13;
                .method(Method.GET)&#13;
                .expiry(5, TimeUnit.DAYS)&#13;
                .build());&#13;
```&#13;
&#13;
&#13;
&#13;
**列出文件列表**&#13;
&#13;
```java&#13;
/**&#13;
 * 测试列出文件列表&#13;
 * @throws Exception 异常&#13;
 */&#13;
static void testListObjs() throws Exception{&#13;
    Iterable&lt;Result&lt;Item&gt;&gt; objs = client.listObjects(ListObjectsArgs.builder()&#13;
                                             .bucket('test')&#13;
                                             .prefix('video/a')&#13;
                                             .recursive(true)&#13;
                                             .build());&#13;
&#13;
    for (Result&lt;Item&gt; obj : objs) {&#13;
        System.out.println(obj.get().objectName());&#13;
    }&#13;
&#13;
    client.close();&#13;
}&#13;
```&#13;
&#13;
&#13;
&#13;
**列出所有bucket**&#13;
&#13;
```java&#13;
/**&#13;
 * 测试列出所有bucket&#13;
 * @throws Exception 异常&#13;
 */&#13;
static void testListBuckets() throws Exception {&#13;
    List&lt;Bucket&gt; buckets = client.listBuckets();&#13;
    buckets.forEach(b -&gt; System.out.println(b.name()));&#13;
    client.close();&#13;
}&#13;
```&#13;
&#13;
&#13;
&#13;
**下载文件**&#13;
&#13;
```java&#13;
/**&#13;
 * 测试下载文件&#13;
 * @throws Exception 异常&#13;
 */&#13;
static void testGetObj() throws Exception {&#13;
    GetObjectResponse resp = client.getObject(GetObjectArgs.builder()&#13;
                                      .bucket('test')&#13;
                                      .object('9666489052.mp4')&#13;
                                      .build());&#13;
&#13;
    byte[] data = resp.readAllBytes();&#13;
    FileOutputStream outputStream = new FileOutputStream(resp.object());&#13;
    outputStream.write(data);&#13;
    outputStream.flush();&#13;
    outputStream.close();&#13;
&#13;
    client.close();&#13;
}&#13;
```&#13;
&#13;
&#13;
&#13;
**复制文件**&#13;
&#13;
```java&#13;
/**&#13;
 * 测试复制文件&#13;
 * @throws Exception 异常&#13;
 */&#13;
static void testCopyObj() throws Exception {&#13;
    CopySource src = new CopySource.Builder().bucket('test').object('9666489052.mp4').build();&#13;
    client.copyObject(CopyObjectArgs.builder()&#13;
                      .source(src)&#13;
                      .bucket('learn')&#13;
                      .object('美女舞蹈.mp4')&#13;
                      .build());&#13;
&#13;
    client.close();&#13;
}&#13;
```&#13;
&#13;
&#13;
&#13;
**删除文件**&#13;
&#13;
```java&#13;
/**&#13;
 * 测试删除文件&#13;
 * @throws Exception 异常&#13;
 */&#13;
static void testDeleteObj() throws Exception {&#13;
    client.removeObject(RemoveObjectArgs.builder()&#13;
                        .bucket('learn')&#13;
                        .object('美女舞蹈.mp4')&#13;
                        .build());&#13;
&#13;
    client.close();&#13;
}&#13;
```&#13;
&#13;
。</description><guid isPermaLink="true">https://iamdurant.github.io/post/minio-ji-chu.html</guid><pubDate>Sat, 02 Nov 2024 04:43:21 +0000</pubDate></item><item><title>树状数组</title><link>https://iamdurant.github.io/post/shu-zhuang-shu-zu.html</link><description>#### 树状数组&#13;
&#13;
**模板**&#13;
```java&#13;
static class FenwickTree {&#13;
        int[] arr;&#13;
        int[] b;&#13;
        int n;&#13;
&#13;
        public FenwickTree(int[] nums) {&#13;
            this.arr = nums;&#13;
            n = nums.length;&#13;
            init(nums);&#13;
        }&#13;
&#13;
        private void init(int[] nums) {&#13;
            b = new int[n + 1];&#13;
            for(int i = 1;i &lt;= nums.length;i++) {&#13;
                int k = i;&#13;
                while(k &lt;= n) {&#13;
                    b[k] += nums[i - 1];&#13;
                    k += (k &amp; -k);&#13;
                }&#13;
            }&#13;
        }&#13;
&#13;
        public void update(int i, int val) {&#13;
            int tmp = arr[i];&#13;
            arr[i] = val;&#13;
            int k = i + 1;&#13;
            while(k &lt;= n) {&#13;
                b[k] -= tmp;&#13;
                b[k] += val;&#13;
                k += (k &amp; -k);&#13;
            }&#13;
        }&#13;
&#13;
        public int sum(int l, int r) {&#13;
            return sum(r + 1) - sum(l);&#13;
        }&#13;
&#13;
        private int sum(int r) {&#13;
            int sum = 0;&#13;
            while(r &gt; 0) {&#13;
                sum += b[r];&#13;
                r -= (r &amp; -r);&#13;
            }&#13;
&#13;
            return sum;&#13;
        }&#13;
    }&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/shu-zhuang-shu-zu.html</guid><pubDate>Tue, 08 Oct 2024 13:16:44 +0000</pubDate></item><item><title>java打包</title><link>https://iamdurant.github.io/post/java-da-bao.html</link><description>```java&#13;
&lt;build&gt;&#13;
    &lt;plugins&gt;&#13;
        &lt;plugin&gt;&#13;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&#13;
            &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;&#13;
            &lt;version&gt;3.1.2&lt;/version&gt; &lt;!-- 使用合适的 Spring Boot 版本 --&gt;&#13;
            &lt;executions&gt;&#13;
                &lt;execution&gt;&#13;
                    &lt;goals&gt;&#13;
                        &lt;goal&gt;repackage&lt;/goal&gt; &lt;!-- 确保将所有依赖打包进 JAR --&gt;&#13;
                    &lt;/goals&gt;&#13;
                &lt;/execution&gt;&#13;
            &lt;/executions&gt;&#13;
        &lt;/plugin&gt;&#13;
    &lt;/plugins&gt;&#13;
&lt;/build&gt;&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/java-da-bao.html</guid><pubDate>Wed, 18 Sep 2024 23:16:13 +0000</pubDate></item><item><title>欧几里得算法求最大公约数（GCD）</title><link>https://iamdurant.github.io/post/ou-ji-li-de-suan-fa-qiu-zui-da-gong-yue-shu-%EF%BC%88GCD%EF%BC%89.html</link><description>判断两个数是否为非互质数的计算原理基于 最大公约数 (GCD)。</description><guid isPermaLink="true">https://iamdurant.github.io/post/ou-ji-li-de-suan-fa-qiu-zui-da-gong-yue-shu-%EF%BC%88GCD%EF%BC%89.html</guid><pubDate>Wed, 18 Sep 2024 12:05:19 +0000</pubDate></item><item><title>最大频率栈</title><link>https://iamdurant.github.io/post/zui-da-pin-lv-zhan.html</link><description>#### 最大频率栈&#13;
实现方法&#13;
- 为每一个频率维护一个栈（map）&#13;
- 为元素维护计数器（map）&#13;
- push元素时注意max以及频率栈的更新&#13;
- pop元素时注意max、频率栈的删除以及计数器的更新&#13;
&#13;
代码如下&#13;
```java&#13;
class FreqStack {&#13;
    Map&lt;Integer, Integer&gt; map = new HashMap&lt;&gt;();&#13;
    Map&lt;Integer, ArrayDeque&lt;Integer&gt;&gt; freqMap = new HashMap&lt;&gt;();&#13;
    int max = 0;&#13;
&#13;
    public FreqStack() {}&#13;
    &#13;
    public void push(int val) {&#13;
        int freq = map.getOrDefault(val, 0) + 1;&#13;
        max = Math.max(max, freq);&#13;
        map.put(val, freq);&#13;
        freqMap.computeIfAbsent(freq, k -&gt; new ArrayDeque&lt;&gt;()).push(val);&#13;
    }&#13;
    &#13;
    public int pop() {&#13;
        ArrayDeque&lt;Integer&gt; stack = freqMap.get(max);&#13;
        int re = stack.pop();&#13;
        map.put(re, map.get(re) - 1);&#13;
        if(stack.isEmpty()) {&#13;
            freqMap.remove(max--);&#13;
        }&#13;
&#13;
        return re;&#13;
    }&#13;
}&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/zui-da-pin-lv-zhan.html</guid><pubDate>Tue, 17 Sep 2024 18:40:34 +0000</pubDate></item><item><title>扫描线</title><link>https://iamdurant.github.io/post/sao-miao-xian.html</link><description>未接触。</description><guid isPermaLink="true">https://iamdurant.github.io/post/sao-miao-xian.html</guid><pubDate>Mon, 16 Sep 2024 14:19:04 +0000</pubDate></item><item><title>线段树</title><link>https://iamdurant.github.io/post/xian-duan-shu.html</link><description>#### 手搓两套线段树板子&#13;
&#13;
- **可以区间修改/求和的线段树模板**&#13;
```java&#13;
class SegmentTree {&#13;
    int[] arr;&#13;
    int[] d;&#13;
    int[] b;&#13;
    boolean[] v;&#13;
&#13;
    public SegmentTree(int[] arr) {&#13;
        this.arr = arr;&#13;
        d = new int[arr.length * 4];&#13;
        b = new int[arr.length * 4];&#13;
        v = new boolean[arr.length * 4];&#13;
&#13;
        buildTree(0, arr.length - 1, 0, d, arr);&#13;
    }&#13;
&#13;
    private void buildTree(int s, int t, int p, int[] d, int[] arr) {&#13;
        if(s == t) {&#13;
            d[p] = arr[s];&#13;
            return;&#13;
        }&#13;
&#13;
        int m = (s + t) &gt;&gt; 1;&#13;
        buildTree(s, m, 2 * p + 1, d, arr);&#13;
        buildTree(m + 1, t, 2 * p + 2, d, arr);&#13;
        d[p] = d[2 * p + 1] + d[2 * p + 2];&#13;
    }&#13;
&#13;
    public void rangeUpdate(int l, int r, int c) {&#13;
        rangeUpdate(l, r, c, 0, arr.length - 1, 0, d, b, v);&#13;
    }&#13;
&#13;
    private void rangeUpdate(int l, int r, int c, int s, int t, int p, int[] d, int[] b, boolean[] v) {&#13;
        if(s &gt;= l &amp;&amp; t &lt;= r) {&#13;
            d[p] = c * ((t - s) + 1);&#13;
            b[p] = c;&#13;
            v[p] = true;&#13;
            return;&#13;
        }&#13;
&#13;
        int m = (s + t) &gt;&gt; 1;&#13;
        if(v[p]) {&#13;
            tagDown(d, b, v, m, s, t, p);&#13;
        }&#13;
&#13;
        if(m &gt;= l) rangeUpdate(l, r, c, s, m, 2 * p + 1, d, b, v);&#13;
        if(m + 1 &lt;= r) rangeUpdate(l, r, c, m + 1, t, 2 * p + 2, d, b, v);&#13;
        d[p] = d[2 * p + 1] + d[2 * p + 2];&#13;
    }&#13;
&#13;
    public int rangeSum(int l, int r) {&#13;
        return rangeSum(l, r, 0, arr.length - 1, 0, d, b, v);&#13;
    }&#13;
&#13;
    private int rangeSum(int l, int r, int s, int t, int p, int[] d, int[] b, boolean[] v) {&#13;
        if(s &gt;= l &amp;&amp; t &lt;= r) {&#13;
            return d[p];&#13;
        }&#13;
&#13;
        int sum = 0;&#13;
        int m = (s + t) &gt;&gt; 1;&#13;
        if(v[p]) {&#13;
            tagDown(d, b, v, m, s, t, p);&#13;
        }&#13;
&#13;
        if(m &gt;= l) sum += rangeSum(l, r, s, m, 2 * p + 1, d, b, v);&#13;
        if(m + 1 &lt;= r) sum += rangeSum(l, r, m + 1, t, 2 * p + 2, d, b, v);&#13;
&#13;
        return sum;&#13;
    }&#13;
&#13;
    private void tagDown(int[] d, int[] b, boolean[] v, int m, int s, int t, int p) {&#13;
        d[2 * p + 1] = b[p] * ((m - s) + 1);&#13;
        d[2 * p + 2] = b[p] * ((t - (m + 1)) + 1);&#13;
        b[2 * p + 1] = b[p];&#13;
        b[2 * p + 2] = b[p];&#13;
        v[2 * p + 1] = true;&#13;
        v[2 * p + 2] = true;&#13;
        b[p] = 0;&#13;
        v[p] = false;&#13;
    }&#13;
}&#13;
```&#13;
&#13;
- **可以区间加/求和的线段树模板**&#13;
```java&#13;
class SegmentTree {&#13;
    int[] arr;&#13;
    int[] d;&#13;
    int[] b;&#13;
&#13;
    public SegmentTree(int[] arr) {&#13;
        this.arr = arr;&#13;
        d = new int[arr.length * 4];&#13;
        b = new int[arr.length * 4];&#13;
&#13;
        // build tree&#13;
        buildSegmentTree(0, arr.length - 1, 0, arr, d);&#13;
    }&#13;
&#13;
    /**&#13;
     * 线段树初始化&#13;
     *&#13;
     * @param s     区间左边界&#13;
     * @param t     区间有边界&#13;
     * @param p     区间编号&#13;
     * @param arr   原始数组&#13;
     * @param d     线段树树状数组&#13;
     */&#13;
    private void buildSegmentTree(int s, int t, int p, int[] arr, int[] d) {&#13;
        if(s == t) {&#13;
            d[p] = arr[s];&#13;
            return;&#13;
        }&#13;
&#13;
        int m = (s + t) &gt;&gt; 1;&#13;
        buildSegmentTree(s, m, 2 * p + 1, arr, d);&#13;
        buildSegmentTree(m + 1, t, 2 * p + 2, arr , d);&#13;
        d[p] = d[2 * p + 1] + d[2 * p + 2];&#13;
    }&#13;
&#13;
    /**&#13;
     * 外部调用方法&#13;
     *&#13;
     * @param l     更新左边界&#13;
     * @param r     更新右边界&#13;
     * @param c     增量&#13;
     */&#13;
    public void rangeUpdate(int l, int r, int c) {&#13;
        rangeUpdate(l, r, c, 0, arr.length - 1, 0, d, b);&#13;
    }&#13;
&#13;
    /**&#13;
     *&#13;
     * @param l     更新左边界&#13;
     * @param r     更新右边界&#13;
     * @param c     增量&#13;
     * @param s     区间左边界&#13;
     * @param t     区间右边界&#13;
     * @param p     区间编号&#13;
     * @param d     线段树树状数组&#13;
     * @param b     标记数组&#13;
     */&#13;
    private void rangeUpdate(int l, int r, int c, int s, int t, int p, int[] d, int[] b) {&#13;
        if(s &gt;= l &amp;&amp; t &lt;= r) {&#13;
            d[p] += c * ((t - s) + 1);&#13;
            b[p] += c;&#13;
            return;&#13;
        }&#13;
&#13;
        int m = (s + t) &gt;&gt; 1;&#13;
        if(s != t &amp;&amp; b[p] != 0) {&#13;
            tagDown(d, b, m, s, t, p);&#13;
        }&#13;
&#13;
        if(m &gt;= l) rangeUpdate(l, r, c, s, m, 2 * p + 1, d, b);&#13;
        if(m + 1 &lt;= r) rangeUpdate(l, r, c, m + 1, t, 2 * p + 2, d, b);&#13;
        d[p] = d[2 * p + 1] + d[2 * p + 2];&#13;
    }&#13;
&#13;
    /**&#13;
     *  外部调用方法&#13;
     * @param l     查询左边界&#13;
     * @param r     查询右边界&#13;
     * @return      区间和&#13;
     */&#13;
    public int rangeSum(int l, int r) {&#13;
        return rangeSum(l, r, 0, arr.length - 1, 0, d, b);&#13;
    }&#13;
&#13;
    /**&#13;
     *&#13;
     * @param l     查询左边界&#13;
     * @param r     查询右边界&#13;
     * @param s     区间左边界&#13;
     * @param t     区间右边界&#13;
     * @param p     区间编号&#13;
     * @param d     线段树树状数组&#13;
     * @param b     标记数组&#13;
     * @return      区间和&#13;
     */&#13;
    private int rangeSum(int l, int r, int s, int t, int p, int[] d, int[] b) {&#13;
        if(s &gt;= l &amp;&amp; t &lt;= r) {&#13;
            return d[p];&#13;
        }&#13;
&#13;
        int sum = 0;&#13;
        int m = (s + t) &gt;&gt; 1;&#13;
        if(s != t &amp;&amp; b[p] != 0) {&#13;
            tagDown(d, b, m, s, t, p);&#13;
        }&#13;
&#13;
        if(m &gt;= l) sum += rangeSum(l, r, s, m, 2 * p + 1, d, b);&#13;
        if(m + 1 &lt;= r) sum += rangeSum(l, r, m + 1, t, 2 * p + 2, d, b);&#13;
&#13;
        return sum;&#13;
    }&#13;
&#13;
    private void tagDown(int[] d, int[] b, int m, int s, int t, int p) {&#13;
        d[2 * p + 1] += b[p] * ((m - s) + 1);&#13;
        d[2 * p + 2] += d[p] * ((t - (m + 1)) + 1);&#13;
        b[2 * p + 1] += b[p];&#13;
        b[2 * p + 2] += b[p];&#13;
        b[p] = 0;&#13;
    }&#13;
}&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/xian-duan-shu.html</guid><pubDate>Mon, 16 Sep 2024 14:18:39 +0000</pubDate></item><item><title>差分数组（一维、二维）</title><link>https://iamdurant.github.io/post/cha-fen-shu-zu-%EF%BC%88-yi-wei-%E3%80%81-er-wei-%EF%BC%89.html</link><description>#### 一维差分&#13;
差分map写法：&#13;
- 主要用到treemap的merge方法&#13;
```java&#13;
public boolean carPooling(int[][] trips, int capacity) {&#13;
        TreeMap&lt;Integer, Integer&gt; d = new TreeMap&lt;&gt;();&#13;
        for (int[] t : trips) {&#13;
            int num = t[0], from = t[1], to = t[2];&#13;
            d.merge(from, num, Integer::sum);&#13;
            d.merge(to, -num, Integer::sum);&#13;
        }&#13;
        int s = 0;&#13;
        for (int v : d.values()) {&#13;
            s += v;&#13;
            if (s &gt; capacity) {&#13;
                return false;&#13;
            }&#13;
        }&#13;
        return true;&#13;
    }&#13;
```&#13;
&#13;
#### 二维差分&#13;
##### 原理图&#13;
- 初始化&#13;
![二维差分原理图](https://pic.leetcode.cn/1702439895-HZofag-LC2132-c.png)&#13;
&#13;
- 恢复&#13;
  - 对行依次做前缀和&#13;
  - 对列依次做前缀和&#13;
&#13;
&#13;
#### 离散化 + 二维差分&#13;
[leetcode：LCP74.最强祝福力场](https://leetcode.cn/problems/xepqZ5/)&#13;
此题困惑点：&#13;
- 存在非整数 ，解决：将所有坐标 * 2&#13;
- 坐标范围很大，解决：离散化&#13;
  - 离散化步骤：&#13;
    - 排序&#13;
    - 去重&#13;
    - 重新编号&#13;
&#13;
。</description><guid isPermaLink="true">https://iamdurant.github.io/post/cha-fen-shu-zu-%EF%BC%88-yi-wei-%E3%80%81-er-wei-%EF%BC%89.html</guid><pubDate>Sat, 14 Sep 2024 14:31:36 +0000</pubDate></item><item><title>二维前缀和</title><link>https://iamdurant.github.io/post/er-wei-qian-zhui-he.html</link><description>求矩阵的子矩阵和
- 方法一：对矩阵的每一行做前缀和，求子矩阵元素和时还需要遍历每一行，并不能做到O(1)的查询时间

- 方法二：预处理二维矩阵前缀和，将每次查询优化到O(1)
![原理图](https://pic.leetcode.cn/1765887873-gHCBKd-lc304-c.png)。</description><guid isPermaLink="true">https://iamdurant.github.io/post/er-wei-qian-zhui-he.html</guid><pubDate>Fri, 13 Sep 2024 13:52:47 +0000</pubDate></item><item><title>位运算</title><link>https://iamdurant.github.io/post/wei-yun-suan.html</link><description>#### n &amp; n - 1&#13;
`n &amp; n - 1`  n减去最低位的2的幂&#13;
&#13;
#### n &amp; -n&#13;
`n &amp; -n`  获取组成n的二进制中的最低位的2的幂。</description><guid isPermaLink="true">https://iamdurant.github.io/post/wei-yun-suan.html</guid><pubDate>Fri, 13 Sep 2024 11:37:36 +0000</pubDate></item><item><title>快速幂</title><link>https://iamdurant.github.io/post/kuai-su-mi.html</link><description>#### 快速幂实现&#13;
&#13;
- 时间复杂度：O(logn)&#13;
- 空间复杂度：O(logn) 栈的开销&#13;
&#13;
```java&#13;
private int fastPow(int val, int p) {&#13;
        if(p == 0) return 1;&#13;
        if(p == 1) return val;&#13;
&#13;
        int half = fastPow(val, p &gt;&gt; 1);&#13;
        int result;&#13;
        if((p &amp; 1) == 0) result = half * half;&#13;
        else result = half * half * val;&#13;
&#13;
        return result;&#13;
    }&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/kuai-su-mi.html</guid><pubDate>Tue, 10 Sep 2024 16:15:51 +0000</pubDate></item><item><title>设计模式</title><link>https://iamdurant.github.io/post/she-ji-mo-shi.html</link><description>#### 创建型模式

##### 单例

**常见的单例：**

- `RunTime`

- Spring中的`ApplicationContext`

```java
public class SingleTon {
    private static volatile SingleTon single;

    private SingleTon() {

    }

    public static SingleTon getInstance() {
        if(single == null) {
            synchronized(SingleTon.class) {
                if(single == null) single = new SingleTon();
            }
        }

        return single;
    }
}
```



##### 简单工厂

**主要角色：**

- 抽象产品
- 具体产品
- 简单工厂

&gt; 所有产品都交由简单工厂创建，若需新增产品，则需要改动简单工厂的代码

```java
// 抽象产品
public interface Phone {
}

// 具体产品
public class XiaoMi implements Phone{
}

// 具体产品
public class HuaWei implements Phone{
}

// 简单工厂
public class PhoneFactory {
    public static Phone createPhone(String type) {
        if(type == null) return null;
        if(type.equals('XiaoMi')) return new XiaoMi();
        else if(type.equals('HuaWei')) return new HuaWei();
        return null;
    }
}
```



##### 工厂方法

**主要角色：**

- 抽象产品
- 具体产品
- 抽象工厂
- 具体工厂

&gt; 对于简单工厂，新增产品需要改动代码。</description><guid isPermaLink="true">https://iamdurant.github.io/post/she-ji-mo-shi.html</guid><pubDate>Sat, 13 Jul 2024 16:13:53 +0000</pubDate></item><item><title>dubbo</title><link>https://iamdurant.github.io/post/dubbo.html</link><guid isPermaLink="true">https://iamdurant.github.io/post/dubbo.html</guid><pubDate>Fri, 12 Jul 2024 04:02:50 +0000</pubDate></item><item><title>Mysql</title><link>https://iamdurant.github.io/post/Mysql.html</link><description>**行记录格式**&#13;
- antelope&#13;
  - redundant&#13;
  - compact&#13;
- barracuda&#13;
    - dynamic&#13;
    - compressed&#13;
&#13;
**compact**&#13;
可变长字段长度(1或2个byte)    NULL值(bitmap)    头部(5个byte)&#13;
&#13;
**三个隐藏字段**&#13;
- ROWID(6 byte)，在没有指定primary key时才存在&#13;
- 事务ID(6 byte)&#13;
- 回滚指针(7 byte)&#13;
&#13;
**buffer pool**&#13;
由控制块与缓存页组成&#13;
&#13;
Hash&#13;
```text&#13;
由表空间及页号组成key，控制块引用为value，O(1)的时间判断要查询的数据是否位于buffer pool&#13;
```&#13;
&#13;
各链表及其作用&#13;
- free链&#13;
```text&#13;
双向链表，节点存储缓存页等信息&#13;
```&#13;
- Flush链&#13;
```text&#13;
双向链表，节点存储缓存页等信息，当数据被更新时，被标记为dirty page，相应的控制块加入flush链，由后台线程异步刷盘&#13;
```&#13;
- LRU链&#13;
```text&#13;
双向链表，节点存储缓存页等信息，分为young数据区、old 数据区，当buffer pool满时，淘汰数据&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/Mysql.html</guid><pubDate>Fri, 12 Jul 2024 04:02:07 +0000</pubDate></item><item><title>RocketMQ</title><link>https://iamdurant.github.io/post/RocketMQ.html</link><description># RocketMQ api实战&#13;
&#13;
### Consumer&#13;
&#13;
- 推模式：broker推送消息给consumer&#13;
- 拉模式：consumer主动从broker拉取消息&#13;
&#13;
&gt; 推模式消费者&#13;
&#13;
```java&#13;
package com.mq.consumer;&#13;
&#13;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;&#13;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;&#13;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;&#13;
&#13;
&#13;
public class SyncConsumer {&#13;
    public static void main(String[] args) throws Exception{&#13;
        // 推模式(boroker(主动推送) -&gt; consumer)&#13;
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer('SyncConsumer');&#13;
&#13;
        // set name server addr&#13;
        consumer.setNamesrvAddr('172.24.192.134:9876');&#13;
&#13;
        // subscribe&#13;
        consumer.subscribe('wwb', '*');&#13;
&#13;
        // consume message&#13;
        consumer.setMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -&gt; {&#13;
            for(int i = 0;i &lt; list.size();i++) {&#13;
                System.out.println('消费一条消息');&#13;
            }&#13;
&#13;
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;&#13;
        });&#13;
&#13;
        consumer.start();&#13;
        System.out.println('消费者启动成功...');&#13;
    }&#13;
}&#13;
```&#13;
&#13;
&gt; 拉模式消费者（@deprecated）&#13;
&#13;
```java&#13;
package com.mq.consumer;&#13;
&#13;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;&#13;
import org.apache.rocketmq.client.consumer.PullResult;&#13;
import org.apache.rocketmq.client.consumer.PullStatus;&#13;
import org.apache.rocketmq.client.consumer.store.ReadOffsetType;&#13;
import org.apache.rocketmq.client.exception.MQBrokerException;&#13;
import org.apache.rocketmq.client.exception.MQClientException;&#13;
import org.apache.rocketmq.common.message.MessageQueue;&#13;
import org.apache.rocketmq.remoting.exception.RemotingException;&#13;
&#13;
import java.util.HashSet;&#13;
import java.util.List;&#13;
import java.util.Set;&#13;
&#13;
public class PullConsumer {&#13;
    @Deprecated&#13;
    public static void main(String[] args) throws Exception{&#13;
        DefaultMQPullConsumer consumer = new DefaultMQPullConsumer('PullConsumer');&#13;
&#13;
        // set name server addr&#13;
        consumer.setNamesrvAddr('172.24.192.134:9876');&#13;
&#13;
        // set register topic&#13;
        consumer.setRegisterTopics(new HashSet&lt;&gt;(List.of('wwb', 'async_wwb', 'TopicTest')));&#13;
&#13;
        consumer.start();&#13;
&#13;
        while(true) {&#13;
            consumer.getRegisterTopics().forEach(t -&gt; {&#13;
                try {&#13;
                    Set&lt;MessageQueue&gt; queues = consumer.fetchSubscribeMessageQueues(t);&#13;
                    queues.forEach(q -&gt; {&#13;
                        try {&#13;
                            long offset = consumer.getOffsetStore().readOffset(q, ReadOffsetType.READ_FROM_MEMORY);&#13;
                            if(offset &lt; 0) offset = consumer.getOffsetStore().readOffset(q, ReadOffsetType.READ_FROM_STORE);&#13;
                            if(offset &lt; 0) offset = consumer.maxOffset(q);&#13;
                            if(offset &lt; 0) offset = 0;&#13;
&#13;
                            PullResult result = consumer.pull(q, '*', offset, 32);&#13;
                            if(result.getPullStatus() == PullStatus.FOUND) {&#13;
                                System.out.println('消息消费成功');&#13;
                                consumer.updateConsumeOffset(q, result.getNextBeginOffset());&#13;
                            }&#13;
                        } catch (MQClientException | MQBrokerException | RemotingException | InterruptedException e) {&#13;
                            throw new RuntimeException(e);&#13;
                        }&#13;
                    });&#13;
                } catch (MQClientException e) {&#13;
                    throw new RuntimeException(e);&#13;
                }&#13;
            });&#13;
        }&#13;
    }&#13;
}&#13;
&#13;
```&#13;
&#13;
&gt; 拉模式消费者&#13;
&#13;
```java&#13;
package com.mq.consumer;&#13;
&#13;
import org.apache.rocketmq.client.consumer.DefaultLitePullConsumer;&#13;
import org.apache.rocketmq.common.message.MessageExt;&#13;
import org.apache.rocketmq.common.message.MessageQueue;&#13;
&#13;
import java.nio.charset.StandardCharsets;&#13;
import java.util.ArrayList;&#13;
import java.util.Collection;&#13;
import java.util.List;&#13;
&#13;
public class LitePullConsumer {&#13;
    public static void main(String[] args) throws Exception{&#13;
        pullAssignQueue();&#13;
    }&#13;
&#13;
    /**&#13;
     * 随机拉取某个队列的消息&#13;
     * @throws Exception&#13;
     */&#13;
    static void pullRandomQueue() throws Exception{&#13;
        DefaultLitePullConsumer consumer = new DefaultLitePullConsumer('LitePullConsumer');&#13;
        consumer.setNamesrvAddr('172.24.192.134:9876');&#13;
        consumer.subscribe('wwb', '*');&#13;
&#13;
        consumer.start();&#13;
&#13;
        while(true) {&#13;
            List&lt;MessageExt&gt; list = consumer.poll();&#13;
            list.forEach(m -&gt; {&#13;
                byte[] body = m.getBody();&#13;
                System.out.println('消息消费成功: ' + new String(body, StandardCharsets.UTF_8));&#13;
            });&#13;
        }&#13;
    }&#13;
&#13;
    /**&#13;
     * 指定拉取某个队列的消息&#13;
     * @throws Exception&#13;
     */&#13;
    static void pullAssignQueue() throws Exception{&#13;
        DefaultLitePullConsumer consumer = new DefaultLitePullConsumer('LitePullConsumer2');&#13;
        consumer.setNamesrvAddr('172.24.192.134:9876');&#13;
        consumer.start();&#13;
&#13;
        Collection&lt;MessageQueue&gt; queues = consumer.fetchMessageQueues('wwb');&#13;
        ArrayList&lt;MessageQueue&gt; li = new ArrayList&lt;&gt;(queues);&#13;
        consumer.assign(li);&#13;
        consumer.seek(li.get(1), 0);&#13;
&#13;
        while(true) {&#13;
            List&lt;MessageExt&gt; list = consumer.poll();&#13;
            list.forEach(m -&gt; {&#13;
                System.out.println(m);&#13;
                System.out.println('消息消费成功: ' + new String(m.getBody(), StandardCharsets.UTF_8));&#13;
            });&#13;
        }&#13;
    }&#13;
}&#13;
```&#13;
&#13;
&#13;
&#13;
### Producer&#13;
&#13;
**消息发送的三种方式**&#13;
&#13;
- 同步发送：等待消息返回后再继续进行下面的操作，适用于并发较低以及可靠性较高的场景&#13;
- 异步发送：不等待消息返回直接进行后续代码流程，适用于并发高的场景&#13;
- 单向发送：只负责发送，不管消息是否发送成功，适用于日志等特殊场景&#13;
&#13;
&#13;
&#13;
&gt; 同步生产者：&#13;
&#13;
```java&#13;
package com.mq.producer;&#13;
&#13;
import org.apache.rocketmq.client.producer.DefaultMQProducer;&#13;
import org.apache.rocketmq.client.producer.SendResult;&#13;
import org.apache.rocketmq.common.message.Message;&#13;
&#13;
import java.nio.charset.StandardCharsets;&#13;
import java.util.Random;&#13;
&#13;
public class SyncProducer {&#13;
    public static void main(String[] args) throws Exception{&#13;
        DefaultMQProducer producer = new DefaultMQProducer('SyncProducer');&#13;
&#13;
        // set name server addr&#13;
        producer.setNamesrvAddr('172.24.192.134:9876');&#13;
&#13;
        producer.start();&#13;
&#13;
        for(int i = 0;i &lt; 10;i++) {&#13;
            Message mess = new Message(&#13;
                    'wwb',&#13;
                    String.valueOf(i),&#13;
                    String.valueOf(new Random().nextInt(10)).getBytes(StandardCharsets.UTF_8));&#13;
            SendResult result = producer.send(mess);&#13;
            System.out.println(result);&#13;
        }&#13;
&#13;
        producer.shutdown();&#13;
    }&#13;
}&#13;
```&#13;
&#13;
&#13;
&gt; 异步生产者&#13;
&#13;
```java&#13;
package com.mq.producer;&#13;
&#13;
import org.apache.rocketmq.client.producer.DefaultMQProducer;&#13;
import org.apache.rocketmq.client.producer.SendCallback;&#13;
import org.apache.rocketmq.client.producer.SendResult;&#13;
import org.apache.rocketmq.common.message.Message;&#13;
&#13;
import java.nio.charset.StandardCharsets;&#13;
import java.util.Arrays;&#13;
import java.util.Random;&#13;
import java.util.concurrent.CountDownLatch;&#13;
import java.util.concurrent.TimeUnit;&#13;
&#13;
public class AsyncProducer {&#13;
    public static void main(String[] args) throws Exception{&#13;
        DefaultMQProducer producer = new DefaultMQProducer('AsyncProducer');&#13;
&#13;
        // set name server addr&#13;
        producer.setNamesrvAddr('172.24.192.134:9876');&#13;
&#13;
        producer.start();&#13;
        CountDownLatch cd = new CountDownLatch(10);&#13;
        for(int i = 0;i &lt; 10;i++) {&#13;
            Message mess = new Message(&#13;
                    'async_wwb',&#13;
                    String.valueOf(i),&#13;
                    String.valueOf(new Random().nextInt(10)).getBytes(StandardCharsets.UTF_8));&#13;
            producer.send(mess, new SendCallback() {&#13;
                @Override&#13;
                public void onSuccess(SendResult sendResult) {&#13;
                    System.out.println('消息发送成功: ' + sendResult);&#13;
                    cd.countDown();&#13;
                }&#13;
&#13;
                @Override&#13;
                public void onException(Throwable throwable) {&#13;
                    System.out.println('消息发送失败' + Arrays.toString(throwable.getStackTrace()));&#13;
                    cd.countDown();&#13;
                }&#13;
            });&#13;
        }&#13;
&#13;
        boolean await = cd.await(5, TimeUnit.SECONDS);&#13;
        if(!await) {&#13;
            System.out.println('部分消息丢失');&#13;
        }&#13;
        producer.shutdown();&#13;
    }&#13;
}&#13;
```&#13;
&#13;
&gt; 单向生产者&#13;
&#13;
```java&#13;
package com.mq.producer;&#13;
&#13;
import org.apache.rocketmq.client.producer.DefaultMQProducer;&#13;
import org.apache.rocketmq.common.message.Message;&#13;
&#13;
import java.nio.charset.StandardCharsets;&#13;
import java.util.Random;&#13;
&#13;
public class OneWayProducer {&#13;
    public static void main(String[] args) throws Exception{&#13;
        DefaultMQProducer producer = new DefaultMQProducer('OneWayProducer');&#13;
&#13;
        // set name server addr&#13;
        producer.setNamesrvAddr('172.24.192.134:9876');&#13;
&#13;
        producer.start();&#13;
&#13;
        for(int i = 0;i &lt; 10;i++) {&#13;
            Message mess = new Message(&#13;
                    'wwb',&#13;
                    String.valueOf(i),&#13;
                    String.valueOf(new Random().nextInt(10)).getBytes(StandardCharsets.UTF_8));&#13;
            producer.sendOneway(mess);&#13;
        }&#13;
&#13;
        producer.shutdown();&#13;
    }&#13;
}&#13;
```&#13;
&#13;
&#13;
&#13;
### 消息类型&#13;
&#13;
- **Normal（普通消息）**&#13;
- **FIFO（顺序消息）**&#13;
- **Delay（延迟消息）**&#13;
&#13;
延迟消息具有18个级别：1s到2个小时&#13;
&#13;
几个有关的方法：&#13;
&#13;
1. setDelayTimeLevel() ：根据延迟级别延迟投递&#13;
&#13;
2. setDelayTimeMs() ：自定义延迟毫秒数&#13;
&#13;
3. setDelayTimeSec() ：自定义延迟秒数&#13;
&#13;
4. setDeliverTimeMs() ：在某个时间戳投递&#13;
&#13;
|延迟级别|延迟时间|&#13;
|--- |--- |&#13;
|1|1s|&#13;
|2|5s|&#13;
|3|10s|&#13;
|4|30s|&#13;
|5|1m|&#13;
|6|2m|&#13;
|7|3m|&#13;
|8|4m|&#13;
|9|5m|&#13;
|10|6m|&#13;
|11|7m|&#13;
|12|8m|&#13;
|13|9m|&#13;
|14|10m|&#13;
|15|20m|&#13;
|16|30m|&#13;
|17|1h|&#13;
|18|2h|&#13;
&#13;
- **Transaction（事务消息）**&#13;
&#13;
RocketMQ 的事务消息用于保证消息和本地事务操作的最终一致性。</description><guid isPermaLink="true">https://iamdurant.github.io/post/RocketMQ.html</guid><pubDate>Fri, 12 Jul 2024 04:01:49 +0000</pubDate></item><item><title>maven</title><link>https://iamdurant.github.io/post/maven.html</link><description>maven的本质是一个项目管理工具，将项目开发和管理抽象成一个项目对象模型（POM）&#13;
POM（Project Object Model）：项目对象模型&#13;
&#13;
**仓库类型**&#13;
- 本地仓库&#13;
- 私服&#13;
- 中央仓库&#13;
&#13;
**坐标**&#13;
- groupId&#13;
- artifactId&#13;
- version&#13;
&#13;
**command**&#13;
- mvn clean&#13;
- mvn compile&#13;
- mvn test&#13;
- mvn package&#13;
- mvn install&#13;
&#13;
**依赖传递性**&#13;
- 直接依赖：直接通过依赖配置建立的依赖关系&#13;
- 间接依赖：被依赖的资源的依赖&#13;
&#13;
**依赖传递冲突问题**&#13;
- 路劲优先：当依赖中出现冲突，层级越深，优先级越低&#13;
- 声明优先：当冲突层级相同，root先声明优先级高&#13;
&#13;
**依赖范围**&#13;
- compile 默认&#13;
- provided 源码 测试&#13;
- test 测试&#13;
- runtime 打包&#13;
&#13;
**模块聚合**&#13;
```xml&#13;
&lt;packaging&gt;pom&lt;/packaging&gt;&#13;
&lt;modules&gt;&#13;
    &lt;module&gt;&lt;/module&gt;&#13;
    &lt;module&gt;&lt;/module&gt;&#13;
&lt;/modules&gt;&#13;
```&#13;
&#13;
**自定义属性**&#13;
```xml&#13;
&lt;properties&gt;&#13;
    &lt;spring.web.version&gt;&lt;/spring.web.version&gt;&#13;
    &lt;redis.version&gt;&lt;/redis.version&gt;&#13;
&lt;/properties&gt;&#13;
```&#13;
&#13;
**pom属性解析到配置文件**&#13;
```xml&#13;
&lt;resources&gt;&#13;
    &lt;resource&gt;&#13;
        &lt;directory&gt;配置文件目录&lt;/directory&gt;&#13;
        &lt;filtering&gt;true&lt;/filtering&gt;&#13;
    &lt;resource&gt;&#13;
&lt;/resources&gt;&#13;
```&#13;
&#13;
**跳过测试**&#13;
```text&#13;
mvn package -D skipTests&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/maven.html</guid><pubDate>Fri, 12 Jul 2024 02:12:03 +0000</pubDate></item><item><title>spring-boot-data-elasticsearch</title><link>https://iamdurant.github.io/post/spring-boot-data-elasticsearch.html</link><description>##### spring-boot-data-elasticsearch&#13;
**依赖**&#13;
```xml&#13;
&lt;dependency&gt;&#13;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&#13;
    &lt;artifactId&gt;spring-boot-starter-data-elasticsearch&lt;/artifactId&gt;&#13;
    &lt;version&gt;2.7.6&lt;/version&gt;&#13;
&lt;/dependency&gt;&#13;
```&#13;
&#13;
**annotation**&#13;
```java&#13;
@Repository&#13;
@EnableElasticSearchRepositories(basePackage = '')&#13;
@Document(indexName = ''， createIndex = true)  标注实体类&#13;
@Id  标注主键&#13;
@Field(type = FieldType.Text, analyzer = 'ik_max_word')  声明字段类型以及其他信息&#13;
```&#13;
&#13;
**interface**&#13;
```java&#13;
@Repository&#13;
public interface EsSubjectRepo extends ElasticsearchRepository&lt;EsSubjectInfo, Long&gt; {&#13;
&#13;
}&#13;
```&#13;
&#13;
**代码demo**&#13;
```java&#13;
@Service&#13;
public class EsSubjectInfoServiceImpl implements EsSubjectInfoService {&#13;
    @Resource&#13;
    private EsSubjectRepo repo;&#13;
&#13;
    @Resource&#13;
    private ElasticsearchRestTemplate restTemplate;&#13;
&#13;
    @Override&#13;
    public void createIndex() {&#13;
        IndexOperations indexOps = restTemplate.indexOps(SubjectInfoEs.class);&#13;
        indexOps.create();&#13;
        Document mapping = indexOps.createMapping(SubjectInfoEs.class);&#13;
        indexOps.putMapping(mapping);&#13;
    }&#13;
&#13;
    @Override&#13;
    public void addDocs() {&#13;
        List&lt;SubjectInfoEs&gt; subjectInfoEs = List.of(&#13;
                new SubjectInfoEs(1L, 'mysql是什么', 'mysql是关系型数据库', 'wwb', new Date()),&#13;
                new SubjectInfoEs(2L, 'redis是什么', 'redis是基于内存的键值对型数据库', 'wwb', new Date()),&#13;
                new SubjectInfoEs(3L, 'es是什么', 'es是搜索引擎', 'wwb', new Date()));&#13;
&#13;
        repo.saveAll(subjectInfoEs);&#13;
    }&#13;
&#13;
    @Override&#13;
    public void find() {&#13;
        Iterable&lt;SubjectInfoEs&gt; all = repo.findAll();&#13;
        for (SubjectInfoEs subjectInfoEs : all) {&#13;
            System.out.println(subjectInfoEs);&#13;
        }&#13;
    }&#13;
&#13;
    @Override&#13;
    public void search() {&#13;
        NativeSearchQuery query = new NativeSearchQueryBuilder()&#13;
                .withQuery(QueryBuilders.matchQuery('createUser', 'wwb'))&#13;
                .build();&#13;
&#13;
        SearchHits&lt;SubjectInfoEs&gt; searched = restTemplate.search(query, SubjectInfoEs.class);&#13;
&#13;
        Iterator&lt;SearchHit&lt;SubjectInfoEs&gt;&gt; iterator = searched.stream().iterator();&#13;
        while (iterator.hasNext()) {&#13;
            SearchHit&lt;SubjectInfoEs&gt; next = iterator.next();&#13;
            SubjectInfoEs content = next.getContent();&#13;
            System.out.println(content.getSubjectName());&#13;
        }&#13;
    }&#13;
}&#13;
```。</description><guid isPermaLink="true">https://iamdurant.github.io/post/spring-boot-data-elasticsearch.html</guid><pubDate>Mon, 08 Jul 2024 19:55:42 +0000</pubDate></item><item><title>git</title><link>https://iamdurant.github.io/post/git.html</link><description>### 概念图&#13;
![image](https://github.com/iamdurant/iamdurant.github.io/assets/107034526/54916701-a7d3-4c9a-b465-b69d0be73426)&#13;
&#13;
![image](https://github.com/iamdurant/iamdurant.github.io/assets/107034526/a25ae0ec-eea1-4aa8-b86c-b9bae5f8d9af)&#13;
&#13;
### 基础&#13;
&#13;
#### 设置用户信息&#13;
`git config --global user.email '123@bbq.com'`&#13;
`git config --global user.name 'kevin'`&#13;
&#13;
#### git log&#13;
`alias gl='git log --pretty=oneline --all --graph --abbrev-commit'`&#13;
`git reflog`&#13;
&#13;
#### 初始化仓库&#13;
`git init`&#13;
&#13;
#### staged&#13;
`git add &lt;file&gt;...`&#13;
&#13;
#### 查看状态&#13;
`git status`&#13;
&#13;
#### 提交&#13;
`git commit -m '提交描述信息'`&#13;
&#13;
#### 版本回退&#13;
`git reset --hard commitID`&#13;
&#13;
#### .gitignore&#13;
```text&#13;
.gitignore&#13;
*.class&#13;
*.iml&#13;
*.idea&#13;
```&#13;
&#13;
### 分支相关&#13;
&#13;
#### 查看分支&#13;
`git branch`&#13;
&#13;
#### 创建分支&#13;
`git branch &lt;branch_name&gt;`&#13;
&#13;
#### 删除分支&#13;
`git branch -d &lt;branch_name&gt;`&#13;
`git branch -D &lt;branch_name&gt;` *强制删除*&#13;
&#13;
#### 切换分支&#13;
```text&#13;
git checkout &lt;branch_name&gt;&#13;
git switch &lt;branch_name&gt;&#13;
```&#13;
&#13;
#### 创建并切换分支&#13;
`git checkout -b &lt;branch_name&gt;`&#13;
&#13;
#### 合并分支&#13;
`git merge &lt;be_merge_branch_name&gt;`&#13;
&#13;
#### 通用分支使用图&#13;
![image](https://github.com/iamdurant/iamdurant.github.io/assets/107034526/f0a1c210-5152-4ac9-a989-1e42fa39a0f8)&#13;
&#13;
### 远程仓库相关&#13;
&#13;
#### 关联仓库&#13;
`git remote add &lt;name&gt; &lt;git_url&gt;`&#13;
&#13;
#### 关联分支&#13;
```text&#13;
git branch --set-upstream=&lt;remote_repository_name&gt;/&lt;remote_branch_name&gt; &lt;current_branch_name&gt;&#13;
```&#13;
&#13;
#### 查看关联仓库&#13;
`git remote`&#13;
&#13;
#### 关联分支&#13;
`git push --set-upstream`&#13;
&#13;
#### 推送代码&#13;
`git push &lt;remote_repository_name&gt; &lt;local_branch&gt;:&lt;remote_branch&gt;`&#13;
&#13;
#### 拉代码&#13;
```text&#13;
git fetch &lt;repository_name&gt; &lt;branch_name&gt;     抓取分支，不合并，若不指定branch，则抓取所有branch&#13;
git pull &lt;repository_name&gt; &lt;branch_name&gt;       拉取分支，合并，若不指定branch，则拉取所有分支&#13;
```&#13;
。</description><guid isPermaLink="true">https://iamdurant.github.io/post/git.html</guid><pubDate>Mon, 08 Jul 2024 01:01:56 +0000</pubDate></item><item><title>编译redis、mysql、nginx</title><link>https://iamdurant.github.io/post/bian-yi-redis%E3%80%81mysql%E3%80%81nginx.html</link><description>#### 环境&#13;
设备：redmi6 android linux-deploy chroot&#13;
架构：arm32&#13;
无法使用systemctl，无法使用docker部署应用，应用的预编译版本都为64位，无法使用&#13;
&#13;
#### 编译redis&#13;
版本：7.0.15&#13;
前置依赖：build-essential tcl&#13;
准备好源代码&#13;
&#13;
1. make -j6&#13;
2. make PREFIX=/tmp/redis install&#13;
&#13;
耗时：10分钟&#13;
&#13;
#### 编译mysql&#13;
版本：8.0.38&#13;
前置依赖：build-essential cmake libncurses5-dev libssl-dev bison libaio-dev&#13;
准备好源代码&#13;
&#13;
1. mkdir build &amp;&amp; cd build&#13;
2. cmake .. -DDOWNLOAD_BOOST=1 -DWITH_BOOST=boost `此步骤可能提示cmake版本过低，若apt存在最低要求的版本最好，直接安装，否则准备需要cmake源码，编译安装cmake：1. apt remove cmake 2. ./bootstrap 3. make -j6 4. make install 5. echo 'export PATH=/usr/local/bin:$PATH' &gt;&gt; ~/.bashrc 7. source ~/.bashrc`&#13;
4. make -j8&#13;
5. make install DESTDIR=/tmp/mysql&#13;
&#13;
耗时：将近6小时&#13;
与此[博主](https://cloud.tencent.com/developer/article/1799089)的耗时差不多（刚开始我还以为mysql也不至于编译那么久）&#13;
&#13;
**意外情况：编译到73%频繁死机，检查后，内存不足，利用scene4增大了2GB的swap后，得以继续编译**&#13;
&#13;
#### mysql编译完成测试：&#13;
1. `groupadd mysql  **not use**`&#13;
2. `useradd mysql -r -g mysql -s /bin/false mysql **not use**`&#13;
3. `./mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data`&#13;
4. `./mysqld_safe --user=root &amp;`。</description><guid isPermaLink="true">https://iamdurant.github.io/post/bian-yi-redis%E3%80%81mysql%E3%80%81nginx.html</guid><pubDate>Sun, 07 Jul 2024 06:29:43 +0000</pubDate></item><item><title>图</title><link>https://iamdurant.github.io/post/tu.html</link><description>### 图&#13;
&#13;
图由vertext以及edge组成，分为有向图和无向图。</description><guid isPermaLink="true">https://iamdurant.github.io/post/tu.html</guid><pubDate>Thu, 27 Jun 2024 11:40:05 +0000</pubDate></item><item><title>排序算法</title><link>https://iamdurant.github.io/post/pai-xu-suan-fa.html</link><description>## 最基础的十大排序算法

- 冒泡排序
- 选择排序
- 插入排序
- 希尔排序
- 堆排序
- 归并排序
- 快速排序
- 计数排序
- 桶排序
- 基数排序

### 基于比较的排序算法，常见有：

![各排序算法复杂度](https://github.com/iamdurant/iamdurant.github.io/assets/107034526/e9360d33-8b17-45d3-affe-1d0a7527914c)

#### 冒泡排序

重复遍历数组（每次遍历不断减少），每轮遍历不断比较并往后交换较大值，用一个变量记录最近的一次交换，可以减少交换次数， 
在部分有序的情况下，可以减少交换次数。</description><guid isPermaLink="true">https://iamdurant.github.io/post/pai-xu-suan-fa.html</guid><pubDate>Tue, 25 Jun 2024 15:08:48 +0000</pubDate></item><item><title>基督山伯爵</title><link>https://iamdurant.github.io/post/ji-du-shan-bo-jue.html</link><description>#### 核心人物&#13;
- 法里亚（神甫）&#13;
- 埃德蒙·唐戴斯（主人公）&#13;
- 卡德鲁斯&#13;
- 梅赛苔丝（德·莫尔赛夫夫人）&#13;
- 费尔南（德·莫尔赛夫伯爵）&#13;
- 唐格拉尔&#13;
- 德·维尔福&#13;
- 莫雷尔&#13;
&#13;
#### 其他人物&#13;
- 雅各布（水手）&#13;
- 朱丽（莫雷尔之女）&#13;
- 马克西米利安·莫雷尔（莫雷尔之子）&#13;
- 埃马纽埃尔（正追求朱丽）&#13;
- 科克莱斯（莫雷尔公司出纳员）&#13;
- 佩纳隆（老水手）&#13;
- 戈玛尔（法老号船长）&#13;
- 阿尔贝·德·莫尔赛夫（子爵）&#13;
- 弗朗兹·德·埃皮奈（男爵）。</description><guid isPermaLink="true">https://iamdurant.github.io/post/ji-du-shan-bo-jue.html</guid><pubDate>Sun, 23 Jun 2024 10:21:46 +0000</pubDate></item><item><title>哈希表</title><link>https://iamdurant.github.io/post/ha-xi-biao.html</link><description>#### 底层数据结构&#13;
&#13;
哈希表底层数据结构为数组＋链表 或者 数组+红黑树。</description><guid isPermaLink="true">https://iamdurant.github.io/post/ha-xi-biao.html</guid><pubDate>Sat, 22 Jun 2024 05:31:16 +0000</pubDate></item><item><title>初识B树</title><link>https://iamdurant.github.io/post/chu-shi-B-shu.html</link><description>### B树&#13;
&#13;
B树（B-Tree）是一种自平衡的树数据结构，它在维护排序数据的同时，支持高效的插入、删除和查找操作。</description><guid isPermaLink="true">https://iamdurant.github.io/post/chu-shi-B-shu.html</guid><pubDate>Thu, 20 Jun 2024 13:04:19 +0000</pubDate></item><item><title>重识yaml</title><link>https://iamdurant.github.io/post/zhong-shi-yaml.html</link><description>### spring将yaml格式作为配置文件格式，必须得好好熟悉一下&#13;
&#13;
### 基本语法&#13;
&#13;
- 大小写敏感&#13;
- 使用缩进表示层级关系&#13;
- 缩进值允许使用空格，不允许使用tab&#13;
- 缩进的空格数不重要，只要保证相同层级的左对齐即可&#13;
- 使用`#`表示注释&#13;
&#13;
### yaml对象&#13;
&#13;
以键值对的方式表示 中间用`: `分隔key &amp; value，`:`号后面带**一个**空格&#13;
&#13;
```yaml&#13;
key: &#13;
  value1&#13;
  value2&#13;
```&#13;
&#13;
### yaml数组&#13;
&#13;
yaml数组用`- `号开头，`:`号后面带**一个**空格&#13;
&#13;
```yaml&#13;
arr: &#13;
 - a&#13;
 - b&#13;
 - c&#13;
```&#13;
&#13;
也可以表示多维数组，需控制层级&#13;
&#13;
```yaml&#13;
array: &#13;
  -&#13;
    - a&#13;
    - b&#13;
    - c&#13;
  -&#13;
    - x&#13;
    - y&#13;
    - z&#13;
```&#13;
&#13;
对象数组，就像java中的`Person p1 = new Person('薛大炮', 18);Person p2 = new Person('蔡徐坤', 26);Person[] persons = new Person[]{p1, p2};`&#13;
&#13;
```yaml&#13;
persons&#13;
  -&#13;
    name: 薛大炮&#13;
    age: 18&#13;
  -&#13;
    name: 蔡徐坤&#13;
    age: 26&#13;
```&#13;
&#13;
### 复合结构&#13;
&#13;
对象和数组可以构成复杂的结构关系&#13;
&#13;
```yaml&#13;
languages: &#13;
  - Java&#13;
  - C++&#13;
  - Go&#13;
WebSites: &#13;
  baidu: https://www.baidu.com&#13;
  google: https:www.google.com&#13;
  bing: https:www.bing.com&#13;
```&#13;
&#13;
对应到json为&#13;
&#13;
```json&#13;
{&#13;
  'languages': ['Java', 'C++', 'Go'],&#13;
  'WebSites': {&#13;
    'baidu': 'https://www.baidu.com',&#13;
    'google': 'https:www.google.com',&#13;
    'bing': 'https:www.bing.com'&#13;
  }&#13;
}&#13;
```&#13;
&#13;
### 纯量&#13;
&#13;
纯量是最基本的，不可再分的值&#13;
- 字符串&#13;
- 整数&#13;
- 浮点数&#13;
- 布尔值&#13;
- null&#13;
- 时间（date）&#13;
- 日期（datetime）&#13;
&#13;
以一个例子来看各纯量的使用&#13;
&#13;
```yaml&#13;
string: &#13;
  - 哈哈            # 字符串可直接写&#13;
  - 'good'         # 或者单引号引表示&#13;
  - '西海岸'        # 或者双引号表示&#13;
  - you jump       # 字符串可写多行，每行被转化为空格即为` you jump i jump！`&#13;
    i jump！&#13;
&#13;
int: &#13;
  - 123&#13;
  - 0100_1101     # 二进制表示&#13;
 &#13;
float: &#13;
  - 66.6&#13;
  - 45.1231402+e5      # 可以使用科学计数法表示&#13;
&#13;
boolean: &#13;
  - TRUE&#13;
  - FALSE&#13;
  - True&#13;
  - False&#13;
  - true&#13;
  - false        # 其实 true false 全小写最常用&#13;
&#13;
null: &#13;
  - a&#13;
  - ~            # 使用~表示null&#13;
&#13;
date: 2024-06-20       # 使用ISO-8601标准：yyyy-MM-dd&#13;
&#13;
datetime: 2024-06-20T16:34:20+09:00     # 使用ISO-8601标准：yyyy-MM-ddTHH:mm:ss+时区&#13;
```&#13;
&#13;
### 引用&#13;
&#13;
`&amp;`表示锚点，`*`表示引用锚点，`&lt;&lt;: `表示合并到当前数据流&#13;
&#13;
```yaml&#13;
database: &amp;database&#13;
  host: 127.0.0.1&#13;
  port: 3306&#13;
  user: root&#13;
  password: 123456&#13;
&#13;
mysql: &#13;
  &lt;&lt;: *database             # 若未生效 则mardown解析器不兼容&#13;
```&#13;
&#13;
&#13;
&#13;
&#13;
&#13;
。</description><guid isPermaLink="true">https://iamdurant.github.io/post/zhong-shi-yaml.html</guid><pubDate>Thu, 20 Jun 2024 08:45:56 +0000</pubDate></item><item><title>初窥markdown</title><link>https://iamdurant.github.io/post/chu-kui-markdown.html</link><description>### 首先呢，要学会markdown语法，markdown语法非常强大，实在是太酷了，迫不及待要看一看它的语法了！！&#13;
&#13;
### 标题&#13;
&#13;
就标题来说只需在文字前面加上（# ），是的，一个#号加一个空格，后面再跟上标题即可，#号越多 标题越小&#13;
&#13;
### 引用&#13;
&#13;
&gt; 只需在文本开头前加上`&gt; `即可（后面带空格）&#13;
&#13;
### 粗体、斜体、粗体斜体&#13;
&#13;
这是斜体 *哈哈哈哈*  文本块前后各加一个星号&#13;
这是加粗 **呵呵呵呵** 文本块前后各加两个星号&#13;
这是斜体与加粗 ***嘻嘻嘻嘻*** 文本块前后各加三个星号&#13;
&#13;
### 无序列表&#13;
&#13;
无序列表的使用也是相当清晰，就像yaml的数组一样，当然了，控制层级的话只需在前面控制空格数量就好了&#13;
- 苹果&#13;
- 香蕉&#13;
- 蔬菜&#13;
  - 青菜&#13;
  - 白菜&#13;
&#13;
### 有序列表&#13;
&#13;
有序列表只需在数字或者符号后加上`. `即可（后面有空格）,&#13;
1. 科比&#13;
2. 杜兰特&#13;
3. 乔丹&#13;
&#13;
### 链接&#13;
&#13;
链接的格式：`[]()` 方括号里面是文字 圆括号里面是连接地址&#13;
比如：[来吧，now，is movie time！！！](https://nunflix.com)&#13;
&#13;
### 图片&#13;
&#13;
哇哇哇哦，图片就比较刺激了，格式：`![]()`，基于连接，由感叹号开头，方括号里面是文字(在这里是图片描述) 圆括号里面是连接地址&#13;
比如：&#13;
![我的leetcode头像](https://assets.leetcode.cn/aliyun-lc-upload/users/tender-satoshino0/avatar_1692271843.png)&#13;
&#13;
### 分割线&#13;
&#13;
格式：`---` 就是三个减号 三个减号上面还需空一行&#13;
&#13;
---&#13;
&#13;
### 表格&#13;
&#13;
表格就更有意思了，太new了，太潮流了&#13;
&#13;
格式：`|`用来分割，第二行的`:` 用来代表左对齐还是右对齐，或者中间对齐&#13;
&#13;
![表格格式](https://github.com/iamdurant/iamdurant.github.io/assets/107034526/5410dc3a-1e97-426e-b8b5-a629ca4f25d6)&#13;
&#13;
&#13;
| 学号 | 姓名 | 年龄 |&#13;
|     :----------     |    :---     |     :--    |&#13;
|     2020132238     |      蔡徐坤   |      23   |&#13;
|     2020132221     |     薛大炮    |    24     |&#13;
|     2020132249    |     洲饱饱    |      23   |&#13;
&#13;
**上述有我的idol 以及我的两个brother，能被我写进blog，是我的brother的福气**&#13;
&#13;
### 脚注&#13;
&#13;
脚注就比较叼了格式也是基于链接：`[](脚注解释 '脚注名字')`&#13;
&#13;
- [全栈工程师](全栈工程师就是。</description><guid isPermaLink="true">https://iamdurant.github.io/post/chu-kui-markdown.html</guid><pubDate>Wed, 19 Jun 2024 17:43:00 +0000</pubDate></item></channel></rss>