你不会找到路,除非你敢于迷路

0%

优秀文章

  • Kindle 盖泡面,单反爱吃灰

    池建强老师经常会给极客时间很多课程写推荐文章。不像其他广告, 池老师的推广文章我是很喜欢阅读的。字里行间,无不透露着他对课程的喜爱与雕琢。用心做内容,并且为自己的产品自豪,达到这种境界,并不简单。

    借用池老师的一句话:“如果连我自己都不好意思推荐我自己的产品,那它一定是有问题的”。

  • 搭便车的时候,请别把车砸了。

    仁慈和善良的人,实际上是被那些不够仁慈,不够善良的人所保护的。

  • 听说你想过上流生活

    关于消费观的一些见解。

  • 微信搜一搜迈出新的一步,好戏来了

    人无我有,人有我优,人优我便利。微信的布局着实可怕。

阅读全文 »

Algorithm

1313. 解压缩编码列表

解法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int[] decompressRLElist(int[] nums) {
int resultLength = 0;
int loop = nums.length / 2;
// 计算目标数组的长度
for (int i = 0; i < loop; i++) {
resultLength += nums[2 * i];
}
// 执行解码操作
int[] result = new int[resultLength];
int resultIndex = 0;
for (int i = 0; i < loop; i++) {
for (int j = 0; j < nums[2 * i]; j++) {
result[resultIndex] = nums[2 * i + 1];
resultIndex++;
}
}
return result;
}
}

执行用时:1ms,内存消耗:37.8MB。

阅读全文 »

Algorithm

1295. 统计位数为偶数的数字

题解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int findNumbers(int[] nums) {
int count = 0;
for (int num : nums) {
int bits = 0;
do {
num = num / 10;
bits++;
} while (num != 0);

if (bits % 2 == 0) {
count++;
}
}

return count;
}
}

执行用时:1ms,内存消耗:38.7MB。

阅读全文 »

Algorithm

1304. 和为零的N个唯一整数

解法一:

钻了个漏洞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int[] sumZero(int n) {
if (n <= 1) {
return new int[]{0};
}

int[] result = new int[n];
int initNumber = 1, sum = 0;

for (int i = 0; i < n - 1; i++) {
result[i] = initNumber;
sum += initNumber;
initNumber++;
}

result[n - 1] = -sum;
return result;
}
}

执行用时:0ms,内存消耗:34.9MB。

阅读全文 »

打卡这么简单的事, 真的能影响到我?

背景

ARTS 起源于 陈皓 老师的 左耳听风 课程, 我们在其基础上做了一丢丢改造, 感谢 陈皓 老师.

两个月前, 我的朋友 大蕉 发起了一个为期 8 周的 ARTS 打卡活动:

每周打卡,自行创建一个博客地址。

打卡要求:
8人8周为一个小组。入群门槛188,8周完成全额退款,若全员完成,大蕉发188红包。

一周没打卡,请发10*当前人数红包一个。
两周没打卡,请退群。

打卡条件:
大家自发每周完成一个ARTS:
(也就是 Algorithm、Review、Tip、Share 简称ARTS)

(高级打卡组)
1.每周至少做一个 leetcode 的算法题
2.阅读并点评至少一篇英文技术文章
3.学习至少一个技术技巧(编程或其他)
4.编写一篇有观点和思考的技术文章(500字以内)

(初级打卡组)
1.每周至少做一个 leetcode 的算法题或者小玩具代码
2.阅读至少一篇英文技术文章
4.编写一篇文章(500字以内)

入群流程:
转账即报名,请描述自己想进入初级还是高级组

承诺:
我不承诺你一定能坚持下去,但我承诺你坚持下去肯定会看见不一样的自己

乍一看, 似乎很简单, 也似乎很难.

一咬牙, 我加入了这个计划.

阅读全文 »

Algorithm

1252. 奇数值单元格的数目

解法一:

思路:

因为题目中行列式每次数据变换都是整行或者整列同时进行的, 所以只需要统计每一行和每一列会被自增多少次即可. 最后对应行和列的统计结果相加即为对应坐标的数值.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int oddCells(int n, int m, int[][] indices) {
int[] rows = new int[n];
int[] columns = new int[m];
for (int[] axis : indices) {
rows[axis[0]]++;
columns[axis[1]]++;
}

int oddNumber = 0;
for (int row : rows) {
for (int column : columns) {
if ((row + column) % 2 == 1) {
oddNumber++;
}
}
}

return oddNumber;
}
}

执行用时: 1ms, 内存消耗: 38.7MB.

阅读全文 »

没错, 我一个程序员, 心血来潮建了个写作群.

大学

大学生涯, 我经历的似乎只有失败二字.

大一上学期就开始挂科, 一直持续到毕业. 40 学分劝退, 曾一度挂到了 39 个学分. 学习上, 我无疑是失败的.

同班 29 人, 与我交好的只有 4 人. 同校 4 万人, 毕业后还有联系的, 屈指可数. 大学前, 整个学校都是我的好朋友, 大学后, 却和他们越走越远. 交际上, 我依然是失败的.

大学前, 我可以很自豪地说自己喜欢计算机, 说自己喜欢 DIY, 说自己喜欢轮滑. 大学后, 却一事无成. 就连兴趣爱好, 我都是失败的.

摸爬滚打两个月, 找到了全班最差的工作; 无可奈何签合同, 拿了同岗位最低的薪水. 甚至还被忽悠着贷款购买了两万块的 Python 课程. 找工作, 当然还是失败的.

阅读全文 »

Algorithm

1266. 访问所有点的最小时间

思路:

这其实更像是一个数学问题.

因为需要按顺序访问所有点, 所以可以把问题简化为计算两个点之间的最小距离, 最后把每两个点之间的最小距离累加, 问题得解.

接下来要计算两个点之间的最小距离, 因为走对角线的效率是分别走横竖的两倍, 所以我们要尽可能多地走对角线. 那么两个点之间势必会形成一块而走对角线的正方形区域, 正方形的边长就是以两点为对角形成的长方形的较短的一条边. 剩余的距离一直横着或者竖着走即可. 问题得解.

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public int minTimeToVisitAllPoints(int[][] points) {
if (points.length <= 1) {
return 0;
}

int total = 0;
int[] last = points[0];
for (int i = 1; i < points.length; i++) {
int[] current = points[i];
int x = Math.abs(current[0] - last[0]);
int y = Math.abs(current[1] - last[1]);

int italic = Math.min(x, y);
int straight = Math.max(x, y) - italic;
int move = italic + straight;
total += move;

last = current;
}

return total;
}
}

执行用时: 1ms, 内存消耗: 41.4MB.

阅读全文 »

Algorithm

66. 加一

思路:

  1. 从最低位开始, 依次把当前位数字加一:
    1. 如果当前位数字加一后结果小于十, 直接返回当前数组, 程序结束;
    2. 如果当前位数字加一后结果等于十, 把当前位数字置为 0. 把当前位左移 1, 跳转到第 1 步;
  2. 如果第 1 步执行结束之后程序依然没有终止, 证明数字一直累加到了最高位, 那么当前数组长度已经放不下加一后的数字了. 重新声明一个长度加一的数组, 最高位置为 1, 其他位置按序填充原数组的内容即可.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int[] plusOne(int[] digits) {
int length = digits.length;
for (int i = length - 1; i >= 0; i--) {
if (++digits[i] < 10) {
return digits;
}

digits[i] = 0;
}

int[] temp = new int[length + 1];
temp[0] = 1;
for (int i = 1; i < temp.length; i++) {
temp[i] = digits[i - 1];
}

return temp;
}
}

执行用时: 0ms, 内存消耗: 35.1MB.

Review

声明!!!

水平以及精力有限,下文可能存在很多问题错译,仅做个人学习之用,如果需要学习 Docker, 还请查阅其他资料!!!

Post-installation steps for Linux

这个章节主要包含了 Docker 在 Linux 下的可选的优化配置.

使用非 root 用户管理 Docker

Docker 守护进程绑定在 Unix 套接字而不是 TCP 端口. 默认情况下 Unix 套接字属于 root 用户, 其他用户只能通过 sudo 命令获得授权. Docker 守护进程经常使用 root 用户身份运行.

如果你不想通过 sudo 命令运行 docker, 可以创建一个 叫做 “docker” 的 Unix 用户组并且把用户添加到这个用户组. Docker 守护进程启动的时候会创建一个 docker 用户组下所有成员都有权限的 Unix 套接字.

警告:

Docker 用户组拥有和 root 用户相同的权限.

操作流程:

  1. 创建 docker 用户组.

    1
    sudo groupadd docker
  2. 把用户添加进 docker 用户组.

    1
    sudo usermod -aG docker $USER
  3. 退出当前账户并重新登录, 当前账户的组成员信息会被重新加载.

    如果你是在虚拟机中测试, 那么你可能需要重启虚拟机.

    在 Linux 桌面版中, 你需要完全退出当前会话并重新登录.

    在 Linux 中, 你也可以使用下面的命令激活用户组的变更:

    1
    newgrp docker
  4. 使用非 sudo 方法测试 docker 命令.

    1
    docker run hello-world

设置 Docker 自启动

当前大多数 Linux 发行版使用 systemd 来管理服务自启动.

可以使用这个命令设置自启动:

1
sudo systemctl enable docker

可以使用下面的命令禁用自启动:

1
sudo systemctl disable docker

设置 Docker 守护进程监听连接

默认情况下, Docker 守护进程会通过一个 UNIX 套接字监听来自本地客户端的连接. 可以通过配置使 Docker 监听 一个 IP

和 端口来接收远程主机的连接.

通过 systemd 单元文件配置远程访问

  1. 使用命令 sudo systemctl edit docker.service 在文本编辑器中打开一个 docker.service 的替代文件.

  2. 添加或者修改这几行, 替换成你自己的值

    1
    2
    3
    [Service]
    ExecStart=
    ExecStart=/usr/bin/dockerd -H fd:// -H tcp:://127.0.0.1:2375
  3. 保存文件.

  4. 重新加载 systemctl 配置.

    1
    sudo systemctl daemon-reload
  5. 重启 Docker

    1
    sudo systemctl restart docker.service
  6. 通过观察 netstat 的输出来确认 dockerd 在监听刚才配置的端口.

    1
    2
    sudo netstat -lntp | grep dockerd
    tcp 0 0 127.0.0.1:2375 0.0.0.0:* LISTEN 3758/dockerd

通过 daemon.json 配置远程访问

  1. 设置 /etc/docker/daemon.json 中的 hosts 数组来连接 Unix 套接字和 IP 地址, 如下:

    1
    2
    3
    {
    "hosts": ["unix::///var/run/docker.sock", "tcp://127.0.0.1:2375"]
    }
  2. 重启 Docker.

  3. 通过观察 netstat 的输出来确认 dockerd 在监听刚才配置的端口.

    1
    2
    sudo netstat -lntp | grep dockerd
    tcp 0 0 127.0.0.1:2375 0.0.0.0:* LISTEN 3758/dockerd

常见问题的解决方案

对症下药, 这里不转译了.

为 Docker 指定 DNS 服务

默认的配置文件是 /etc/docker/daemon.json. 你可以使用 --config-file 标记更改配置文件的位置. 下面的文档假设配置文件是 /etc/docker/daemon.json.

  1. 创建或者修改 docker 进程配置文件, 这个文件默认是 /etc/docker/daemon.json.

    1
    sudo nano /etc/docker/daemon.json
  2. 添加一个名为 dns 的键, 对应的值可以写一个或者多个 IP 地址. 如果这部分内容已经存在于文件中, 你只需要添加或者修改 dns 对应的那一行.

    1
    2
    3
    {
    "dns": ["8.8.8.8", "8.8.4.4"]
    }

    如果你的内网 DNS 服务器不能解析公网 IP 地址, 请至少配置一个可以解析公网 IP 地址的 DNS 服务器. 以便于你可以连接到 Docker 中心并且你的容器可以解析公网域名.

    保存并且关闭文件.

  3. 重启 Docker 进程.

    1
    sudo service docker restart
  4. 通过拉取一个镜像验证 Docker 可以解析外部 IP 地址.

    1
    docker pull hello-world
  5. 如果有需要, 可以通过 ping 一个内部 IP 地址来验证 Docker 是否可以解析内部 IP 地址.

    1
    2
    3
    4
    5
    6
    7
    docker run --rm -it alpine ping -c4 <my_internal_host>

    PING google.com (192.168.1.2): 56 data bytes
    64 bytes from 192.168.1.2: seq=0 ttl=41 time=7.597 ms
    64 bytes from 192.168.1.2: seq=1 ttl=41 time=7.635 ms
    64 bytes from 192.168.1.2: seq=2 ttl=41 time=7.660 ms
    64 bytes from 192.168.1.2: seq=3 ttl=41 time=7.677 ms

允许通过防火墙远程访问 API

如果你在运行 Docker 的同一台主机上运行了防火墙, 在 Docker 中启用了远程访问并且希望从另一台主机访问 Docker 的 API, 你需要配置防火墙来允许外部连接访问 Docker 端口, 默认端口在开启了 TLS 加密传输的情况下是 2376, 其他情况下是 2375.

两个比较常见的防火墙是 UFW 和 firewalld.

  • UFW: 在 UFW 中进行如下配置: DEFAULT_FORWARD_POLICY="ACCEPT".

  • firewalld: 把类似的规则添加到你的策略中(其中一条是入口规则, 另一条是出口规则), 一定要确认接口名字和链路名字是正确的.

    1
    2
    3
    4
    <direct>
    [ <rule ipv="ipv6" table="filter" chain="FORWARD_direct" priority="0"> -i zt0 -j ACCEPT </rule> ]
    [ <rule ipv="ipv6" table="filter" chain="FORWARD_direct" priority="0"> -o zt0 -j ACCEPT </rule> ]
    </direct>

Your kernel does not support cgroup swap limit capabilities

在 Ubuntu 或者 Debian 系统中, 你可能会在运行镜像的时候遇到类似的错误:

1
WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.

这个警告不会出现在基于 RPM 的系统, 它们已经默认启用了这些功能.

如果你不需要这些特性, 你可以忽视这个警告. 或者你也可以通过下面的说明启用这些特性, 但是内存和交换会产生总内存的 1% 并且总体性能会降低 10%, 即使 Docker 没有在运行.

  1. 使用拥有 sudo 权限的账号登录系统 (Ubuntu 或者 Debian);

  2. 编辑 /etc/default/grub 文件, 添加或者编辑 GRUB_CMDLINE_LINUC 这一行并添加如下两个键值对:

    1
    GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

    保存并关闭文件.

  3. 更新 GRUB.

    1
    sudo update-grub

    如果你的 GRUB 配置文件有错误, 这里一步会发生错误. 在这种情况下, 你需要重复第 2 步和第 3 步.

    这个设置会在系统重启后生效.

Tip

SpringBoot 单元测试事务回滚

SpringBoot 中如果要写单元测试只需要给测试类添加两个注解即可:

1
2
@SpringBootTest
@RunWith(SpringRunner.class)

如果需要进行事务回滚, 可以通过添加 @Transitional 注解实现, @Transitional 可以添加到类或者方法上:

  • 如果添加到方法上, 那么只对当前方法进行事务回滚;

  • 如果添加到类上, 那么当前类的所有方法都会进行事务回滚;

    在这种情况下, 如果要对个别方法提交事物, 可以在对应方法上添加 @Rollback(false) 注解来提交事务.

Share

不尝试一下,你怎么知道自己不行?

记录了 ARTS 第一期的打卡总结, 因为文字较多, 独立写了一篇文章.

参考文献

  • SpringBoot实现单元测试时回滚事务 — Alphathur

前言

毕业差不多一年了, 一年之中似乎每天都在寻求着进步, 一年之后又觉得其实自己并没有进步. 碌碌无为, 好像就是我每天不断重复的循环.

意识到这个问题之后便开始尝试破除当下的困境, 恰巧上周看到一篇文章: 如何利用碎片时间,提升自己的职场竞争优势, 感觉应该有用, 不仿就先试试文章中提到的时间梳理吧.

时间梳理-现状

以半小时为时间片段进行分割, 现阶段我的时间使用情况如下:

阅读全文 »