Skip to content

Latest commit

 

History

History
254 lines (170 loc) · 9.02 KB

2018-12-11.md

File metadata and controls

254 lines (170 loc) · 9.02 KB

一. Algorithm

本周做的是 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)。

二. Review

本次读了关于 Go 语言的一篇文章: Concurrency in Golang

作者大致讲述了自己对 Go 语言中并发模型的理解。Go 语言的并发模型是基于 CSP(Communicating Sequential Processes),相信很多同学都知晓关于 Go 的一句名言:

Do not communicate by sharing memory; instead, share memory by communicating. 不要通过共享内存来通信,而应该通过通信来共享内存

这两句话到底意味着什么呢?下面是作者对此的理解:

1. Do not communicate by sharing memory

对于用过 Java 语言的同学都知道,我们需要通过多线程来实现并发。对于某些多个线程都要用到的数据,我们需要使用锁来避免同一时间多个线程访问同一个数据。其实就是锁住了某块在线程间共享内存,避免该块内存被同时访问,这就是所谓的 communicate by sharing memory 。但是这种方式会造成锁的竞争、内存管理、死锁以及难以解释的随机唤醒等问题。

2. share memory by communicating

既然传统的加锁共享内存机制存在问题,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
}

三. Tip

本周整服务器时用到 Linix 设置 shell 的一些基本操作操作,简要整理出来分享下吧。


一. 查看当前使用的 shell

shell 是通过环境变量 SHELL 设置的,通过查看环境变量即可得知。

$ echo $SHELL
/usr/bin/zsh

二. 查看可用的 shell

可以通过如下命令查看当前 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

三. 设置 shell

既然默认是 bash shell,我们想改成 zsh 该如何做呢? 可以通过三种方式来修改:

1. chsh 命令

没有管理员权限的话只能修改自己的 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 已经修改成功了。

2. 修改 passwd 文件

如果有管理员权限可以通过直接修改 /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
3. 创建用户时直接指定

在创建用户时可以直接指定用户要使用的 shell。

 useradd -s /bin/bash ahriJ

另外一个就是在 Ubuntu 上创建用户时 useradd 命令不会创建 /home/username 目录,要完成整个创建的话 建议使用 adduser 命令。


四. Share

本周阅读时思考了关于工作效率改善的一些问题,分享一下。

在读公众号 「欢喜的实验室」文章时有下面两句话;

求局部有效性和效率有时是在给自己挖陷阱,无形中导致了整体的混沌。

如果整体陷入了混沌,就好像赢得了每一场战役,但是却输掉了战争。

站在个人的角度,总体的优化是人生整体效率、认知、财富、阅历的提升,而日常生活的学习和工作就是每个局部的优化。不要过于注重每件事的成败得失,而是看到当前时段内的事物在整体内的效率和是否达到预期。

对于工作的优化,最重要的首先是系统的优化,然后才是个体的优化。

虽然通过个体的优化可以反过来提升系统的优化,但是局部态的优化并不能从根本上提升效率。那么该如何最大限度的通过个体的行动提升整体效能的优化呢?以下是自己想到的三点:

    1. 梳理优先级,优先完成最重要的事情。事情永远是做不完的,所以只能优先保证最重要的事落地完成,也就是是史蒂夫柯维在《高效能的七个习惯》中的 要事第一。注意这里是完成而不仅仅是做。效率的提高最本质上是我们完成了多少事,而不是我们开始做了多少事情。
    1. 提升自身的工作质量。每做完一件事,保证完成的质量是优秀的,这样可以避免返工的事情。比如写代码,反复思考、review 、自测后可以极大的减少后续的测试与改 bug 时间,从而将时间利用在更加值得的地方。
    1. 主动与职权更高的上司交流与改进。作为系统中的局部,无论怎样优化都是有局限性的,这时候就要向外拓展,如果可以尽量影响你的同事,如果不行就需要找更高级别的人去协调。比如制定更加合理的事务分配流程,沟通流程和开会准则,尽量减少不必要的会议等。

只有整体达到了最优,每个个体才可以充分发挥能力,更快的成长。否则就会形成内耗,无形中降低整个团队的战斗力以及挫伤个人的积极性。