题目


代码-"快慢指针"
遍历两趟的就说一下思路吧(因为我没写代码)
第一遍先数一下整个链表有多少个节点,第二遍再找到要删除的节点的前一个位置,然后改变指针指向即可。
只遍历一遍的方法我想到的是"快慢指针"(应该算吧?)。要删除的是倒数第n个节点,那我可以先让一个fast指针走n步,之后slow和fast一起移动,一直到fast走到头了为null了,此时slow就是指向倒数第n个节点了。但这里有个问题,在链表A->B->C中,如果我想删除B,那我应该做的是让A的next指向C,因此我们要找的不是要删除的节点本身,而是要找该节点的前一个节点。所以在slow和fast同步移动之前,还需要让fast多走一步。
javascript
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
let fast = head, slow = head
while (n--) {
fast = fast.next
}
// fast为null,说明删除的是头节点
if (fast === null) return head.next
fast = fast.next
while (fast !== null) {
fast = fast.next
slow = slow.next
}
slow.next = slow.next.next
return head
};
代码-哑节点
d老师倾情传授。
上面的代码里有对fast的显式判断,但这一步我们可以做优化,让整个代码看起来更简洁。
思路其实还是一样的。前面我们说了要找的是删除节点的前一个节点,因此实际要求fast先走n+1步,这就涉及边界的问题,如果n和链表长度一样,那fast走到null的时候还要往后走一步就会报错。而此时我们在head前面加上一个哑节点就完美解决这个问题。
javascript
var removeNthFromEnd = function(head, n) {
// 创建哑节点,指向原链表头
let dummy = new ListNode(0, head);
let fast = dummy;
let slow = dummy;
// 快指针先走 n+1 步
for (let i = 0; i <= n; i++) {
fast = fast.next; // fast 不会提前为 null
}
// 同步移动,直到 fast 到达末尾
while (fast !== null) {
fast = fast.next;
slow = slow.next;
}
// 此时 slow 指向待删除节点的前驱,删除 slow.next
slow.next = slow.next.next;
// 返回新链表头(哑节点的下一个)
return dummy.next;
};
两种方法的时间复杂度都是O(L)(L为链表长度)