本周做的是 2. Add Two Numbers。给定两个非空链表代表两个数字,计算两数字之和,结果用列表表示。列表第一个节点表示数字的个位,依次类推。
一道链表相关的题目,因为从节点开始代表个位,因此基本思路是遍历链表,依次相加求和,最终求出所有位上的数字然后构造成链表进行返回。代码如下:
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
List<Integer> nums = new ArrayList();
int carryBit = 0;
// 计算每位的数字,存入列表
while (l1 != null || l2 != null) {
int val1 = 0;
int val2 = 0;
if (l1 != null) {
val1 = l1.val;
l1 = l1.next;
}else {
l1 = null;
}
if (l2 != null) {
val2 = l2.val;
l2 = l2.next;
}else {
l2 = null;
}
int value = val1 + val2 + carryBit;
if (value >= 10) {
value = value - 10;
carryBit = 1;
}else {
carryBit = 0;
}
nums.add(value);
}
if (carryBit > 0) {
nums.add(carryBit);
}
int size = nums.size();
// 根据列表构造链表
ListNode result = new ListNode(nums.get(0));
ListNode tail = result;
for (int i = 1; i < size; i ++) {
ListNode node = new ListNode(nums.get(i));
tail.next = node;
tail = node;
}
return result;
}
}
每一个节点都需要进行一次遍历计算,因此时间复杂度为 O(N)。
本次读了关于 Go 语言的一篇文章: Concurrency in Golang。
作者大致讲述了自己对 Go 语言中并发模型的理解。Go 语言的并发模型是基于 CSP(Communicating Sequential Processes),相信很多同学都知晓关于 Go 的一句名言:
Do not communicate by sharing memory; instead, share memory by communicating. 不要通过共享内存来通信,而应该通过通信来共享内存
这两句话到底意味着什么呢?下面是作者对此的理解:
对于用过 Java 语言的同学都知道,我们需要通过多线程来实现并发。对于某些多个线程都要用到的数据,我们需要使用锁来避免同一时间多个线程访问同一个数据。其实就是锁住了某块在线程间共享内存,避免该块内存被同时访问,这就是所谓的 communicate by sharing memory 。但是这种方式会造成锁的竞争、内存管理、死锁以及难以解释的随机唤醒等问题。
既然传统的加锁共享内存机制存在问题,Go 语言是怎样实现并发的呢?
Go 语言允许我们将数据从一个线程发送到另一个线程。当一个线程处理完数据后将数据发出,另一个线程等待接收数据。当数据没有处理完时,线程会一直等待,这样就大大降低了出现锁冲突的概率。这就是所谓的 share memory by communicating。
在使用 Go 的并发模型时,需要注意的地方是避免过度使用,某些地方还是需要通过传统的锁机制来实现的,比如引用计数、文件读写等,这些都可以通过 Go 提供的 sync 包实现。
下面是一段简单的例子,通过开启两个协程和通道,进行数据的发送和处理。
package main
import (
"fmt"
"time"
)
func main() {
// 创建发送数据的通道
ch := make(chan int)
// 接收数据的通道
done := make(chan bool)
// 开启协程
go sendingGoRoutine(ch)
go receivingGoRoutine(ch,done)
// 这里会阻塞,直到数据接收完毕
<- done
}
func sendingGoRoutine(ch chan int){
//start a timer to wait 5 seconds
t := time.NewTimer(time.Second*5)
<- t.C
fmt.Println("Sending a value on a channel")
// 发送数据
ch <- 45
}
func receivingGoRoutine(ch chan int, done chan bool){
// 接收 ch 通道的数据
v := <- ch
fmt.Println("Received value ", v)
// 处理完成后向 done 通道发送数据告知操作已完成
done <- true
}
本周整服务器时用到 Linix 设置 shell 的一些基本操作操作,简要整理出来分享下吧。
shell 是通过环境变量 SHELL 设置的,通过查看环境变量即可得知。
$ echo $SHELL
/usr/bin/zsh
可以通过如下命令查看当前 Linux 系统可用的 shell
$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
/usr/bin/tmux
/bin/zsh
/usr/bin/zsh
所有 Linux 发行版默认提供的 shell 为 bash shell,不同的 shell 有不同的特性,但就使用来说最为强大的应该是 zsh ,其结合了其他类型 shell 的特性并提供了高级编程特性、共享历史文件和主题化提示符等高级特性。
关于 zsh 的使用可以参考池建强老师的一篇博客: 终极 Shell。
既然默认是 bash shell,我们想改成 zsh 该如何做呢? 可以通过三种方式来修改:
没有管理员权限的话只能修改自己的 shell,可以通过 chsh 命令实现:
ahriJ@ahriJ:~$ chsh
输入命令后,会提示让你输入要指定的 shell 路径,如果不输入则使用默认值。路径可以通过上面提到的 cat /etc/shells 命令查看。
输入密码和路径后重新登录再次查看 SHELL 变量就会发现已经变成我们设置的 shell 了。
Password:
Changing the login shell for qcfit
Enter the new value, or press ENTER for the default
Login Shell [/bin/bash]: /usr/bin/zsh
当然也可以直接指定
$ chsh -s /bin/bash
Password:
输入密码后修改成功,重新打开窗口就会发现 shell 已经修改成功了。
如果有管理员权限可以通过直接修改 /etc/passwd 文件,这样可以进行批量修改。
/etc/passwd 每行代表一个用户,后面指定了该用户使用的 shell,可以直接进行替换。
ahriJ:x:1000:1000:,,,:/home/ahriJ:/usr/bin/zsh
将 zsh 换回 bash
ahriJ:x:1000:1000:,,,:/home/ahriJ:/usr/bin/bash
在创建用户时可以直接指定用户要使用的 shell。
useradd -s /bin/bash ahriJ
另外一个就是在 Ubuntu 上创建用户时 useradd 命令不会创建 /home/username 目录,要完成整个创建的话 建议使用 adduser 命令。
本周阅读时思考了关于工作效率改善的一些问题,分享一下。
在读公众号 「欢喜的实验室」文章时有下面两句话;
求局部有效性和效率有时是在给自己挖陷阱,无形中导致了整体的混沌。
如果整体陷入了混沌,就好像赢得了每一场战役,但是却输掉了战争。
站在个人的角度,总体的优化是人生整体效率、认知、财富、阅历的提升,而日常生活的学习和工作就是每个局部的优化。不要过于注重每件事的成败得失,而是看到当前时段内的事物在整体内的效率和是否达到预期。
对于工作的优化,最重要的首先是系统的优化,然后才是个体的优化。
虽然通过个体的优化可以反过来提升系统的优化,但是局部态的优化并不能从根本上提升效率。那么该如何最大限度的通过个体的行动提升整体效能的优化呢?以下是自己想到的三点:
-
- 梳理优先级,优先完成最重要的事情。事情永远是做不完的,所以只能优先保证最重要的事落地完成,也就是是史蒂夫柯维在《高效能的七个习惯》中的 要事第一。注意这里是完成而不仅仅是做。效率的提高最本质上是我们完成了多少事,而不是我们开始做了多少事情。
-
- 提升自身的工作质量。每做完一件事,保证完成的质量是优秀的,这样可以避免返工的事情。比如写代码,反复思考、review 、自测后可以极大的减少后续的测试与改 bug 时间,从而将时间利用在更加值得的地方。
-
- 主动与职权更高的上司交流与改进。作为系统中的局部,无论怎样优化都是有局限性的,这时候就要向外拓展,如果可以尽量影响你的同事,如果不行就需要找更高级别的人去协调。比如制定更加合理的事务分配流程,沟通流程和开会准则,尽量减少不必要的会议等。
只有整体达到了最优,每个个体才可以充分发挥能力,更快的成长。否则就会形成内耗,无形中降低整个团队的战斗力以及挫伤个人的积极性。