0%

熵是一个物理学术语,他定义了一个系统的“无序”总量。

破窗理论,一扇破的窗户,只要有一段时间不去修理,建筑中的居民就会迁移默化地产生一种被遗弃的感觉—当权者不关心这幢建筑的感觉。

不要搁置“破窗”(糟糕的设计、错误的决定、低劣的代码)不去修理。每发现一个就赶紧修一个。

永远留意大局,持续不断地审视你身边发生的事情,而不要只专注于你个人在做的事情。

够好即可的软件,训练自己写出够好即可的软件—对用户、未来的维护者来说够好即可,只要好的程度让你自己内心平静就可以。

创业不要一味地追求完美,能带着bug尽快试错,才有可能让你的用户提出更好的建议。

投资自己和理财的方法论有相似之处:

  1. 正规投资者有定期投资的习惯
  2. 多样化是长线成功的关键。
  3. 聪明的投资者会平衡保守型和风险型高回报的投资组合。
  4. 投资者用低买高卖来获得最大的回报。
  5. 应定期审查和重新平衡投资组合。

管理这类投资是一项技能,就像其他技能一样—可以学会,诀窍是让自己一开始就这样,并养成习惯。

习惯需要逐渐培养,不能贪多,也不能三分钟热度,制定一个计划按时完成它,可以是很简单的事情,不要忽视习惯的力量。

计算机是由人来使用的,你做的事情是为了满足人的需要,这非常重要。和你一起工作的是人、雇佣你的也是人、黑你的也是人。不要忘记方程式中人的那一面,他需要完全不同的技能集(我们称这些为软技能,听起来很容易,但实际很硬核,难以掌握)

这可能是我的致命弱点。

碰到解决不了的问题,要坦率的承认,但不要让问题沉寂,要寻求解决问题的途径。

批判性思维,要批判性的思考读到的听到的东西。你需要确保组合中的知识是精准的,未受供应商或媒体炒作的影响。当心坚持教条的狂热者,他们将其视为唯一答案—而那些教条未必适合你和你的项目。

要质疑,不能完全听信权威,要有自己独立的思考和判断。

交流

缺乏有效的沟通,好点子就成了一个孤儿。

沟通或者回避沟通是我最大的问题,要试着和别人交流想法,不能因为内心的鄙视拒绝和人沟通,要宽容。

  • 明白自己在说什么
  • 了解听众
  • 选择时机
  • 挑选风格
  • 让他看起来不错
  • 让听众参与
  • 做倾听者
  • 回应别人

这很重要,有必要先列一个大纲,问一下自己想说清楚什么。。

  • 点击发送按钮前先校对一遍。
  • 检查一遍拼写检查,找到有可能是自动纠错没做对的地方。
  • 用简单的格式。
  • 尽可能少应用原文。
  • 不要在网上侮辱别人,不要做喷子,除非你喜欢被喷,喜欢受虐。

任何时候和其他人交流,要站在对方的角度上观察。如果你收到一封语无伦次的邮件,你愿意读下去吗?如果别人不尊重你,你会真诚的和他交流吗?如果别人在浪费你的时间,你愿意回答他的问题吗?

ETC 原则,Easier To Change,更容易改变。设计原则全部符合 ETC。

为什么解耦很好?因为通过隔离关注焦点,可让每一部分都容易变更。

为什么单一职责原则很有用?因为一个需求变化仅体现为某个单一模块上的一个对应变化。

为什么命名很重要?因为好的命名可以使代码更容易阅读,而你需要通过阅读来变更代码。

DRY 原则,在一个系统中,每一处知识都必须单一、明确、权威地表达。

在现在的项目中,有许多重复的接口,导致想要做统一处理需要同时修改多个地方,很容易改漏。无形中增加了很多开发成本和出错的概率。

正交性是从几何学中借用来的术语。若两条直线相交后构成直角,他们就是正交的。在计算科学中,这个术语象征着独立性或解耦性。正交的好处,提高生产力及降低风险。

我们希望设计的组件自成一体:独立自主,有单一的清晰定义的意图。(又称为内聚)

养成不断质疑代码的习惯。只要有机会就重新组织、改善其结构和正交性。

工欲善其事,必先利其器。

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

思路:

回文链表和回文字符串类似,反转列表后和原链表值一一对应,所以首先能想到的解法是复制一个链表,然后问题转化为 反转链表,这样的空间复杂度是 $O(n)$。

题目进阶要求用 $O(1)$ 的空间复杂度。考虑不复制原链表的方案,回文的另一个特点是中心对称,所以问题转化为怎么找到链表的中心位置,这里可以用到双指针技巧,定义slowflowslow 以步长 1 前进,fast以步长 2 前进,当 fast.Next = nil 时,slow的位置就是链表中心位置。当然还需要考虑链表个数奇偶数的情况。然后从中心位置反转后半部分链表与前半部分比较。

代码:

func isPalindrome(head *ListNode) bool {
if head == nil {
return true
}
var cp = Copy(head)
rcp := reverseList2(cp)
for ;head.Next != nil && rcp.Next != nil; {
if head.Val != rcp.Val {
return false
} else {
head = head.Next
rcp = rcp.Next
}
}
return true
}

func reverseList2(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
last := reverseList2(head.Next)
head.Next.Next = head
head.Next = nil
return last
}

func Copy(head *ListNode) *ListNode {
if head == nil {
return nil
}

var result = &ListNode{}
result.Val = head.Val
result.Next = Copy(head.Next)
return result
}

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7],

  3
 / \
9  20
  /  \    
 15   7

返回它的最大深度 3 。

Tips:

二叉树是一种树形结构,每个节点做多有两颗子树,且节点有左右之分。遍历方式分为深度优先遍历 DFS(前序、中序、后序)和广度优先遍历 BFS

type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}

例子如下:

  3
/ \
9 20
/ \
15 7

思路:

可以把问题拆解成 1 + 左右子中较大的层数

代码:

func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}
return max(maxDepth(root.Left), maxDepth(root.Right)) + 1
}

func max(left, right int) int {
if left > right {
return left
} else {
return right
}
}

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

思路:

在遍历列表时,将当前节点的 head.Next 指针改为指向prev

1->2->3->4->5->NULL
NULL<-1<-2<-2<-4<-5

代码:

// 迭代
type ListNode struct {
Val int
Next *ListNode
}

func reverseList(head *ListNode) *ListNode {
var temp *ListNode
var prev *ListNode = nil
for ; head != nil; {
temp = head.Next
head.Next = prev
prev = head
head = temp
}
return prev
}

递归解法很难用语言描述,我大概模拟一下递归过程,太巧妙了

// 递归
func reverseList2(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
last := reverseList2(head.Next)
println(head.Val, last.Val)
head.Next.Next = head
head.Next = nil
return last
}
reverseList(1->2->3->4->5->nil)

revreverseList(2->3->4->5->nil)

reverseList(3->4->5->nil)

reverseList(4->5->nil)

reverseList(5->nil)
head = 5->nil
return last = head

head = 4->5->nil
head.Next.Next = head // 5.Next -> 4
head.Next = nil // 4.Next -> nil
return last = 5->4->nil

head = 3->4<-5
|
nil
head.Next.Next = head // 4.Next -> 3
head.Next = nil // 3.Next -> nil
return last = 5->4-3->nil

head = 2->3<-4<-5
|
nil
head.Next.Next = head // 3.Next -> 2
head.Next = nil // 2.Next -> nil
return last = 5->4-3->2->nil

head = 1->2<-3<-4<-5
|
nil
head.Next.Next = head // 2.Next -> 1
head.Next = nil // 1.Next -> nil
return last = 5->4-3->2->1->nil

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

这道题目的关键是找到切入点,$dp(i)$ 指的是以第 i 个元素结尾的最长递增序列, 用数学公式表示如下:

表1 模拟

i 0 1 2 3 4 5 6 7
nums[i] 10 9 2 5 3 7 101 18
dp(i) 1 1 1 2 2 3 4 4

代码:

func lengthOfLIS(nums []int) int {
l := len(nums)
if l == 0 {
return 0
}
dp := make([]int, l)
// initial dp array default 1
for i := 0; i < l; i++ {
dp[i] = 1
}

res := 1
for i := 0; i < l; i++ {

for j := i - 1; j >= 0; j-- {
if nums[j] < nums[i] {
dp[i] = max(dp[i], 1 + dp[j])
}
}
res = max(res, dp[i])
}
return res
}

func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}

Coin Change 题目

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

解题思路

条件:

  1. 有多种面额
  2. 每种面额数量不限
  3. 输出凑成面额的最少硬币个数
  4. 没有可能输出 -1

如有 [1, 2, 5] 三种面额,总金额 amount 为 11,答案为 3 既 11 = 5 + 5 + 1

比如你想求 amount = 11 时的最少硬币数(原问题),如果知道凑出 amount = 10 的最少硬币数(子问题),只需要把子问题的答案加一(再选一枚面值为 1 的硬币))就是原问题的答案,因为硬币的数量是没有限制的,子问题之间没有相互制,是互相独立的。这类子问题又称为最优子结构。

func coinChange(coins []int, amount int) int {
cache := make(map[int]int)
return dp(coins, amount, cache)
}

func dp(coins []int, amount int, cache map[int]int) int {
if n, ok := cache[amount]; ok {
return n
}
if amount == 0 {
return 0
}
if amount < 0 {
return -1
}
res := math.MaxInt8
for _, coin := range coins {
sub := dp(coins, amount-coin, cache)
if sub == -1 {
continue
}
res = min(res, 1+sub)
}
if res != math.MaxInt8 {
cache[amount] = res
} else {
cache[amount] = -1
}
return cache[amount]
}

func min(a, b int) int {
if a < b {
return a
}
return b
}

For the past 33 years, I’ve looked in the mirror every morning and asked myself: “If today were the last day of my life, would I want to do what I am about to do today?” And whenever the answer has been “No” for too many days in a row, I know I need to change something.

在过去的 33 年,我每天早上都对着镜子问自己:“如果今天是生命的最后一天,我会做我准备要做的事情吗?”。每当我连续太多天都得到“不”的答案时,我就知道我必须有所改变了。

Remembering that I’ll be dead soon is the most important tool I’ve encountered to help me make the big choices in life. Because almost everything — all external expectations, all pride, all fear of embarrassment or failure — these things just fall away in the face of death, leaving only what is truly important.

提醒自己快死了,是我人生中下重大决定时所用过的最重要的方法。因为几乎每件事 — 所有的外界期望、所有名誉、所有对困窘或失败的恐惧 — 在面对死亡时,都消失了,只有最重要的东西才会留下。

Remembering that you are going to die is the beat way I know to avoid the trap of thinking you have something to lose. You are already naked. There is no reason not to follow your heart.

提醒自己快死了,是我所知避免掉入自己有东西要失去了的陷阱里最好的方法。人生不带来,死不带去,没什么道理不顺心而为。

About a year ago I was diagnosed with cancer. I had a scan at 7:30 in the morning, ant it clearly showed a tumor on my pancreas. I did not even know what a pancreas was. The doctors told me this was almost certainly a type of cancer that is incurable, and that I should expect to live no longer than three to six months.

一年前,我被诊断出癌症。我再早上 7:30 作了身体扫描,我的胰脏清楚的长了一个肿瘤。可我连胰脏是什么都不知道。医生告诉我,那几乎可以确定是一种不治之症,我大概只能活三到六个月了。

My doctor advised me to go home and get my affairs in order, which is doctor’s code for prepare to die. It means to try and tell your kids everything. You thought you’d have the next 10 years to tell them in just a few months. It means to make sure everything is buttoned up so that it will be as easy as possible for your family.

医生建议我回家,好好处理后事,这是医生对临终病人的标准建议。那代表你得试着在几个月内把你将来十年想跟小孩讲的话讲完。那代表你得把每件事情都搞定,家人才会尽量轻松。

It means to say your goodbyes.

那代表你得跟所有人说再见了。

创建数据库

CREATE DATABASE finance CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

创建用户

CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
CREATE USER 'username'@'%' IDENTIFIED BY 'password';

更改用户权限

GRANT ALL PRIVILEGES ON *.* TO 'username'@'localhost' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'username'@'%' WITH GRANT OPTION;

取消用户权限

REVOKE ALL PRIVILEGES ON *.* FROM 'username'@'localhost';
REVOKE ALL PRIVILEGES ON *.* FROM 'username'@'%';

刷新权限

FLUSH PRIVILEGES;

每次重新部署 Nginx 总会遇到 Nginx 403 Forbidden 这个错误,这里列一个排除此类错误的清单。

  1. 确认 Nginx 正常启动,这里要记录启动 Nginx 的用户供后续步骤使用,通常是 nginx

    $ ps -ef | grep nginx | grep -v grep

    root 1714 1 0 14:27 ? 00:00:00 nginx: master process /usr/sbin/nginx
    nginx 1973 1714 0 14:45 ? 00:00:00 nginx: worker process
  2. 确认 Nginx 的初始配置 /etc/nginx/nginx.conf 正确,特别注意 rootindex

    $ cat /etc/nginx/nginx.conf

    ...
    server {
    listen 80;
    listen [::]:80;
    server_name _;
    root /srv/zhongyiio/;

    location / {
    index index.html;
    }
    }
    ...

    $ sudo nginx -t # 如果不是以下结果,需要根据报错修改语法错误

    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
  3. 确认网站静态目录权限,一般情况文件夹 755、文件 644 就行,用户和用户组和步骤 1 保持一致,这里为 nginx。

    $ ls -la /srv/zhongyiio

    total 44
    drwxr-xr-x. 11 nginx nginx 235 Nov 13 08:27 .
    drwxr-xr-x. 3 root root 23 Nov 14 14:14 ..
    drw-r--r--. 5 nginx nginx 36 Nov 13 08:27 2017
    -rw-r--r--. 1 nginx nginx 10 Nov 13 08:27 CNAME
    drw-r--r--. 3 nginx nginx 36 Nov 13 08:27 archives
    -rw-r--r--. 1 nginx nginx 18197 Nov 13 08:27 atom.xml
    -rw-r--r--. 1 nginx nginx 1785 Nov 13 08:27 baidusitemap.xml
    drw-r--r--. 4 nginx nginx 32 Nov 13 08:27 categories
    drw-r--r--. 2 nginx nginx 44 Nov 13 08:27 css
    -rw-r--r--. 1 nginx nginx 1150 Nov 13 08:27 favicon.ico
    drw-r--r--. 2 nginx nginx 70 Nov 13 08:27 img
    -rw-r--r--. 1 nginx nginx 5164 Nov 13 08:27 index.html
    drw-r--r--. 2 nginx nginx 23 Nov 13 08:27 js
    drw-r--r--. 2 nginx nginx 24 Nov 13 08:27 reading
    -rw-r--r--. 1 nginx nginx 922 Nov 13 08:27 sitemap.xml
    drw-r--r--. 3 nginx nginx 23 Nov 13 08:27 tags
    drw-r--r--. 2 nginx nginx 24 Nov 13 08:27 talking
  4. 确认 SELinux 是否开启,不建议关闭 SELinux

    $ sestatus

    SELinux status: enabled
    SELinuxfs mount: /sys/fs/selinux
    SELinux root directory: /etc/selinux
    Loaded policy name: targeted
    Current mode: enforcing
    Mode from config file: enforcing
    Policy MLS status: enabled
    Policy deny_unknown status: allowed
    Max kernel policy version: 28

    # 如果状态为 enabled 使用该命令,确保之后建立的文件和复制的文件具有
    # 相同 httpd_sys_content_t 的类型,从而使受限的 httpd 进程能够访问
    $ sudo chcon -R -t httpd_sys_content_t /srv/zhongyiio/

如果你能保持冷静,当你身边的人们都变得疯狂,纷纷指责你;

如果你能相信自己,当所有人怀疑你,但且让他们怀疑去吧;

如果你遭等待,却不因等待疲倦,或者遭受欺骗,却不用谎言回敬,或者遭到憎恨,却不用憎恨反击,能够不得意忘形,也能够不巧言令色;

如果你能与凡人交谈,且彬彬有礼,或与国王同行,而不奴颜婢膝;

如果仇敌和密友都无法伤害你,如果你在乎每个人,但不会缺了谁都不行;

如果你在想发泄愤怒的那一分钟去进行六十秒的跑步,大地以及大地上的万物都属于你,而更重要的是,你将是真正的男人,我的孩子!

—- 摘自《如果》