[Algorithm]红黑树笔记

主要来源 https://segmentfault.com/a/1190000000472153

红黑树,众所周知,是一个STL标准库的map当中sort的算法。其实在学CS100的时候完全不知道这些是什么,感谢侯捷老师,带我领略STL的奇妙世界。

红黑树介绍

第一个性质

对于树中的每一个节点,如哦它有左子树,且他的中所有节点的值不大于该节点值;如果它有右子树,则右子树中所有节点的值不小于该节点的值。

红黑树,能保证在最坏情况下,基本的动态几何操作的时间均为O(lgn)。

在红黑树的每一个节点中都含有五个域color,key,left,right,p
如果指针域没有,则轮空

性质总结

1.每个节点要么是红的,要么是黑的。
2.根节点是黑的。
3.每个叶节点(叶节点即指树尾端NIL指针或NULL节点)是黑的。
4.如果一个节点是红的,那么它的两个儿子都是黑的。
5.对于任一节点而言,其到叶节点树尾端NIL指针的每一条路径都包含相同数目的黑节点。

一些常规操作

左旋

img

当在某个节点pivot上的时候,假设右孩子不是NIL[t],pivot 可以为树内的任意左孩子而不是NIL[t]的节点。

左旋以pivot到y之间的链为“支轴”进行,它使y成为该孩子树的新根,而y的左孩子b则成为pivot的右孩子。

LEFT-ROTATE(T, x)
 y ← right[x] ▹ Set y.
 right[x] ← left[y]      ▹ Turn y's left subtree into x's right subtree.
 p[left[y]] ← x
 p[y] ← p[x]             ▹ Link x's parent to y.
 if p[x] = nil[T]
    then root[T] ← y
    else if x = left[p[x]]
            then left[p[x]] ← y
            else right[p[x]] ← y
 left[y] ← x             ▹ Put x on y's left.
 p[x] ← y

右旋

RB-INSERT(T, z)
 y ← nil[T]
 x ← root[T]
 while x ≠ nil[T]
     do y ← x
        if key[z] < key[x]
           then x ← left[x]
           else x ← right[x]
 p[z] ← y
 if y = nil[T]
    then root[T] ← z
    else if key[z] < key[y]
            then left[y] ← z
            else right[y] ← z
 left[z] ← nil[T]
 right[z] ← nil[T]
 color[z] ← RED
 RB-INSERT-FIXUP(T, z)

咱们来具体分析下,此段代码:

RB-INSERT(T, z),将z插入红黑树T 之内。

为保证红黑性质在插入操作后依然保持,上述代码调用了一个辅助程序RB-INSERT-FIXUP来对节点进行重新着色,并旋转。

 left[z] ← nil[T]
 right[z] ← nil[T]  //保持正确的树结构

第16行,将z着为红色,由于将z着为红色可能会违背某一条红黑树的性质,
所以,在第17行,调用RB-INSERT-FIXUP(T,z)来保持红黑树的性质。

RB-INSERT-FIXUP(T, z),如下所示:

while color[p[z]] = RED
    do if p[z] = left[p[p[z]]]
          then y ← right[p[p[z]]]
               if color[y] = RED
                  then color[p[z]] ← BLACK                    ▹ Case 1
                       color[y] ← BLACK                       ▹ Case 1
                       color[p[p[z]]] ← RED                   ▹ Case 1
                       z ← p[p[z]]                            ▹ Case 1
                  else if z = right[p[z]]
                          then z ← p[z]                       ▹ Case 2
                               LEFT-ROTATE(T, z)              ▹ Case 2
                          color[p[z]] ← BLACK                 ▹ Case 3
                          color[p[p[z]]] ← RED                ▹ Case 3
                          RIGHT-ROTATE(T, p[p[z]])            ▹ Case 3
          else (same as then clause
                      with "right" and "left" exchanged)
color[root[T]] ← BLACK

ok,参考一网友的言论,用自己的语言,再来具体解剖下上述俩段代码。
为了保证阐述清晰,我再写下红黑树的5个性质:

  1. 每个节点要么是红的,要么是黑的。
  2. 根节点是黑的。
  3. 每个叶节点,即空节点(NIL)是黑的。
  4. 如果一个节点是红的,那么它的俩个儿子都是黑的。
  5. 对每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点。

在对红黑树进行插入操作时,我们一般总是插入红色的节点,因为这样可以在插入过程中尽量避免对树的调整。 那么,我们插入一个节点后,可能会使原树的哪些性质改变列? 由于,我们是按照二叉树的方式进行插入,因此元素的搜索性质不会改变。

如果插入的节点是根节点,性质2会被破坏,如果插入节点的父节点是红色,则会破坏性质4。 因此,总而言之,插入一个红色节点只会破坏性质2或性质4。 我们的恢复策略很简单,

  1. 把出现违背红黑树性质的节点向上移,如果能移到根节点,那么很容易就能通过直接修改根节点来恢复红黑树的性质。直接通过修改根节点来恢复红黑树应满足的性质。
  2. 穷举所有的可能性,之后把能归于同一类方法处理的归为同一类,不能直接处理的化归到下面的几种情况,

注:以下情况3、4、5与上述算法导论上的代码RB-INSERT-FIXUP(T, z),相对应:

插入修复具体操作情况

1) 情况1:插入的是根节点。

原树是空树,此情况只会违反性质2。

对策:直接把此节点涂为黑色。

2) 情况2:插入的节点的父节点是黑色。

此不会违反性质2和性质4,红黑树没有被破坏。

对策:什么也不做。

3) 情况3:当前节点的父节点是红色且祖父节点的另一个子节点(叔叔节点)是红色。

此时父节点的父节点一定存在,否则插入前就已不是红黑树。与此同时,又分为父节点是祖父节点的左子还是右子,对于对称性,我们只要解开一个方向就可以了。 在此,我们只考虑父节点为祖父左子的情况。 同时,还可以分为当前节点是其父节点的左子还是右子,但是处理方式是一样的。我们将此归为同一类。

对策:将当前节点的父节点和叔叔节点涂黑,祖父节点涂红,把当前节点指向祖父节点,从新的当前节点重新开始算法。

针对情况3,变化前(图片来源:saturnman)[当前节点为4节点]:

img

变化后:

img

4) 情况4:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的右子

对策:当前节点的父节点做为新的当前节点,以新当前节点为支点左旋。

如下图所示,变化前[当前节点为7节点]:

img

变化后:

img

5) 情况5:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的左子

解法:父节点变为黑色,祖父节点变为红色,在祖父节点为支点右旋

如下图所示[当前节点为2节点]

img

变化后:

img

回顾:经过上面情况3、情况4、情况5等3种插入修复情况的操作示意图,读者自会发现,后面的情况4、情况5都是针对情况3插入节点4以后,进行的一系列插入修复情况操作,不过,指向当前节点N指针一直在变化。所以,你可以想当然的认为:整个下来,情况3、4、5就是一个完整的插入修复情况的操作流程。

II、ok,接下来,咱们最后来了解,红黑树的删除操作:

为了保证以下的介绍与阐述清晰,我第三次重写下红黑树的5个性质

  1. 每个节点要么是红的,要么是黑的。
  2. 根节点是黑的。
  3. 每个叶节点,即空节点(NIL)是黑的。
  4. 如果一个节点是红的,那么它的俩个儿子都是黑的。
  5. 对每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点。

相信,重述了3次,你应该有深刻记忆了。:D

我们删除的节点的方法与常规二叉搜索树中删除节点的方法是一样的,如果被删除的节点不是有双非空子女,则直接删除这个节点,用它的唯一子节点顶替它的位置,如果它的子节点分是空节点,那就用空节点顶替它的位置,如果它的双子全为非空,我们就把它的直接后继节点内容复制到它的位置,之后以同样的方式删除它的后继节点,它的后继节点不可能是双子非空,因此此传递过程最多只进行一次。

继续讲解之前,补充说明下二叉树节点删除的几种情况,待删除的节点按照儿子的个数可以分为三种:

  1. 没有儿子,即为叶节点。直接把父节点的对应儿子指针设为NULL,删除儿子节点就OK了。
  2. 只有一个儿子。那么把父节点的相应儿子指针指向儿子的独生子,删除儿子节点也OK了。
  3. 有两个儿子。这是最麻烦的情况,因为你删除节点之后,还要保证满足搜索二叉树的结构。其实也比较容易,我们可以选择左儿子中的最大元素或者右儿子中的最小元素放到待删除节点的位置,就可以保证结构的不变。当然,你要记得调整子树,毕竟又出现了节点删除。习惯上大家选择左儿子中的最大元素,其实选择右儿子的最小元素也一样,没有任何差别,只是人们习惯从左向右。这里咱们也选择左儿子的最大元素,将它放到待删节点的位置。左儿子的最大元素其实很好找,只要顺着左儿子不断的去搜索右子树就可以了,直到找到一个没有右子树的节点。那就是最大的了。

OK,回到红黑树上来。算法导论一书,给的红黑树节点删除的算法实现是:

RB-DELETE(T, z) 单纯删除节点的总操作

  if left[z] = nil[T] or right[z] = nil[T]
    then y ← z
    else y ← TREE-SUCCESSOR(z)
 if left[y] ≠ nil[T]
    then x ← left[y]
    else x ← right[y]
 p[x] ← p[y]
 if p[y] = nil[T]
    then root[T] ← x
    else if y = left[p[y]]
            then left[p[y]] ← x
            else right[p[y]] ← x
 if y 3≠ z
    then key[z] ← key[y]
         copy y's satellite data into z
 if color[y] = BLACK
    then RB-DELETE-FIXUP(T, x)
 return y

在删除节点后,原红黑树的性质可能被改变,如果删除的是红色节点,那么原红黑树的性质依旧保持,此时不用做修正操作,如果删除的节点是黑色节点,原红黑树的性质可能会被改变,我们要对其做修正操作。那么哪些树的性质会发生变化呢,如果删除节点不是树唯一节点,那么删除节点的那一个支的到各叶节点的黑色节点数会发生变化,此时性质5被破坏。如果被删节点的唯物主唯一非空子节点是红色,而被删节点的父节点也是红色,那么性质4被破坏。如果被删节点是根节点,而它的唯一非空子节点是红色,则删除后新根节点将变成红色,违背性质2。

RB-DELETE-FIXUP(T, x) 恢复与保持红黑性质的工作

 while x ≠ root[T] and color[x] = BLACK
     do if x = left[p[x]]
           then w ← right[p[x]]
                if color[w] = RED
                   then color[w] ← BLACK                        ▹  Case 1
                        color[p[x]] ← RED                       ▹  Case 1
                        LEFT-ROTATE(T, p[x])                    ▹  Case 1
                        w ← right[p[x]]                         ▹  Case 1
                if color[left[w]] = BLACK and color[right[w]] = BLACK
                   then color[w] ← RED                          ▹  Case 2
                        x p[x]                                  ▹  Case 2
                   else if color[right[w]] = BLACK
                           then color[left[w]] ← BLACK          ▹  Case 3
                                color[w] ← RED                  ▹  Case 3
                                RIGHT-ROTATE(T, w)              ▹  Case 3
                                w ← right[p[x]]                 ▹  Case 3
                         color[w] ← color[p[x]]                 ▹  Case 4
                         color[p[x]] ← BLACK                    ▹  Case 4
                         color[right[w]] ← BLACK                ▹  Case 4
                         LEFT-ROTATE(T, p[x])                   ▹  Case 4
                         x ← root[T]                            ▹  Case 4
        else (same as then clause with "right" and "left" exchanged)
 color[x] ← BLACK

上面的修复情况看起来有些复杂,下面我们用一个分析技巧:我们从被删节点后来顶替它的那个节点开始调整,并认为它有额外的一重黑色。这里额外一重黑色是什么意思呢,我们不是把红黑树的节点加上除红与黑的另一种颜色,这里只是一种假设,我们认为我们当前指向它,因此空有额外一种黑色,可以认为它的黑色是从它的父节点被删除后继承给它的,它现在可以容纳两种颜色,如果它原来是红色,那么现在是红+黑,如果原来是黑色,那么它现在的颜色是黑+黑。有了这重额外的黑色,原红黑树性质5就能保持不变。现在只要花时是恢复其它性质就可以了,做法还是尽量向根移动和穷举所有可能性。 ——saturnman

红黑树删除修复操作的几种情况@saturnman:

注:以下的情况3、4、5、6,与上述算法导论之代码RB-DELETE-FIXUP(T, x) 恢复与保持 中case1,case2,case3,case4相对应。

情况1:当前节点是红+黑色

解法,直接把当前节点染成黑色,结束。此时红黑树性质全部恢复。

情况2:当前节点是黑+黑且是根节点

解法:什么都不做,结束

情况3:当前节点是黑+黑且兄弟节点为红色(此时父节点和兄弟节点的子节点分为黑)。

解法:把父节点染成红色,把兄弟节点染成黑色,之后重新进入算法(我们只讨论当前节点是其父节点左孩子时的情况)。此变换后原红黑树性质5不变,而把问题转化为兄弟节点为黑色的情况(注:变化前,原本就未违反性质5,只是为了把问题转化为兄弟节点为黑色的情况)。

变化前:

img

变化后:

img

情况4:当前节点是黑加黑且兄弟是黑色且兄弟节点的两个子节点全为黑色。

解法:把当前节点和兄弟节点中抽取一重黑色追加到父节点上,把父节点当成新的当前节点,重新进入算法。(此变换后性质5不变)

变化前

img

变化后

img

情况5:当前节点颜色是黑+黑,兄弟节点是黑色,兄弟的左子是红色,右子是黑色。。

解法:把兄弟节点染红,兄弟左子节点染黑,之后再在兄弟节点为支点解右旋,之后重新进入算法。此是把当前的情况转化为情况6,而性质5得以保持。

变化前:

img

变化后:

img

情况6:当前节点颜色是黑-黑色,它的兄弟节点是黑色,但是兄弟节点的右子是红色,兄弟节点左子的颜色任意。

解法:把兄弟节点染成当前节点父节点的颜色,把当前节点父节点染成黑色,兄弟节点右子染成黑色,之后以当前节点的父节点为支点进行左旋,此时算法结束,红黑树所有性质调整正确。

变化前:

img

变化后:

img

搞了个黑威联通

自编译了一套威联通系统,参考老骥伏枥

机器 4570 16GB 2T*4 1066 大概13年给家里买的台式(主要是给我打QQ三国和三国无双,结果我爸为了不让我玩后者就没给我买显卡。大学以后才购入的1066跑渲染、剪视频和炼丹,做了1年主力机后和15底年的macbook air一起退役。

编译好的文件在 https://www.asplos.dev/vQTS-Boot.img 和 https://www.asplos.dev/TS653B20180528.img

2022.1.29 update 那台机器后来弄成了我的gitlab CI,先后支撑了我的CG CI,春东课sniper CI,还有自娱自乐训练的一些东西,于2021.5.16 宕机 被前前后后拆了垃圾装又被Logan拿了盘去给他的nas,1066送给了murez。

Docker on Nvidia Jetson Nano

#!/bin/bash
# Copied and modified from https://github.com/Technica-Corporation/Tegra-Docker
# Copyright (c) 2017, Technica Corporation. All rights reserved.

NV_LIBS="/usr/lib/aarch64-linux-gnu \
	 /usr/local/cuda/lib64 \
	/usr/local/cuda \
	 /usr/src/tensorrt \
         /usr/local/cuda-10.0 \
         /usr/include \
	/usr/src "

LD_PATH="/usr/lib/aarch64-linux-gnu \
         /usr/lib/aarch64-linux-gnu/tegra \
         /usr/local/cuda/lib64 \
	 /usr/src/tensorrt \
	/usr/local/cuda \
	 /usr/local/cuda-10.0 \
	 /usr/include"

GPU_DEVICES="/dev/nvhost-ctrl \
             /dev/nvhost-ctrl-gpu \
             /dev/nvhost-prof-gpu \
             /dev/nvmap \
             /dev/nvhost-gpu \
             /dev/nvhost-as-gpu"

NV_DOCKER_ARGS="--net=host"

build_docker_args() {
        #set the required libraries as volumes on the docker container

        LIB_ARGS=""
        for lib in $NV_LIBS; do
                LIB_ARGS="$LIB_ARGS -v $lib:$lib"
        done

        #set the required devices to be passed through to the container
        DEV_ARGS=""
        for dev in $GPU_DEVICES; do
                DEV_ARGS="$DEV_ARGS --device=$dev"
        done

        NV_DOCKER_ARGS="$NV_DOCKER_ARGS $LIB_ARGS $DEV_ARGS"
}

build_env() {
        #build the LD_LIBRARY_PATH
        LD_LIBRARY_PATH=""
        for lib in $LD_PATH; do
                LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$lib"
        done
}

if [[ $# -ge 2 &amp;&amp; $1 == "run" ]]; then
        echo "Running an nvidia docker image"
        build_docker_args
	build_env

	DOCKER_OPTS="-e LD_LIBRARY_PATH=$LD_LIBRARY_PATH $NV_DOCKER_ARGS ${@:2}"
        echo "docker run $DOCKER_OPTS"
        docker run $DOCKER_OPTS
fi

rDNA AMD怕不是这次要逆天

RX 5700 XT分为几个主要模块,使用AMD的Infinity Fabric连接在一起。 命令处理器和PCI Express接口将GPU连接到外部世界并控制各种功能。 两个着色器引擎包含所有可编程计算资源和一些专用图形硬件。 两个着色器引擎中的每一个包括两个着色器阵列,其包括新的双计算单元,共享图形L1高速缓存,基元单元,光栅化器和四个渲染后端(RB)。 此外,GPU包括用于多媒体和显示处理的专用逻辑。 对内存的访问通过分区的L2缓存和内存控制器进行路由。

RDNA架构是第一个使用PCIe®4.0与主机连接的GPU系列
处理器。

用软件的力量代替硬件

我们知道限制网速的因素有很多,内网有这么好和多的手法传输数据,却因为厂商之间的壁垒而打破,尤其是华为、苹果还有其他鬼。DJI的设备和安卓还有苹果还好。这个时候就需要一个nas。

树莓派3b+大家都知道是一个只有百兆网口和usb2.0的设备,而且它们占用同一根数据总线,操作数一多,或者两者同时占用,比如从usb2.0到百兆网口,极限速度大概只有4MB/s。

现在arm开发板的瓶颈大多在io和内存,cpu的性能可以比大多数入门级单核vps好。挂个docker不是什么问题。

有人说一定要x86架构,我不敢苟同,他威联通还能用marvell的armada8040做4k处理器,再加个gpu训练人脸识别就绝了。还能做gpu docker

https://community.arm.com/cn/b/blog/posts/marvell-armada-8040

Marvell ARMADA 8040超大规模vSoC的特性包括:
• 2.0GHz四核ARM Cortex-A72
• 1MB共享L2存储以及1MB专用L3存储
• 完整的ARMv8-A CPU虚拟化以及I/O虚拟化
• 高吞吐率低延迟的内存一致性子系统
• 拥有2x10GbE + 4x2.5GbE连接性的网络包处理器
• 可扩展到从1GbE到10GbE间的多端口
• SuitB兼容、10Gbp/s吞吐量安全引擎、IPSEC以及SSL协议卸载
• DDR3/3L/4 32b/64b+ECC扩展
• SATA3.0、USB3.0、PCIe3.0

ClearFog CX 8K

有pcie就可以拓展显卡了,虽然它只有工控机的能力。拓展雷电三就完全不一样了。

用坚果pro当tensorflow 训练器 & 人脸识别服务器


前一段时间很想要一个速度快的nas,更准确的说是nrs。

但是,我同时拥有那么多垃圾,为什么要买一个900块钱的新设备呢。

而且没有硬盘架的nas是没有灵魂的nas(我的下一个目标是amd gpu训练集群)

那,我就想到我的坚果pro垃圾

 

进入正题(刷机)
先说一下,Pro只有魔趣和flyme两个第三方系统。本来有MIUI的资源的后来找不到了。。

注意:如果需要刷面具magisk的话,必须先解Bootloader再刷
不多逼逼了干正事

1.准备一根9008线
这个tb有卖,也就十块钱这样,自己做的话难度系数还是有点的。上张图

这种需要你整个转接头

2.安装驱动和刷机工具
驱动不是刚需,但装上会比较好一点

通用驱动

然后下载安装qpst

qpst和twrp线刷这两个

如果你是win7电脑的话可能会提示你缺少组建
Microsoft NET Farmeworke 4.0
这时候就需要自行安装

这是最新版本

官网可以下载

查看链接 这是链接

安装这个

安装完成,你会在你的开始菜单(Windows菜单)里看到这些

这是一系列工具

3.正式进入刷机
打开QFIL程序,将手机进入9008刷机模式
9008模式的话,就是手机的所有按键加上9008线上面那一个按钮一起按下
关不关机都可以,十秒这样电脑会有提示。

QFIL软件内

这是已进入9008连接电脑

查看是否进入9008模式

解压这个文件

将这个文件解压并放置桌面以便寻找

然后此时,QFIL里面会显示9008端口连接
按红色箭头点击即可

好了这里第三步已经做完了,已经成功刷入twrp

4.进入rec刷系统并进行Bootloader解锁(不解锁无法使用面具和root)
进入twrp刷入

这是twrp内

先刷底包,这是魔趣8.1,链接我等会放出来,官网现在只有9.0,9.0不能解锁

刷魔趣前必须刷底包

刷入包,刷入前记得双清

这是系统包

进入系统后,打开开发者选项,找到oem解锁,打开

打开即可

这时电脑连上手机,随便找一个adb工具。如果你找不到的话我 用刷机精灵,工具里面有

adb命令

这是adb工具

输入adb reboot bootloader,按下回车

按下回车

此时手机进入Bootloader
然后输入fastboot oem unlock
会提示你输入第二次
第二次则输入 fastboot oem unlock-go

我已经解过锁了,我就不演示了

按下回车即可,此时手机已经解锁完成

  • 5.正式刷系统和magisk
  • 这里说一下,魔趣的话相机不能用,刷入前必须刷底包
  • flyme更新速度不能保证但是bug不多
  • magisk要刷17.1最好,刷完后必须手动安装magisk这个app
  • 话不多说上连接
  • 坚果Pro(SmartisanU2Pro)odin玩机
  • 查看链接 密码:2333
  • 这里面有魔趣的8.1,flyme,魔趣9.0的话官网自行下载体验
  • flyme最新的我晚点放在评论区
  • ok拜拜,教程就到这
  • 想上个头条,嘻嘻

https://www.coolapk.com/feed/10007447?shareKey=ODE3ZjE1NzU2NWJkNWQ1YmY2MmU~&shareUid=1683554&shareFrom=com.coolapk.market_9.4.1

搞定刷封闭系统的root和框架,在弄完了qqbot和微信解锁自动登录破解9图等功能以后,我选择死亡。一个晚上过去了。

装上vnc https://yadominjinta.github.io/2018/07/30/GUI-on-termux.html

http://blackwolfsec.cc/2016/12/10/termux/渗透走起

装好tensorflow和opencv后的感觉是这样的


忘记说了,tensorflow和opencv是装在linux deploy里面的kali(附带许多渗透工具)

termux上装了很多nodejs的服务,比如http-server。

还有个难点,你的安卓设备的/dev/里面虽然有很多video 双摄的话可能有4个(一个前置一个后置的主要代号 两个后置 还有一个灯),这个是没办法被container里的termux或者虚拟机linuxdeploy所调用的(后面会提到的docker服务也是这个原因)就算装好了cheese 预装的libv4l2也是不行的,人看得到,机器看不到。

那就在安卓里面搞个通信app呗!

https://dq.tieba.com/p/5735551383

问题解决,很多调用us音频等问题都可以通过写Android程序socket或者ip通信解决,毕竟都在一个ip下。

那docker是怎么解决的呢?

记得我上次说的在树莓派里面运行docker,不过是用了REST api,并不足为奇,这次来点真的 https://yadominjinta.github.io/2018/08/22/QEMU-on-termux.html (its-xxxx的那个仓库里的qemu-system-x86改成 qemu-system-x86 _64)

弄个真的虚拟机alpine x86_64,装个docker,再ip通讯,弄到这里来,就搞定了。

虽然我的坚果pro在安装的时候很费力。不过没事,成功就好!

完结撒花,我可以天天看着我自己的书桌了。哈哈哈

关于网线代替总线的思考

事先说明,我还没有好好学过操作系统和CA。在写这篇文章的时候纯属小白一个

看过了很多产品和软件。

比如docker的 daemon。一个前端一个后面的计算资源。数据传输层完全是ip。

「docker daemon」的圖片搜尋結果
「docker daemon」的圖片搜尋結果

还有一个kvm over ip这么神奇的东西,在我房间里用一个安卓手机完美替代了。

「kvm over ip」的圖片搜尋結果

最后想说的是还剩下一个威联通QNAP的万兆网卡剪视频哈哈,很好玩。

「威联通 thunderbolt」的圖片搜尋結果
「thunderbult3 to 10G eth」的圖片搜尋結果

都是如此的牛逼。

那我就有以下设想

结果当然是被人对天对地。

收工。