携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情
说实话,作为一个前端,在接触算法前是不知道链表是什么的。因为js里没有链表概念,只有数组。但是既然算法里有,我们就一起来探索下链表相关的算法吧。
链表是为了弥补数组的缺陷(必须开辟一段连续的内存空间)而产生的。
链表
- 优点
- 灵活分配内存空间
- 只要位置前面的节点已知,可以在O(1)时间内删除或添加元素
- 缺点
- 查询元素需要O(n)的时间
数组
- 优点
- 根据下标查询元素需要O(1)的时间
- 缺点
- 构建时必须分配一段连续的内存空间
- 在O(n)时间内删除或添加元素
链表的解题技巧:
- 利用快慢指针
- 构建一个虚拟的链表头
206. 反转链表
- 我们需要构造一个虚拟的链表头pre,然后当前到的节点为cur,那么当cur移动到null时,pre正好是循环的最后一个元素,即为翻转后的链表头
var reverseList = function(head) {
let pre = null
let cur = head
while (cur) {
let temp = cur.next
cur.next = pre
pre = cur
cur = temp
}
return pre
};
时间复杂度为O(n),空间复杂度为O(1)
92. 反转链表 II
- 先要找出左边界的上一个节点和右边界的下一个节点
- 然后切断中间需要反转的部分,反转中间链表
- 然后将反转后的链表与第一步找到的两个节点相连接
var reverseBetween = function(head, left, right) {
let dummy = new ListNode(-1)
dummy.next = head
let leftPre, rightNext
let pre = dummy
// 1.
for (let i = 0; i <= right; i++) {
if (i < left) {
leftPre = pre
}
if (i === right) {
rightNext = pre.next
break
}
pre = pre.next
}
// 2.
let cur = leftPre.next
leftPre.next = null
pre.next = null
reverseList(cur)
// 3.
leftPre.next = pre
cur.next = rightNext
return dummy.next
};
时间复杂度为O(n),空间复杂度为O(1)
- 除此之外另一种方案不需要调用之前的反转列表
- 整体思想为,在需反转区间内,每遍历到一个节点,就将它插入头部
var reverseBetween = function(head, left, right) {
let dummy = new ListNode(-1)
dummy.next = head
let pre = dummy
for (let i = 0; i < left - 1; i++) {
pre = pre.next
}
let cur = pre.next
for (let i = 0; i < right - left; i++) {
let next = cur.next
cur.next = next.next
next.next = pre.next
pre.next = next
}
return dummy.next
};
时间复杂度为O(n),空间复杂度为O(1)
其实链表题目,最主要是画图,会比较容易理出规则