这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战 | 创作学习持续成长,夺宝闯关赢大奖 - 掘金 (juejin.cn)
题目链接
- 两数相加II leetcode-cn.com/problems/ad…
- 重排链表 leetcode-cn.com/problems/re…
- 环路检测 leetcode-cn.com/problems/li…
- 设计链表 leetcode-cn.com/problems/de…
- 删除链表的节点 leetcode-cn.com/problems/sh…
题解及分析
两数相加II
给你两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字0之外,这两个数字都不会以零开头。
示例1:
输入:l1 = [7,2,4,3], l2 = [5,6,4]
输出:[7,8,0,7]
思路:如果把题目示例1的看成7243+564,那么结果得出7807就能理解题意了。实际上这道题是要求利用链表做加法。
- 利用两个栈顺序保存两个链表的值
- 不知道栈的话可以看看这个递归与栈拷问专题一 - 掘金 (juejin.cn)
- 将栈中的值弹出,两两相加,空位用0替代
- 注意加法进位
var addTwoNumbers = function(l1, l2) {
const stack1 = []
const stack2 = []
while (l1 || l2) {//两链表入栈
if (l1) {
stack1.push(l1.val);
l1 = l1.next;
}
if (l2) {
stack2.push(l2.val);
l2 = l2.next;
}
}
let newNodeList = null
let prefix = 0
while(stack1.length || stack2.length || prefix !== 0) {
const newNode1 = stack1.length ? stack1.pop() : 0
const newNode2 = stack2.length ? stack2.pop() : 0
const newVal = newNode1 + newNode2 + prefix
const curNode = new ListNode((newVal % 10))
curNode.next = newNodeList
newNodeList = curNode
prefix = parseInt(newVal / 10)
}
return newNodeList
}
重排链表
给定一个单链表 L 的头节点head,单链表L表示为: L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为: L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
思路一:栈
- 找出中间节点。(快慢指针)
- 将中间节点之后的值用栈存起来(反正就是找一个翻转链表的办法)
- 同时遍历中间节点之前的链表和栈,组装一个新链表
var reorderList = function(head) {
if(!head || !head.next || !head.next.next) return head
let fast = head
let slow = head
while(fast && fast.next) {
slow = slow.next
fast = fast.next.next
}
let left = slow.next
slow.next = null
const stack = []
while(left) {
stack.push(left)
left = left.next
}
while(head && stack.length) {
const next = head.next
const newNode = stack.pop()
newNode.next = next
head.next = newNode
head = next
}
return head
}
思路二:数组
- 将链表从每个节点处断开并存入数组
- 利用快慢指针,重新组转链表
var reorderList = function(head) {
let arr = []
let tmp = null
while (head)
tmp = head.next,
head.next = null,
arr.push(head),
head = tmp
var i = -1, j = arr.length
while (++i < --j)
arr[i].next = arr[j],
j !== i + 1 && (arr[j].next = arr[i + 1])
return arr[0]
}
环路检测
给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。若环不存在,请返回null。
如果链表中有某个节点,可以通过连续跟踪next指针再次到达,则链表中存在环。为了表示给定链表中的环,我们使用整数pos来表示链表尾连接到链表中的位置(索引从0开始)。如果pos是-1,则在该链表中没有环。注意:pos不作为参数进行传递,仅仅是为了标识链表的实际情况。
这道题目和链表折磨专题一 - 掘金 (juejin.cn)中第二题一样,这里不做其他解释
设计链表
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val和next。val是当前节点的值,next是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性prev以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
get(index):获取链表中第index个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为val的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为val的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第index个节点之前添加值为val的节点。如果index等于链表的长度,则该节点将附加到链表的末尾。如果index大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引index有效,则删除链表中的第index个节点。
这道题实际上参考了链表折磨专题四 - 掘金 (juejin.cn)第一题,但是条件从获取/添加/删除中间节点变成了获取/添加/删除指定index节点,其他功能实际上一样
单链表解法
- 获取指定index节点,直接遍历链表找到对应的下标即可
- 添加指定index节点,需要注意我们应该遍历到第index-1个节点,然后执行操作
- 要注意链表为空的情景
- 删除指定index节点和添加注意事项一致
- 顺带一提,双链表其实也一样,但是需要注意prev指针的指向
var MyLinkedList = function() {
this.head = null
};
MyLinkedList.prototype.get = function(index) {
let count = 0
let cur = this.head
while(cur) {
if(count === index) {
return cur.val
}
cur = cur.next
count++
}
return -1
};
MyLinkedList.prototype.addAtHead = function(val) {
this.head = new ListNode(val, this.head)
};
MyLinkedList.prototype.addAtTail = function(val) {
let cur = this.head
if(!cur) {
this.head = new ListNode(val)
return
}
while(cur && cur.next) {
cur = cur.next
}
cur.next = new ListNode(val)
};
MyLinkedList.prototype.addAtIndex = function(index, val) {
if(!index) return this.addAtHead(val)
let count = 0
let cur = this.head
while(cur) {
if(count === index - 1) {
let newNode = new ListNode(val, cur.next)
cur.next = newNode
break
}
cur = cur.next
count++
}
};
MyLinkedList.prototype.deleteAtIndex = function(index) {
if(!index) return this.head = this.head.next
let count = 0
let cur = this.head
while(cur && cur.next) {
if(count === index - 1) {
cur.next = cur.next.next || null
break
}
cur = cur.next
count++
}
};
删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。 返回删除后的链表的头节点。
思路一:单指针
- 需要注意的是,修改指针的条件是当指针指向符合条件的节点的前一个节点时
var deleteNode = function(head, val) {
if (head.val == val) return head.next
let prev = head
while(prev) {
if(prev.next.val === val) {
const next = prev.next
prev.next = next.next
return head
}
prev = prev.next
}
return head
}
思路二:双指针
(没什么能解释的)
var deleteNode = function(head, val) {
if (head.val == val) return head.next
let prev = head
let cur = head.next
while(cur) {
if(cur.val === val) {
const next = cur.next
prev.next = next
return head
}
prev = prev.next
cur = cur.next
}
return head
}
思路三:递归
- 利用递归的特性解决
- 刚好只要判断head即可
var deleteNode = function(head, val) {
if (head.val == val) return head.next
head.next = deleteNode(head.next, val)
return head
}
题目总结
链表的题目,说穿了只有增删改查。但如何找到需要修改的位置才是题目真正考察的核心。