ARTS是什么?
Algorithm:每周至少做一个leetcode的算法题;
Review:阅读并点评至少一篇英文技术文章;
Tip:学习至少一个技术技巧;
Share:分享一篇有观点和思考的技术文章。
Algorithm
题目解析:
在二叉搜索树上删除一个节点,这道题目有一个隐含的条件,就是树上节点的值不重复。另外题目还要求时间复杂度需要保证 O(h) 这里的 h 表示的是二叉树的高度。
其实这个题目是分成两个步骤的,第一个是找到对应的节点,第二个是删除节点。因为是二叉搜索树,对于树上每个节点来说,其右子树的节点都要大于其左子树的节点,那么要找对应节点,我们可以从根节点开始,一路比较,大的话就去右边找,小的话就去左边找,这样每次我们都往下,可以保证时间复杂度是 O(h)。当我们找到了要删除的节点,在删除这一步就会有很多的细节,主要是因为我们需要调整余下的结构,以维持二叉搜索树的性质。
针对这个问题,我们可以分情况讨论:
5
/ \
3 6
/ \ \
2 4 7
/ \
1 8
情况 1:当删除的节点没有左右子树,比如上图的 4、8、1
这时直接删除即可,树依旧可以保持二叉搜索树的性质
情况 2:当删除的节点有左子树没有右子树,比如上图的 2
这时我们只需要将整个左子树移到当前位置即可
也就是将左子树的根节点放到删除节点的位置,其余不变
情况 3:当删除的节点没有左子树有右子树,比如上图的 6、7
这时我们只需要将整个右子树移到当前位置即可
也就是将右子树的根节点放到删除节点的位置,其余不变
情况 4:当删除的节点既有左子树又有右子树,比如上图的 5、3
这时就有两种方法供选择:
去到左子树中,找到值最大节点,将右子树全部移到这个节点下
去到右子树中,找到值最小节点,将左子树全部移到这个节点下
通过上面的讨论分析,我们有了大致的思路。在实现方面,我们可以借助递归来巧妙地达到删除对应节点的目的。
参考代码:
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) {
return null;
}
// 当前遍历到的节点大于要找的节点,去左边继续找
if (root.val > key) {
root.left = deleteNode(root.left, key);
}
// 当前遍历到的节点小于要找的节点,去右边继续找
else if (root.val < key) {
root.right = deleteNode(root.right, key);
}
// 找到要删除的节点,进行删除操作
else {
// 情况 1 & 2
if (root.right == null) {
return root.left;
}
// 情况 3
if (root.left == null) {
return root.right;
}
// 去到删除节点的右子树,找到值最小的节点
TreeNode rightSmallest = root.right;
while (rightSmallest.left != null) {
rightSmallest = rightSmallest.left;
}
// 将删除节点的左子树全部移到这个节点下
rightSmallest.left = root.left;
// 返回右子树的根节点,放到当前删除节点的位置
return root.right;
}
return root;
}
Review
The Powerful Differences Between Good and Great Programmers.
这篇文章主要是在讲好的程序员和伟大的程序员之间的区别。通常来说,好的程序员的评价标准比较直观,比如说能够写出简洁优雅的代码、有扎实的代码以及逻辑功底、对编程有热情、乐于帮助周围的同事等等。但是这些只能让你成为一个好的程序员,它并不能让你成为一个伟大的程序员。
想要成为一个伟大的程序员,除了上面这些,你还需要一些额外的特质。
-
有极强的学习能力
伟大的程序员会乐于去学习并了解一些新的技术。自学能力非常的强,懂得如何去问问题,问对的问题,问对的人。
-
能够很好地权衡实用与完美
计算机这个行业中,很多问题都是需要权衡,需要取舍的。没有绝对的好,也没有绝对的不好,一个技术用在某个特定的场景下,很好,但是用在一些其他的场景就不如其他的技术。伟大的程序员能够在实用和完美之间做好取舍与权衡,理性分析问题,做对的事情,不走极端。
-
有很好的直觉
往往看待一个问题,在有理性的分析之前,我们先会有一个直觉上的认知。这可以算是认识问题并解决问题的一个突破口。伟大的程序员通常在看到问题的蛛丝马迹后,就能够在脑袋里面对问题有个合理的猜测,并且心里也会针对不同的预期,准备相应的应对方案。这种直觉是超越编程之外的一种思维模式,习得这种能力不仅需要平时的积累,而且还跟人的一些特质有关。
-
有非常好的沟通能力
仅仅是沉浸到代码中,写出好的代码并不能够让其他人认识到你做的东西的优势。想让别人,或者说是一般的人理解并认同你,你还需要有好的沟通能力。伟大的程序员往往言简意赅,能够用简洁而直白的语言让周围的人懂得一些不容易解释的东西。
你可以看到,上面列出来的这几项其实并不会与特定的技术,或者是成就挂钩。没错,一个程序员是否伟大,并不能仅仅通过他的薪水、职位高低、甚至是编码能力来决定,更看重的往往是态度方面的问题,如果一个程序员能够积极主动去学习,提高自己,刻意地去通过自身或者外界的反馈来改变自己的认知,尝试着用自己学到的东西努力去解决一些问题,带动着周围的人也一起积极地看待问题,那么你也可以收获伟大。
Tip
Git Learn Branching 是一个学习 Git 的很好的平台,这里通过互动界面让你对 Git 当中的一些常见用法有更深入的认识。虽然说用 Git 已经很久了,但是跟着走了一遍后,还是觉得自己知道的太少,这里做个学习记录吧:
- 整个 Git 仓库就是一个树结构,Branch 其实就是指针,当你在 Git 中创建了一个 branch 其实就是创建了一个指向特定 Commit 的指针,最新产生的 Commit 就是这个树的一个叶子节点。认识到这一点很重要,因为这可以说是整个 Git 的基础
- Rebase:
git rebase作用是拷贝一些 commit 去到另外一个地方,相比于git merge,它能够更好地维持 branch 的整洁,常见用法如下:# 将 branch2 的不同部分移到 branch1 上 # branch1 的位置不发生改变,branch2 的位置发生改变 # 如果当前位置已经在 branch2 上了,那么 branch2 可以省略 >$ git rebase branch1 branch2 # 将当前的 branch rebase 到 origin/master 分支上(表示 remote 的 master 的分支) >$ git rebase origin/master # -i 可以让你任意选择之前 commits,也可以置换这些 commits 的前后顺序 # 这里的 master 也可以用特定的 commit 来代替 >$ git rebase -i master - detaching head:经常在 Git 的提示中经常看到这个名词,它表示的意思是 HEAD 当前在一个特定的 Commit 上,而不在某个 branch 上。
- relative refs:两个便捷的符号 ^ 和 ~,使用它们可以让命令更简洁
- ^ : 表示某个 commit 的父 commit
- ~ : 也是表示某个 commit 的父 commit,但是后面可以接数量
比如下面是一个仓库的 Git 记录: C0 | C1 | C2 | C3 <- (master, HEAD) # 将 master 移动到 C0 >$ git branch -f master HEAD~3 - cherry-pick:复制一系列的 commits 到你当前(HEAD)的位置下,这可以算是 Git 当中非常灵活的一个指令,有些时候可以替代 rebase
>$ git check-pick <c1> <c2> ... - tag & describe: 这其实是两个指令,tag 就是针对某个 commit 做个标记,因为 commit hash 太难记了,为了突出某个 commit,我们可以给它起个醒目的名字。但是注意,我们并不能直接通过 checkout tag 来移动 HEAD。describe 是将离当前位置最近的 tag 信息给打印出来,打印出来的格式是
<tag>_<numCommits>_g<hash>,我们可以通过 describe 指令拿到最近的 tag 的 hash,然后就可以 checkout 过去了。 - remote branch:格式是
<remote name>/<branch name>通常这里的<remote name>会是origin,但是注意的是,在本地我们不能够直接 checkout remote branch,不然会 detaching HEAD - fetch:
- 关于
git fetch的三点重要认知:- 从 remote 下载 local 缺失但是 remote 存在的 commits
- 会更新 local 上 remote branch 的位置
- 不会改变 local (branch)的状态
- fetch 的高级用法
git fetch <remote name> <remote branch name>下载 remote 上特定的 branch 上的信息git fetch <source>:<destination>下载 remote 上特定的 branch 上的信息到本地特定 branch 上去
- 关于
- push: 同
fetch相类似,git push也有一些高级用法:git push <remote name> <remote branch name>push 到 remote 特定的 branch 上去git push <source>:<destination>push 本地特定的 branch 到 remote 特定的 branch 上去
- diverged history:每当你 push commit 之前,你需要确保 local 和 remote 保持同步,这里有四个常见操作,其中 1、3 等同(rebase),2、4 等同(merge):
git fetch+git rebase origin/master+git pushgit fetch+git merge origin/master+git pushgit pull --rebase+git pushgit pull+git push
- remote tracking:举个例子,local 上
origin/masterbranch 是用来表示 remote 上的 master。我们也可以创建其他的 branch 来和origin/master关联起来,这样fetch、pull、push操作可让相关联的 branch 自动移动。这里注意,我们前面也说过,fetch操作是不会改变 local 的状态的。有两个指令可以帮我们关联 branch:git checkout -b branch <remote name>/<branch name>git branch -u <remote name>/<branch name> <local branch>
当然 Git 远不止这些,还有 git stash 之类的指令,但是这些是最常用到的一些指令
Share
继续 Clean code 之旅