零:碎碎念
参与 ARTS 打卡的人其实很多,既有完成 100 天挑战的也有半途而废的。我也不知道自己是否能够完成本次挑战,如果能够做到那是更好的了。以下是我想到的能够帮助我完成这一目标的方法:
- 结果驱动:如果能够完成这一打卡挑战,首先我能够沉淀下来一系列文章,其次是能够养成关注技术、分享好文章的习惯,并且能够持续分享下去。最后能够完成挑战本身也是一件值得骄傲的事情,我还可以将其拓展到 200 天、1000 天的挑战,作为持续学习的一部分,可以一直持续下去。
- 调整优先级:起床碰电脑的第一件事就是打开本期的 ARTS 文档,然后逐步往里面填充素材,并从周一开始优化文档,在周五的上午发布本周周刊。
- 有深度的写作:首先每天开始动笔写 10 分钟,快速启动写作状态,习惯会让我接着写下一个个 10 分钟,这样快到周末的时候就有了比较丰富的素材。其次我的第一优先级还是质量,用篇幅合适的长文能够比较全面地介绍一个或者两个知识点,并且有一定的深度。
希望运用这三个方法能够让写作这个项目更好的运转下去。
一、Algorithm
每周至少做一个算法题。
计划每天做两道新题目,复习之前的题目,这样可以有更好的刷题效果。
本周算法是链表中的经典题目。
反转单链表 206. Reverse Linked List
分析:
本题比较直接,顺着链表不断得将当前节点的 next 指针改成指向前驱节点,这样逐步将整条链表都反转过来。
JavaScript 版:
const reverseList = function(head){
let current = head;
let next = null;
let prev = null;
while(current){
next = current.next;
current.next = prev;
prev = current;
current = next;
}
}
Python 版:
class Solution(object):
def reverseList(self, head):
prev, current = None, head
while current:
current.next, prev, current = prev, current, current.next
return prev
时间复杂度都是 O(n),因为需要遍历一次链表,空间复杂度是 O(1),因为只需要两个变量来保存
不同的是如果是 JavaScript,需要第三个变量才能实现交换两个变量的值,而 Python 由于有多重赋值特性可以实现一行代码完成三个赋值操作,所以可以只用声明两个变量。
两两交换链表中的节点 24. Swap Nodes in Pairs
分析:
题目目的是将链表中的节点每两个节点,两两交换。比反转链表复杂的点在于关注要反转的节点数量。
思考:
先考虑针对本题的解。先设一个起始节点,再顺着链表往前走两步,然后从头节点开始两两交换。
JavaScript:
var swapPairs = function(head){
let dumyNode = new ListNode(0);
dumyNode.next = head;
let current = dumyNode;
while(current.next && current.next.next){
let node1 = current.next;
let node2 = current.next.next;
current.next = node2;
node1.next = node2.next;
node2.next = node1
current = node1
}
return dumyNode.next
}
Python:
class Solution(object):
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
dummyNode = ListNode(0)
dummyNode.next = head
current = dummyNode
while current.next and current.next.next:
node1, node2 = current.next,current.next.next
current.next = node2
node1.next, node2.next = node2.next, node1
current = node1
return dummyNode.next
这两种方法的时间复杂度是 O(n),因为只需要遍历一次链表,空间复杂度是 O(1),使用了固定个数的变量。
每 K 个节点一组反转链表 25. Reverse Nodes in k-Group
分析:
跟前两题不同的一轮的循环节点数量是变量控制的。
可以先向前找后继节点,找到第 K 个节点。然后在将第 0 个到第 K 个节点之间的节点全部反转。
实现方式可以是借助堆栈 stack,或者是双指针法来寻找需要反转的子链
JavaScript 版本使用双指针
function reverse(head, tail) {
let prev = tail.next;
let p = head;
while (prev != tail) {
let nex = p.next;
p.next = prev;
prev = p;
p = nex;
}
return [tail, head];
}
function reverseKGroup(head, k) {
let dummy = new ListNode(0);
dummy.next = head;
let pre = dummy;
while (head != null) {
let tail = pre;
// quick jump to border
for (let i = 0; i < k; i++) {
tail = tail.next;
if (tail == null) {
return dummy.next;
}
}
let nex = tail.next;
[head, tail] = reverse(head, tail);
pre.next = head;
tail.next = nex;
pre = tail;
head = tail.next;
}
return dummy.next;
}
时间复杂度是 O(n),虽然看起来有两层嵌套的 while 循环,但是第一层并不是遍历每个节点,最终每个节点都只被访问了一次。
空间复杂度是 O(1),因为只使用了常数个变量
Python 版本使用 Stack:
class Solution(object):
def reverseKGroup(self, head, k):
stack = []
dummy = ListNode(0)
ptr = dummy
while True:
count = 0
temp = head
while temp and count < k:
stack.append(temp)
temp = temp.next
count += 1
if count != k:
ptr.next = head
break
while stack:
ptr.next = stack.pop()
ptr = ptr.next
ptr.next = temp
head = temp
return dummy.next
时间复杂度是 O(n),空间复杂度是 O(k),因为增加了一个堆栈用来存储每 k 个需要翻转的节点。
二、Review
每周阅读并点评至少一篇英文文章。
看别人写的文章并点评,本周文章是 Is React Having An Angular.js Moment?
最近开始用 Next.js 来写业余项目,发现 React 官方文档已经优先推荐使用 Next.js,而 Next.js 中组件会默认是从服务端渲染,除非是手动声明了 =use client=。
这带来了很多问题,其中最严重的是需要重新理解它的执行机制,包括参数获取、网络请求等都有所不同。
比如不再能够在 useEffect
中去获取数据。
很多 UI 库构建在标准的 hooks 之上,现在可能需要寻找跟 React 服务端组件兼容的版本。
那我们为什么需要服务端 React 呢,跟之前前后端未分离时的 JSP 模板有什么区别呢?
看起来并非一个新鲜的概念,可能区别在于可以由前端人员来掌控全过程…
我的看法是可以先去学习尝试一下,就像 React 函数式组件和 hooks 刚出的那会,有些人会觉得 Class 组件已经挺好用了为什么还要再造个轮子呢,不熟悉的人 useEeffect 使用不合适的第二个参数还容易引起死循环。
给一些时间让它更成熟吧。
三、Tip
每周学习并分享至少一个技术技巧。
问题:
解决两个使用 Taro 编写的单页应用工程之间相互跳转时应用状态会丢失的问题。
分析:
场景是有两个页面 A 和 B,分别属于两个 Taro 工程,两者在发布时会产出 H5 版本,分别部署在同一个域名下的两个子目录中。
我们先访问页面 A,在点击页面 A 中的按钮,跳转到页面 B。然后点击返回按钮,回退到页面 A。此时页面 A 会重新渲染,但是丢失了之前保留的状态。
可以绘制出时序图如下:
Taro 工程打包成 H5 之后,在同一个工程范围内跳转再回退是没有这个问题的,因为路由库将页面状态给保存起来了,由于是同一个工程内,实际上并没有发生刷新 html 的情况,回退后可以从已经保存的状态中重新渲染出 Dom。
但是在跨工程的场景中,情况就有所不同,首先这两个工程的页面在浏览器端内存是不共享的,从页面 B 回退到页面 A 相当于重新执行整个生命周期。
解决办法:
采用打开新标签的形式跳转到页面 B,要回退到 A 页面时直接将页面 B 关闭。
这种方法的成本比较小,体验上略有下降,在 PC 端还是移动端会有一个“跳出”的感觉。对体验要求非常高的情况下可以无压力的使用。
探索如何将页面状态包括 state 里的和 Dom 里的给保存起来,然后在回退时还原
这个方法探索成本比较高,但是体验上是最好的。
选择:对于这块业务可以先选择方案一,然后再研究方案二。
也就是在页面 A 跳转到页面 B 时,拼装好目标页面链接,然后执行 =window.open=,在回退时直接使用
window.close()
四、Share
每周分享至少一篇有观点和思考的技术文章。最好是自己写的文章。
本期分享的是 Tauri 从零到一:使用 Tauri 开发一个 ChatGPT 工具。
这篇文章主要尝试讲述 Tauri 从零开发一个应用应该要做什么,并写了一个简单的 ChatGPT 工具,当然这个工具比较简单,只是一个 小小的 Demo 。
后续还会尝试出一个 Tauri 系列,记录下自己开发工具过程中的心得。
本周的分享就到这里,祝大家生活愉快。