# Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

0%

## Leetcode 236 二叉树的最近公共祖先(Lowest Common Ancestor of a Binary Tree) 题解分析

发表于 2021-05-23 分类于 Java ， leetcode ， Lowest Common Ancestor of a Binary Tree 阅读次数： Disqus：

### 题目介绍

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes `p` and `q` as the lowest node in `T` that has both `p` and `q` as descendants (where we allow a node to be a descendant of itself).”

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为：“对于有根树 `T` 的两个节点 `p`、`q`，最近公共祖先表示为一个节点 `x`，满足 `x` 是 `p`、`q` 的祖先且 `x` 的深度尽可能大（一个节点也可以是它自己的祖先）。”

## 代码

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
            // 如果当前节点就是 p 或者是 q 的时候，就直接返回了
            // 当没找到，即 root == null 的时候也会返回 null，这是个重要的点
            if (root == null || root == p || root == q) return root;
            // 在左子树中找 p 和 q
            TreeNode left = lowestCommonAncestor(root.left, p, q);
            // 在右子树中找 p 和 q
            TreeNode right = lowestCommonAncestor(root.right, p, q);
            // 当左边是 null 就直接返回右子树，但是这里不表示右边不是 null，所以这个顺序是不影响的
            // 考虑一种情况，如果一个节点的左右子树都是 null，那么其实对于这个节点来说首先两个子树分别调用
            // lowestCommonAncestor会在开头就返回 null，那么就是上面 left 跟 right 都是 null，然后走下面的判断的时候
            // 其实第一个 if 就返回了 null，如此递归返回就能达到当子树中没有找到 p 或者 q 的时候只返回 null
            if (left == null) {
                return right;
            } else if (right == null) {
                return left;
            } else {
                return root;
            }
    //        if (right == null) {
    //            return left;
    //        } else if (left == null) {
    //            return right;
    //        } else {
    //            return root;
    //        }
    //        return left == null ? right : right == null ? left : root;
        }

## rust学习笔记-所有权三之切片

发表于 2021-05-16 分类于 语言 ， Rust 阅读次数： Disqus：

除了引用，Rust 还有另外一种不持有所有权的数据类型：切片(slice)。切片允许我们引用集合中某一段连续的元素序列，而不是整个集合。
例如代码

    fn main() {
        let mut s = String::from("hello world");

        let word = first_word(&s);

        s.clear();

        // 这时候虽然 word 还是 5，但是 s 已经被清除了，所以就没存在的意义
    }

这里其实我们就需要关注 s 的存在性，代码的逻辑合理性就需要额外去维护，此时我们就可以用切片

    let s = String::from("hello world")

    let hello = &s[0..5];
    let world = &s[6..11];

其实跟 Python 的list 之类的语法有点类似，当然里面还有些语法糖，比如可以直接用省略后面的数字表示直接引用到结尾

    let hello = &s[0..];

甚至再进一步

    let hello = &s[..];

使用了切片之后

    fn first_word(s: &String) -> &str {
        let bytes = s.as_bytes();

        for (i, &item) in bytes.iter().enumerate() {
            if item == b' ' {
                return &s[0..i];
            }
        }

        &s[..]
    }
    fn main() {
        let mut s = String::from("hello world");

        let word = first_word(&s);

        s.clear(); // error!

        println!("the first word is: {}", word);
    }

那再执行 main 函数的时候就会抛错，因为 word 还是个切片，需要保证 s 的有效性，并且其实我们可以将函数申明成

    fn first_word(s: &str) -> &str {

这样就既能处理&String 的情况，就是当成完整字符串的切片，也能处理普通的切片。
其他类型的切片

    let a = [1, 2, 3, 4, 5];
    let slice = &a[1..3];

简单记录下，具体可以去看看这本书

## 聊聊给亲戚朋友的老电脑重装系统那些事儿

发表于 2021-05-09 分类于 生活 阅读次数： Disqus：

前面这个五一回去之前，LD 姐姐跟我说电脑很卡了，想让我重装系统，问了下 LD 可能是那个 09 年买的笔记本，想想有点害怕[捂脸]，前年有一次好像让我帮忙装了她同事的一个三星的笔记本，本着一些系统洁癖，所以就从开始找纯净版的 win7 家庭版，因为之前那些本基本都自带 win7 的家庭版，而且把激活码就贴在机器下面，然后从三星官网去找官方驱动，还好这个机型的驱动还在，先做了系统镜像，其实感觉这种情况需要两个 U 盘，一个 U 盘装系统作为安装启动盘，一个放驱动，毕竟不是专业装系统的，然后因为官方驱动需要一个个下载一个个安装，然后驱动文件下载的地方还没标明是 32 位还是 64 位的，结果还被 LD 姐姐催着，一直问好没好，略尴尬，索性还是找个一键安装的

这次甚至更夸张，上次还让带回去，我准备好了系统镜像啥的，第二天装，这次直接带了两个老旧笔记本过来说让当天就装好，感觉有点像被当修电脑的使，又说这些电脑其实都不用了的，都是为了她们当医生的要每年看会课，然后只能用电脑浏览器看，结果都在用 360 浏览器，真的是万恶的 360，其实以前对 360 没啥坏印象，毕竟以前也经常用，只是对于这些老电脑，360 全家桶真的就是装了就废了，2G 的内存，开机就开着 360 安全卫士，360 杀毒，有一个还装了腾讯电脑管家，然后腾讯视频跟爱奇艺也开机启动了，然后还打开 360 浏览器看课，就算再好的系统也吃不消这么用，重装了系统，还是这么装这些东西，也是分分钟变卡，可惜他们都没啥这类概念。

对于他们要看的课，更搞笑的是，明明在页面上注明了说要使用 IE 浏览器，结果他们都在用 360 浏览器看，但是这个也不能完全怪他们，因为实在是现在的 IE 啥的也有开始不兼容 flash 的配置，需要开启兼容配置，但是只要开启了之后就可以直接用 IE 看，比 360 靠谱很多， 资源占用也比较少，360 估计是基于 chromium 加了很多内置的插件，本身 chromium 也是内存大户，但是说这些其实他们也不懂，总觉得找我免费装下系统能撑一段时间，反正对我来说也应该很简单（他们觉得），实际上开始工作以后，我自己想装个双系统都是上淘宝买别人的服务装的，台式机更是几年没动过系统了，因为要重装一大堆软件，数据备份啥的，还有驱动什么的，分区格式，那些驱动精灵啥的也都是越来越坑，一装就给你带一堆垃圾软件。

感悟是，总觉得学计算机的就应该会装系统，会修电脑，之前亲戚还拿着一个完全开不起来的笔记本让我来修，这真的是，我说可以找官方维修的，结果我说我搞不定，她直接觉得是修不好了，直接电脑都懒得拿回去了，后面又一次反复解释了才明白，另外就是 360 全家桶，别说老电脑了，新机器都不太吃得消。

## Leetcode 48 旋转图像(Rotate Image) 题解分析

发表于 2021-05-01 更新于 2022-06-11 分类于 Java ， leetcode ， Rotate Image 阅读次数： Disqus：

### 题目介绍

You are given an n x n 2D `matrix` representing an image, rotate the image by 90 degrees (clockwise).

You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.
如图，这道题以前做过，其实一看有点蒙，好像规则很容易描述，但是代码很难写，因为要类似于贪吃蛇那样，后来想着应该会有一些特殊的技巧，比如翻转等

### 代码

直接上码

    public void rotate(int[][] matrix) {
            // 这里真的傻了，长宽应该是一致的，所以取一次就够了
            int lengthX = matrix[0].length;
            int lengthY = matrix.length;
            int temp;
            System.out.println(lengthY - (lengthY % 2) / 2);
            // 这里除错了，应该是减掉余数再除 2
    //        for (int i = 0; i < lengthY - (lengthY % 2) / 2; i++) {
            /**
             * 1 2 3             7 8 9
             * 4 5 6     =>      4 5 6     先沿着 4 5 6 上下交换
             * 7 8 9             1 2 3
             */
            for (int i = 0; i < (lengthY - (lengthY % 2)) / 2; i++) {
                for (int j = 0; j < lengthX; j++) {
                    temp = matrix[i][j];
                    matrix[i][j] = matrix[lengthY-i-1][j];
                    matrix[lengthY-i-1][j] = temp;
                }
            }

            /**
             * 7 8 9               7 4 1
             * 4 5 6     =>        8 5 2   这里再沿着 7 5 3 这条对角线交换
             * 1 2 3               9 6 3
             */
            for (int i = 0; i < lengthX; i++) {
                for (int j = 0; j <= i; j++) {
                    if (i == j) {
                        continue;
                    }
                    temp = matrix[i][j];
                    matrix[i][j] = matrix[j][i];
                    matrix[j][i] = temp;
                }
            }
        }

还没到可以直接归纳题目类型的水平，主要是几年前做过，可能有那么点模糊的记忆，当然应该也有直接转的方法

## rust学习笔记-所有权一

发表于 2021-04-18 更新于 2022-06-11 分类于 语言 ， Rust 阅读次数： Disqus：

最近在看 《rust 权威指南》，还是难度比较大的，它里面的一些概念跟之前的用过的都有比较大的差别
比起有 gc 的虚拟机语言，跟像 C 和 C++这种主动释放内存的，rust 有他的独特点，主要是有三条

- Rust中的每一个值都有一个对应的变量作为它的所有者。
- 在同一时间内，值有且只有一个所有者。
- 当所有者离开自己的作用域时，它持有的值就会被释放掉。
  这里有两个重点：
- s 在进入作用域后才变得有效
- 它会保持自己的有效性直到自己离开作用域为止

然后看个案例

    let x = 5;
    let y = x;

这个其实有两种，一般可以认为比较多实现的会使用 copy on write 之类的，先让两个都指向同一个快 5 的存储，在发生变更后开始正式拷贝，但是涉及到内存处理的便利性，对于这类简单类型，可以直接拷贝
但是对于非基础类型

    let s1 = String::from("hello");
    let s2 = s1;

    println!("{}, world!", s1);

有可能认为有两种内存分布可能
先看下 string 的内存结构
第一种可能是
第二种是
我们来尝试编译下
发现有这个错误，其实在 rust 中`let y = x`这个行为的实质是移动，在赋值给 y 之后 x 就无效了
这样子就不会造成脱离作用域时，对同一块内存区域的二次释放，如果需要复制，可以使用 clone 方法

    let s1 = String::from("hello");
    let s2 = s1.clone();

    println!("s1 = {}, s2 = {}", s1, s2);

这里其实会有点疑惑，为什么前面的`x, y` 的行为跟 `s1, s2` 的不一样，其实主要是基本类型和 string 这类的不定大小的类型的内存分配方式不同，`x, y`这类整型可以直接确定大小，可以直接在栈上分配，而像 string 和其他的变体结构体，其大小都是不能在编译时确定，所以需要在堆上进行分配