ChatGPT

目前利用chatgpt其实可以解决很多现实中的问题,比如说前一段时间帮我表姐写了一个简单的说明书网页显示,自己搞定了前端的页面(preview/login/admin page),配合eggjs的后端渲染能力轻松搞定增删改查,阿里云的部署,ICP备案一条龙服务,虽然是个很小的项目,大项目该走的流程也都走了一遍。
话具体的说来gpt的帮助远比我想象中的好一些,英文的答案也比想象中更加的充分一些,目前国内的访问途径基本就如下几种

  • 付费openai(direct/indirect)
  • 国产平替

目前github也有一些大模型的统一界面比如什么chatall什么的,今天我看了一眼chatGPT-NextWeb, 目前支持azure/openai/google,其他的一些软件可能支持的全一些。
巧的是我通过公司的邮箱地址成功申请下来了azure的openai服务,填了url和azure-api-key就可以直接访问了,提问了十几次,预算是CNY0.17,在gpt-3.5-turbo的下面也算是国内访问最便捷的方式了,缺点就是目前该项目只支持端侧和网页,不支持移动端,不过好消息是比较稳定目前。
GPT-4其实也可以在申请配额(quota increase)不过相对于几乎0.2元一次的价格(几乎),还是有一点肉疼,不过先尽量发挥3.5的潜力,毕竟有他的帮助,感觉还是很轻松。

另外提一嘴,azure的付费模式和阿里云完全不同,接口走的是先用后付,openai会挂载在subscription的下面,然后使用绑定信用卡就可以了(好像是要求visa/master,我正好有一张就没有在尝试别的,国内办理也很好办理)

很久没有打开hexo了,发现了很多新的功能

  1. 更便捷的theme管理方式
  2. 更友好的部署方式git action
1
2
3
4
5
6
cd hexo-site
$ npm install hexo-theme-next@latest
cp node_modules/hexo-theme-next/_config.yml _config.next.yml

//update theme in config.yml
theme: next

解决了早期theme无法很好备份的问题
link: https://theme-next.js.org/docs/getting-started/

另外介于typora已经开始收费了,vscode开启了markdwon all in one开始在vscode里面直接进行创作

github action遇到了一些问题,主要是一些保护规则,
//Branch x is not allowed to deploy to github-pages due to environment protection rules
比如我用的部署分支是source不是常见的main/master,在github 项目的setting–environment–里面改变规则即可,个人感觉action的方式比一键部署更加方便一些

144. 二叉树的前序遍历

总体思路递归,easy等级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function (root) {
const ans = [];
recursion(root, ans);
return ans
};
function recursion(root, ans) {
if (root === null) return;
ans.push(root.val);
recursion(root.left, ans)
recursion(root.right, ans)
}

Leetcode 671

671. 二叉树中第二小的节点

难度简单198收藏分享切换为英文接收动态反馈

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 20。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。

更正式地说,root.val = min(root.left.val, root.right.val) 总成立。

给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1

示例 1:

img

1
2
3
输入:root = [2,2,5,null,null,5,7]
输出:5
解释:最小的值是 2 ,第二小的值是 5 。

示例 2:

img

1
2
3
输入:root = [2,2,2]
输出:-1
解释:最小的值是 2, 但是不存在第二小的值。

提示:

  • 树中节点数目在范围 [1, 25]
  • 1 <= Node.val <= 231 - 1
  • 对于树中每个节点 root.val == min(root.left.val, root.right.val)

Solution

如题意,父节点就是最小值,借鉴了答案,递归条件写错了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var findSecondMinimumValue = function (root) {

let ans = -1;
const parentValue = root.val;//root value

const dfs = (node) => {
if (node === null) {
return;
}
if (ans !== -1 && node.val >= ans) {
return;
}
if (node.val > parentValue) {
ans = node.val;
}
dfs(node.left);
dfs(node.right);
}

dfs(root);
return ans;
};

#Leetcode-1109. 航班预订统计

难度中等157收藏分享切换为英文接收动态反馈

这里有 n 个航班,它们分别从 1n 进行编号。

有一份航班预订表 bookings ,表中第 i 条预订记录 bookings[i] = [firsti, lasti, seatsi] 意味着在从 firstilasti包含 firstilasti )的 每个航班 上预订了 seatsi 个座位。

请你返回一个长度为 n 的数组 answer,其中 answer[i] 是航班 i 上预订的座位总数。

示例 1:

1
2
3
4
5
6
7
8
9
输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
输出:[10,55,45,25,25]
解释:
航班编号 1 2 3 4 5
预订记录 1 : 10 10
预订记录 2 : 20 20
预订记录 3 : 25 25 25 25
总座位数: 10 55 45 25 25
因此,answer = [10,55,45,25,25]

示例 2:

1
2
3
4
5
6
7
8
输入:bookings = [[1,2,10],[2,2,15]], n = 2
输出:[10,25]
解释:
航班编号 1 2
预订记录 1 : 10 10
预订记录 2 : 15
总座位数: 10 25
因此,answer = [10,25]

提示:

  • 1 <= n <= 2 * 104
  • 1 <= bookings.length <= 2 * 104
  • bookings[i].length == 3
  • 1 <= firsti <= lasti <= n
  • 1 <= seatsi <= 104

Solution

题目到是很简单,主要做的就是一维数组做个累加,时间复杂度O(N^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
var corpFlightBookings = function (bookings, n) {
const answer = [];
for (let i = 0; i < n; i++)answer.push(0);
for (let item of bookings) {
const [first, last, seats] = item;
for (let j = first; j <= last; j++) {
answer[j-1] += seats;
}
}
return answer;

};
console.log(corpFlightBookings([[1, 2, 10], [2, 3, 20], [2, 5, 25]], 5))

但是时间只超过了50%,就考虑问题

问题应该在O^2的复杂度上

后来想遍历的时候

  • Array(n).fill(0) 代码更优美
  • 增量加在数组里,最后走for循环跑一次就可以降一层for循环
1
2
3
const [first, last, seats] = item;
answer[first - 1] += seats;
if (last < n) answer[last] -= seats;

LeetCode:剑指 Offer 53

打开网页,突然看到日推是easy难度,本来想就几行代码的事情,弄完了就休息了,提交后–傻了眼–:cry:,居然只打败了6.27%的人,草率了

题目:

剑指 Offer 53 - I. 在排序数组中查找数字 I

统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

限制:

0 <= 数组长度 <= 50000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// first commit
// 执行用时:2 ms, 在所有 Java 提交中击败了6.27%的用户
// 内存消耗:40.8 MB, 在所有 Java 提交中击败了98.84%的用户
class Solution {
public int search(int[] nums, int target) {
if(nums==null||nums.length<0){
return 0;
}
int count = 0;
for (Integer num : nums) {
if(num>target)break;
if (target == num)
count++;
}
return count;
}
}

找到了耗时1ms的答案一看,是foreach替换成了for循环 果然下标访问更快一些

以下贴一个最优解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public int search(int[] nums, int target) {
// 就是left right 更快的定位,总体复杂度差不多,不过
int left = getRight(nums,target-1);
int right = getRight(nums,target);
return right-left;

}
public int getRight(int[] nums ,int target){
int left = 0;
int right = nums.length-1;

while(left<=right){
int mid = (left+right)/2;
if(nums[mid]>target){
right = mid-1;
}
else if(nums[mid]<=target){
left = mid + 1;
}
}
return left;
}

}

Prototype

含义:proto(/ˈproʊtə/)原始, 原型, 原始的

目的:补充JavaScript对于对象的支持,通过protype来实现class中的method

过程:熟悉实例对象<—>构造函数<—>原型 三者之间的关系

1
2
3
4
5
6
7
//构造函数 创建对象
function Dog() {

}
var dog = new Dog();//Person 为构造函数,person为实例对象
dog.name = '柯基';
console.log(dog.name) // 柯基
  • 构造函数通过prototype访问原型(一个类的属性,对象都可以访问)
  • 实例对象通过 _proto_ 访问原型 === 构造函数通过prototype访问原型(原型也有_proto_
  • 实例原型通过constructor访问构造函数(Dog=== Dog.prototype.constructor)

image-20210224154735705

  • 原型遵循向上原则,即找不到就不断向上(prototype)查询
  • 原型因为不停延长形成链,称作原型链,但是 Object.prototype.__proto__ 的值为 null 跟 Object.prototype 没有原型
  • 原型链大概实现了类(Class)以及继承(Extend)的问题,但它并不是复制,是建立一种关联,通过prototype/_proto_ 来访问其他对象的属性和方法,属于委托/借用

Extend

一共分为6种

  • 原型链继承
  • 借用构造函数(经典继承)
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承

原型链继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Parent () {
this.names = ['kevin', 'daisy'];
}
function Child () {}
Child.prototype = new Parent();

var child1 = new Child();

child1.names.push('yayu');

console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();

console.log(child2.names); // ["kevin", "daisy", "yayu"]

问题:

  • 属性被所有child共享
  • 创建child实例时,不能向parent传参

借用构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Parent (name) {
this.name = name;
}

function Child (name) {
Parent.call(this, name);
}

var child1 = new Child('kevin');

console.log(child1.name); // kevin

var child2 = new Child('daisy');

console.log(child2.name); // daisy

有点:

  • 避免引用类型的属性被所有实例共享
  • 可以在Child中间parent传参

缺点:

  • 方法在构造函数中定义,每次创建势力都会创建一遍方法

组合继承

以上两种方法的组合,为最常用的继承方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
console.log(this.name)
}

function Child (name, age) {
Parent.call(this, name);
this.age = age;
}

Child.prototype = new Parent();
Child.prototype.constructor = Child;

var child1 = new Child('kevin', '18');

child1.colors.push('black');
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]

var child2 = new Child('daisy', '20');

console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]

link:mysql-docker

支持标签

快速手册

什么是 MySQL?

MySQL 是最受欢迎的,开源的数据库. 凭借被验证过的性能表现,可靠性,易用性, MySQL已经成为基于web的应用程序的 主要选择,包括完整得个人项目和网站项目(电子上午,信息服务),也包括优秀的Facebook Facebook, Twitter, YouTube, Yahoo!

如何使用mysql image

创建 mysql 服务实例

启动 MySQL 比较简单:

1
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
  • some-mysql 容器名称
  • my-secret-pw 是root账户的密码
  • tag 是mysql的版本

通过mysql命令行连接mysql

以下命令可以启动mysql容器并运行终端,执行SQL语句

1
$ docker run -it --network some-network --rm mysql mysql -hsome-mysql -uexample-user -p
  • some-mysql 容器名称
  • some-network 连接网络(方便容器间访问)

也可以直接运行客户端,访问远程数据库

1
$ docker run -it --rm mysql mysql -hsome.mysql.host -usome-mysql-user -p

更多命令请访问 MySQL documentation

使用docker stack 或docker-compose部署

示例stack.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Use root/example as user/password credentials
version: '3.1'
services:

db:
image: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_ROOT_PASSWORD: example

adminer:
image: adminer
restart: always
ports:
- 8080:8080
  • docker stack deploy -c stack.yml mysql
  • docker-compose -f stack.yml up

启动后, 访问 http://swarm-ip:8080, http://localhost:8080, or http://host-ip:8080

shell访问查看 MySQL 日志

使用 docker exec 可以让你在容器内执行命令,命令如下

1
$ docker exec -it some-mysql bash

容器日志:

1
$ docker logs some-mysql

自定义 MySQL 配置文件

mysql默认配置文件在 /etc/mysql/my.cnf, 也可能指定了额外文件如: /etc/mysql/conf.d or /etc/mysql/mysql.conf.d. 请检查mysqlimage本身的相关文件和目录以了解更多信息

如果 /my/custom/config-file.cnf 是你自定义的文件未知和名字, 你可以这样启动你的mysql 容器

1
$ docker run --name some-mysql -v /my/custom:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

你会启动一个用你自定义配置 /etc/mysql/my.cnf and /etc/mysql/conf.d/config-file.cnf, 的mysql容器

不使用cnf 文件配置

很多配置都可以传给 mysqld. 使你自定义容器而不需要 cnf 文件. 如当你想改变默认编码和排序规则,使用 UTF-8 (utf8mb4) 只需要执行如下命令:

1
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

如果你想看到所有的配置项,只需要执行:

1
$ docker run -it --rm mysql:tag --verbose --help

环境变量

docker run 时,可以一个或多个参数进行配置. 不过需要注意,如果使用已经包含数据库的数据目录启动容器,以下变量不会产生影响,在启动时,任何预先存在的数据库都保持不变,任何之前存在的数据库在容器启动时将保持不变.

MYSQL_ROOT_PASSWORD

该变量是必须的,是root账户的密码.

MYSQL_DATABASE

该变量可选,允许在启动时,指定数据库的名称. 如果提供了用户名/密码,用户会被赋予超级权限.

MYSQL_USER, MYSQL_PASSWORD

可选变量,用于创建新用户和密码,用户将获得超级管理员权限,两个参数都是必须的.

注意:不需要使用该机制来创建root超级用户,默认使用 MYSQL_ROOT_PASSWORD 来创建密码

MYSQL_ALLOW_EMPTY_PASSWORD

可选变量,设置非空值(如yes),允许root用户无密码启动容器. 注意: 除非你知道你在做什么,否则不建议设置为 yes ,因为这将使mysql实例完全不受保护,允许所有人获得完全的超级用户权限.

MYSQL_RANDOM_ROOT_PASSWORD

可选变量,设置非空值(如yes),使用pwgen , 为root用户随机生成密码 .密码将被打印.

MYSQL_ONETIME_PASSWORD

设置用户 初始化完成后过期,在首次登录时候强制修改密码. 任何非空值将激活这个配置,注意:仅支持5.6+版本,以下版本会报错

MYSQL_INITDB_SKIP_TZINFO

默认,entrypoint脚本自动加载CONVERT_TZ()函数需要的时区数据,如果不需要,任何非空值都将禁用时区加载

Docker Secrets

通过环境变量传递敏感信息,还有另一种方法, _FILE 可以附加到前面列的环境变量,使得可以从文件中的变量初始化脚本,特别是,这可以用于存在/run/secrets/<secret_name>中的docker screts从加载密码, 如 :

1
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql-root -d mysql:tag

目前仅支持: MYSQL_ROOT_PASSWORD, MYSQL_ROOT_HOST, MYSQL_DATABASE, MYSQL_USER, 和MYSQL_PASSWORD.

初始化新实例

刚启动容器,指定名字的新数据库会被创建,并且根据提供的变量初始化. 此外,它将执行扩展名为.sh, .sql.sql.gz/docker-entrypoint-initdb.d文件夹中).文件将按照字幕顺序执行. 你可以轻松使用dump备份填充,. 默认情况下,sql文件将被保存在 MYSQL_DATABASE 指定的数据库中.

Caveats//告诫

数据存储在哪里

重要内容:有几种方式在容器运行时存储数据. 我们推荐 mysql 用户熟悉可用的选项,包括:

  • 让docker使用自己的内部volume 将数据库文件写入主机系统上的磁盘(而不在容器内)从而管理数据库数据的存储。这也是默认的配置,也非常简单透明。缺点是相比直接部署找文件困难.
  • 在主机上创建一个数据目录,并将其装载到容器内部的一个目录中,使得数据库文件放置在主机已知的位置上,更轻松访问文件,缺点是需要确保目录存在,且有权限和安全机制

Docker 文档是理解不同存储选项和变量的最好起步,并且有很多博客论坛讨论并提供建议,我们将简单展示基本过程:

  1. 创建文件夹在主机如 /my/own/datadir.

  2. 启动 mysql 容器

    1
    $ docker run --name some-mysql -v /my/own/datadir:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

-v /my/own/datadir:/var/lib/mysql/my/own/datadir 目录从主机装入容器中作为 /var/lib/mysql 默认不传-v情况下,mysql将写入其他数据文件.

直到初始化完成才有连接

如果容器启动没有初始化数据库,则创建默认数据库. 初始化完成之前不会接受传入连接. 在使用自动化工具如 docker-compose同时启动多个容器时,这可能会导致问题.

如果应用尝试连接不提供服务的mysql,需要继续重试等待连接成功. 官方示例, 详见 WordPress or Bonita.

现用数据库使用

如果在一个有mysql数据目录的volume启动mqsql,应省略 $MYSQL_ROOT_PASSWORD命令; 及时填写也不会生效, 且不会更改预先存在的数据库.

以任意用户身份运行

如果你正确设置了目录权限,或者你需要使用特定的uid/gid运行mysqld,则可以通过 --user 设为任意值(root/0外)来实现所需的权限/配置:

1
2
3
4
$ mkdir data
$ ls -lnd data
drwxr-xr-x 2 1000 1000 4096 Aug 27 15:54 data
$ docker run -v "$PWD/data":/var/lib/mysql --user 1000:1000 --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

创建备份

大多数工具都会正常工作,尽管他们的使用在某些情况下可能有点复杂, 以确保可以访问mysqld服务器,确保这一点的一个简单方法是使用 docker exec 并从同一容器运行工具,如:

1
$ docker exec some-mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /some/path/on/your/host/all-databases.sql

从备份还原数据

1
$ docker exec -i some-mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < /some/path/on/your/host/all-databases.sqlwith any relevant licenses for all software contained within.1

柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术

1
2
3
4
5
6
7
8
9
10
function add(a, b) {
return a + b;
}

// 执行 add 函数,一次传入两个参数即可
add(1, 2) // 3

// 假设有一个 curry 函数可以做到柯里化
var addCurry = curry(add);
addCurry(1)(2) // 3

前置知识:JavaScript是静态作用域

闭包:访问自由变量的函数

1
2
3
4
5
6
7
var a = 1;//既不是foo的局部变量,也不是foo函数的参数,a为自由变量

function foo() {
console.log(a);
}

foo();//1

即使上下文被销毁,它仍然存在,因为在作用域链上被引用了,是js的一个特性,目前如PHP,Java不会原生支持

面试题

常见的新手面试题,我遇到过好几次(作用域+闭包考点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var data = [];

for (var i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}

data[0]();
data[1]();
data[2]();


//closure
var data = [];

for (var i = 0; i < 3; i++) {
data[i] = (function (i) {
return function(){
console.log(i);
}
})(i);
}

data[0]();//不用找global的i
data[1]();
data[2]();
0%