203.移除链表元素
题目描述:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
初始思路,分情况讨论 删除头结点、中间结点
初始的思路是直接在原链表上进行修改:
用node变量遍历链表,如果node.next 的 val 等于要删除的目标值,则 node.next = node.next.next
同时,对需要删除的节点是头结点时进行处理和判断。
var removeElements = function(head, val) {
while(head!=null && head.val === val){
head = head.next
}
if(head === null ) return null
let pre = head
let cur = head.next
while(cur){
if(cur.val === val){
pre.next = cur.next
}else{
pre = pre.next
}
cur = cur.next
}
return head
};
虚拟头节点
创建一个新的节点,指向链表的头结点,称为虚拟头结点。
虚拟头结点仅作为辅助使用,无需对头结点和非头结点进行单独处理,使用上一个解法中删除普通结点的操作,删除链表中的所有目标结点。
var removeElements = function(head, val) {
let dummyHead = new ListNode(0,head)
let cur = dummyHead
while(cur.next){
if(cur.next.val === val){
cur.next = cur.next.next
continue
}
cur = cur.next
}
return dummyHead.next
};
707. 设计链表
题意:
在链表类中实现这些功能:
- get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
- addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
- addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
- addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
- deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点
示例:
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3
初始思路:
在遇到这个问题的时候,LeetCode核心模式已经给了我几个方法的定义和规定的入类型和返回类型。 对JavaScript语言来说,这些类型都在 MyLinkedList 原型链上。 一开始我没有明确定义好节点和链表,直接在函数体中书写,不明晰也不流畅。
在看过部分解析之后:
首先发现 MyLinkedList 并没有定义其基础组成部分—— 链表节点,所以首先定义链表节点类:
function LinkNode (val,next){
this.val = val===undefined ? -1 : val;
this.next = next || null
}
在初始化thi.sval时,我一开始直接写的 ``this.val = val || -1; 但实际上需要注意的是,题目规定 val 可以等于 0, 所以我修改为 只有当 val 是undefined 的时候,对 val 修改初值为 -1
接下来定义链表类:
var MyLinkedList = function() {
this.size = 0
this.head = null
this.tail = null
};
在第一次书写方法函数的时候,我发现很多时候都需要获取链表的长度,以及其头结点、尾结点。
同时,在 addAtIndex get deleteAtIndex 中,都需要用到 获取某个索引所对应的节点 这个方法,为了使代码简洁可读,定义一个新的 MyLinkedList 方法:
MyLinkedList.prototype.getNode = function(index){
if(index<0 || index >= this.size) return null
let cur = new LinkNode(50,this.head)
while(index >= 0){
cur = cur.next
index --
}
return cur
}
这个方法接收一个数值参数作为inedx,返回当前链表的第 index 个节点。
注意,在本题中,index从0开始计算
由此,可以通过调用这个方法,更加便捷的完成其他几个函数:
MyLinkedList.prototype.get = function(index) {
if(index < 0 || index >= this.size){
return -1
}
return this.getNode(index).val
};
MyLinkedList.prototype.addAtHead = function(val) {
let newNode = new LinkNode(val,this.head)
this.head = newNode
this.size ++
if(!this.tail){
this.tail = newNode
}
};
MyLinkedList.prototype.addAtTail = function(val) {
let newNode = new LinkNode(val,null)
this.size ++
if(this.tail){
this.tail.next = newNode
this.tail = newNode
return
}
this.tail = newNode
this.head = newNode
};
MyLinkedList.prototype.addAtIndex = function(index, val) {
if(index<=0){
this.addAtHead(val)
return
}else if(index>this.size){
return
}else if(index === this.size){
this.addAtTail(val)
return
}else{
let node = this.getNode(index-1)
node.next = new LinkNode(val,node.next)
this.size ++
}
};
MyLinkedList.prototype.deleteAtIndex = function(index) {
if(index >=0 && index<this.size){
if(index === 0){
this.head = this.head.next
if(index === this.size-1){
this.tail = this.head
}
this.size --
return
}
let node = this.getNode(index-1)
node.next = node.next.next
if(index === this.size -1){
this.tail = node
}
this.size--
}else{
return
}
};
同时,我们需要注意,在增删链表节点的时候,对链表的属性:头结点、尾结点、长度进行及时的更新。
206. 反转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
初始解题思路:
维护3个变量,分别是当前节点 node, (原链表中)当前节点的下一个节点next,当前节点的上一个节点pre
使 node.next 指向 pre, 并实时更新node, pre 和 next,从头到尾进行遍历和翻转。
var reverseList = function(head) {
let node = head
let pre = null
let next
while(node){
next = node.next
node.next = pre
pre = node
node = next
}
return pre
};
递归的解法
其基本思想和手段与我最开始的解题思路相同,但是是以递归的方法实现的。算是个 Trick
// 从前向后递归
var reverse = function(pre,head){
if(!head) return pre
let next = head.next
head.next = pre
pre = head
return reverse(pre,next)
}
var reverseList = function(head) {
return reverse(null,head)
};
var reverse = function(head){
//到最后一个节点返回
if(!head || !head.next) return head
const pre = reverse(head.next)
console.log(pre.next,head.next)
head.next = pre.next
pre.next = head
return head
}
//从后向前递归,类似于翻折、对折
var reverseList = function(head) {
let cur = head
while(cur && cur.next){
cur = cur.next
}
//当前cur是链表的最后一个元素
reverse(head)
return cur
};