Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARTS 第二十一周(2019.11.18~2019.11.24) #21

Open
catcuts opened this issue Nov 26, 2019 · 0 comments
Open

ARTS 第二十一周(2019.11.18~2019.11.24) #21

catcuts opened this issue Nov 26, 2019 · 0 comments
Labels

Comments

@catcuts
Copy link
Owner

catcuts commented Nov 26, 2019

ARTS 第二十一周(2019.11.18~2019.11.24)

Algorithm 两两交换链表中的节点(中等难度)

题目

两两交换链表中的节点(中等难度)

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var swapPairs = function(head) {
    // 如果为空链表则直接返回头(null)
    if (!head) return head;
    // 取得本节点(head)的下一个节点
    let next = head.next;
    // 如果本节点有下一个节点
    if (next) {
        // 取得本节点下一个节点的下一个节点,作为下一对节点的头节点
        let nextPairHead = next.next;
        // 令下一个节点的下一个节点指向本节点
        next.next = head;
        // 令本节点的下一个节点指向 下一对节点互换后的头节点
        head.next = swapPairs(nextPairHead);
        // 返回本节点的下一个节点,作为本对的新头节点返回
        return next;
    }
    // 如果本节点没有下一个节点,则返回本节点,作为“本对节点”(实际上只有一个节点)的头结点
    else {
        return head;
    }
};

执行结果:通过
执行用时:60 ms,在所有 javascript 提交中击败了 91.77% 的用户
内存消耗:33.8 MB,在所有 javascript 提交中击败了 16.23% 的用户

思路:
使用递归,详见注释:
如果为空链表则直接返回头(null)
取得本节点(head)的下一个节点
如果本节点有下一个节点
取得本节点下一个节点的下一个节点,作为下一对节点的头节点
令下一个节点的下一个节点指向本节点
令本节点的下一个节点指向 下一对节点互换后的头节点
返回本节点的下一个节点,作为本对的新头节点返回
如果本节点没有下一个节点,则返回本节点,作为“本对节点”(实际上只有一个节点)的头结点

另外,还可以使用迭代:

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var swapPairs = function(head) {
    if (head && head.next) {
        // 上一个节点
        let prev;
        // 遍历的当前节点
        let curr = head;
        // 最终要返回的头节点
        head = curr.next;
        while (curr && curr.next) {
            let next = curr.next;

            if (prev) prev.next = next;
            
            curr.next = next.next;
            next.next = curr;

            prev = curr;
            curr = curr.next;
        }
    }
    return head;
};

对比:
与高分对比:
本代码运行 55 个测试用例花费约 60ms,平均一个测试用例约 1.1ms;
高分代码运行 55 个测试用例花费约 60ms,平均一个测试用例约 1.1ms。
思路一致代码略。

Review 图解 Kubernetes

阅读:
Know Kubernetes — Pictorially

点评:
Kubernetes 被称作 “containers orchestrator”,也就是容器编排者。

所谓“容器”(container),就是用于盛放应用代码及其依赖库,还有操作系统内核依赖库的程序。容器将以上这些东西与容器的宿主(也就是运行容器的主机)进行隔离,包括:

  • 工作空间的隔离(进程、网络)
  • 资源的隔离(CPU、内存)
  • 文件系统的隔离(Docker 使用 UniconFS)
    正是对应用程序运行环境的隔离和包装,使得应用程序在不同环境下都能提供同样的稳定性,即杜绝了“这段代码在我的电脑上能运行啊”的问题。

而 Kubernetes 则起到了对这些容器编排的作用。所谓编排,就是管理,具体是提供诸如以下但不限于以下这些功能:

  • 容器的运行管理(比如一个友好的管理界面,而非使用命令行或脚本)
  • 容器的互相关联(比如一个容器要自动与另一个容器的最新版本通讯)
  • 容器的灰度部署(同运行管理)

容器编排者有很多,为何作者独爱 Kubernetes ?

因为它如同乐高积木般的特性,使得使用者能够在容器群规模扩大的同时仍能对它们的管理保持得心应手,并且通过对容器进行灵活的组装编排,实现不同的自定义需求(比如任务调度功能等),而高度活跃的社区更是令这种自定义需求能够获得快速的响应。

接着作者就开始以图文并茂的方式介绍 Kubernetes 的架构啦,具体看原文吧~

简单来说,就是:

  • 每个 Kubernetes 集群都包含两种节点(Node,也称机器 Machine),分别是主节点(Master)和工作节点(Worker);其中主节点用于监测和控制工作节点,而工作负载(payload,即应用程序 application)则运行在工作节点上。另外,高可用的集群应至少包含一个主节点和三个工作节点。
  • 每个主节点都是由以下几部分构成的:
    • etcd:一个数据库,存储本节点的所有数据,包括当前的运行状态、接入信息以及其他配置信息。
    • API Server:一个 RESTful API 服务器。工作节点、以及主节点中几乎所有的组件都是通过这个 API 服务器来与主节点的 Kubernetes 内核进行通讯的。
    • Scheduler:一个任务调度器,用于动态分配负载到工作节点。
    • Control Manager:一个控制器,用于实时轮询获取集群状态(通过上述 API 服务器),并采取必要措施控制运行状态达到设定预期。
  • 每个工作节点都是由以下几部分构成的:
    • kubelet:它是工作节点的核心,与主节点的 API 服务器进行通讯,从而运行分配给该工作节点的容器。
    • kube Proxy:一个网络代理,为工作节点中的所有 pods 提供网络通讯能力。
    • Pod:一个容器荚,如同豌豆荚一样,包着多个容器,各个容器之间能够进行网络通讯和共享数据,容器之间互为 localhost 提供了极大的便利性(IP 不用写死了!)。容器荚提供了不同的运行模式满足不同的应用需求,详见
  • 每个节点都提供了多种不同的控制器(Controller)。比如:
    • 复制集控制器(ReplicaSet Controller):的主要职责就是为容器荚(Pod)创建复制集(ReplicaSet),并在监测到一个容器荚宕机的情况下对其进行快速替换;
    • 部署控制器(Deployment Controller)提供复制集的灰度替换。
    • 服务控制器(Service Controller):主要负责不同工作节点之间的负载均衡;
    • 入口控制器(Ingress Cotroller):控制集群数据的进出

Tip 使用 vscode 作为默认的 git mergetool 和 difftool

当使用 git 命令行合并代码时,通常会用到 git mergetool 命令。

这个命令会调出一个编辑器,用来编辑代码,进行合并。

有时候,我们希望使用 vscode 作为默认的编辑器,因为它的代码合并和比较功能较为友好。

这时可以使用这个方法

首先参考官方文档,找到 gitconfig

git config --list --show-origin

然后打开 gitconfig,添加或编辑如下内容:

# 声明默认的 mergetool
[merge]
    tool = vscode
# 使用 vscode 作为 mergetool 时,
# vscode 的打开命令
[mergetool "vscode"]
    cmd = code --wait $MERGED
# 声明默认的 difftool
[diff]
    tool = vscode
# 使用 vscode 作为 difftool 时,
# vscode 的打开命令
[difftool "vscode"]
    cmd = code --wait --diff $LOCAL $REMOTE

此时,当进入合并状态时,git mergetoolgit difftool 就会调出 vscode,
就可以很开心地使用 vscode 的合并和比较功能了~

参考:

Share [极客专栏:设计模式之美] 09 | 为什么基于接口而非实现编程?有必要为每个类都定义接口吗

分享一篇极客专栏《设计模式之美》
07 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗

本文介绍了“基于接口而非实现”的编程原则,以及如何贯彻该原则,并分析了是否有必要为每个类都定义接口。
重要段落摘抄如下:

为什么基于接口而非实现编程?

“基于接口而非实现编程”这条原则的另一个表述方式,是“基于抽象而非实现编程”。
后者的表述方式其实更能体现这条原则的设计初衷(也可参考扩展阅读)。

在软件开发中,最大的挑战之一就是需求的不断变化,
这也是考验代码设计好坏的一个标准。

越抽象、越顶层、越脱离具体某一实现的设计,
越能提高代码的灵活性,越能应对未来的需求变化。

好的代码设计,不仅能应对当下的需求,而且在将来需求发生变化的时候,
仍然能够在不破坏原有代码设计的情况下灵活应对。

而抽象就是提高代码可扩展性、灵活性、可维护性最有效的手段之一。

如何进行基于接口而非实现的编程?

要遵从“基于接口而非实现编程”的原则,具体来讲,我们需要做到下面 3 点:

  1. 函数的命名不能保护任何实现细节。
  2. 应对具体的实现细节进行封装。
  3. 应为实现类定义抽象的接口。
    (使用者依赖接口,而非具体的实现类来编程)

我们在做软件开发的时候,一定要有抽象意识、封装意识、接口意识。
在定义接口的时候,不要暴露任何实现细节。
接口的定义只表明做什么,而不是怎么做。

此外,在设计接口的时候,我们要多思考一下,
这样的接口是否足够通用、
是否能够做到在替换具体的接口实现的时候,不需要任何接口定义的改动

是否有必要为每个类都定义接口?

从“基于接口而非实现编程”原则的设计初衷出发,
如果在我们的业务场景中,
某个功能只有一种实现方式,未来也不可能被其他实现方式替换,
那么我们就没有必要为其设计接口,也没有必要基于接口编程,
而是直接使用实现类就可以了。

也就是说,越是不稳定的系统,我们越是要在代码的可扩展性、维护性上下功夫。
而相反,如果某个系统特别稳定,在开发完之后,基本上不需要做维护,
那么我们就没有必要为其扩展性和维护性投入不必要的开发时间。

扩展阅读:

@catcuts catcuts added the ARTS label Nov 26, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant