给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
链表的结构是这样的
1解法
链表的结构是这样的
let ListNode = {
val: 1,
next: {
val: 2,
next: {
val: 3,
next: null,
},
},
};
分情况处理
var removeNthFromEnd = function(head, n) {
let list = [head];
let node = head;
while(node.next){
node = node.next;
list.push(node);
}
if(list.length === 1){
//只有一个
return null;
}
let pos = list.length - 1 - n;
if(pos === -1 ){
//删除第一个
list[0].val = list[0].next.val;
list[0].next = list[0].next.next;
}
else if(n === 1){
//删除最后一个
list[pos].next = null;
}
else{
//中间接口
list[pos].next = list[pos].next.next;
}
return head;
};
找到要删除节点的上一个节点。
通过next属性将每一个节点分离出来=>放入数组
在分删除第一个、删除最后一个、删除中间的、只有1一个的情况 分别处理
不转化为数组
var removeNthFromEnd = function(head, n) {
const dummy = {
next: head,
};
let slowP = dummy;
let fastP = dummy.next;
while (n && fastP) {
n--;
fastP = fastP.next;
}
while (fastP) {
slowP = slowP.next;
fastP = fastP.next;
}
slowP.next = slowP.next.next;
return dummy.next;
};
上面解法的精髓在于,既然我们想要删除一个节点就要找到上一个节点,直接在前面加一个节点,这样就不用考虑一些特殊情况
第二点,以a=>b=>c=>d=>e为例,想要删除d这个元素,也就是倒数第2个,p向前走2步到b,此时距离p走到e还有3,此时q一起走3步 ,就找到了c,这是就可以通过c删除d了
可以通过一次while循环搞定
var removeNthFromEnd = function(head, n) {
if (!head) return null;
let tmp = { next: head };
let p = tmp,
q = tmp;
let count = 0;
while (q.next) {
if (count < n) {
count++;
q = q.next;
} else {
p = p.next;
q = q.next;
}
}
p.next = p.next.next;
return tmp.next;
};
2结语
最初的解法一开始并没有考虑到全部的情况
看来好的算法可以减少逻辑错误,尽量利用数据结构自身的特点去解决问题是优化算法的不二法则。