LeetCode第19题思悟——删除链表的倒数第N个节点(remove-nth-node-from-end-of-list)
知识点预告
- 双指针遍历技巧得以扩展:同向同速双指针遍历;
- 思考问题的模式:归纳【尾节点(倒数第一个节点)和倒数第N个节点之间的关系】和演绎【倒数第N个-正数第K个-链表长度】
题目要求
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
说明:给定的 n 保证是有效的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的思路
一般解法是首先求出链表的长度,然后计算出倒数第N个节点的正序数字,接着就是链表删除的常规操作;这种思路的形成过程为:
- 删除节点,首先得定位节点;
- 倒数第N个节点可通过计算得到正数情况下的编号;
- 要知道正数情况下的编号,需要链表长度;
于是操作顺序就变成了:首先获取链表长度,然后计算得出正数数字,接着删除;
这里的第2步是可以优化的;我们是需要定位节点,但是我们不一定需要通过计算来得到;
在链表中,我们很容易得到的是尾节点和头节点以及正数的第N个节点,这是因为尾节点的next指向null;那么我们如何通过尾节点得到倒数第N个节点呢?于是我们需要在该节点与尾节点之间建立联系:它们的正数顺序相差N-1(倒数第N减去倒数第1);
也即是说,当指针P指向尾节点时,指针S指向倒数第N个节点,S与P相差N-1;那么当S指向头节点的时候,P便指向正数第N个节点;于是第2种解法就产生了:
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode fastPointer=head;
ListNode slowPointer=head;
for(int i=0;i<n;i++){
fastPointer=fastPointer.next;
}
if(fastPointer==null){//删除的是头结点
return head.next;
}
while(fastPointer.next!=null){
fastPointer=fastPointer.next;
slowPointer=slowPointer.next;
}
slowPointer.next=slowPointer.next.next;
return head;
}
优秀解法
//解法A
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode tempNode = head;
int length = getLength(tempNode);
if (length==n) {
head = head.next;
return head;
}
for (int i = 1; i <= length; i++) {
if (i == length - n) {
tempNode.next = tempNode.next.next;
break;
}
tempNode = tempNode.next;
}
return head;
}
public int getLength(ListNode head) {
int result = 1;
while (head.next != null) {
head = head.next;
result++;
}
return result;
}
差异分析
我们来分析两种解法的效率:
自己的解法里,快指针遍历了一遍链表(移动L-1次);慢指针经过了L-N+1个节点(移动了L-N次);所以对于N,我们的解法共移动指针:L-1+L-N=2L-1-N次;无额外计算;
优秀解法里:同样分析,移动指针2L-1-N次,但是为了计算长度,是有自增操作的;从理论上分析,自己的解法应该操作次数更少;
知识点小结
- 双指针遍历技巧得以扩展:同向同速双指针遍历;
- 思考问题的模式:归纳【尾节点(倒数第一个节点)和倒数第N个节点之间的关系】和演绎【倒数第N个-正数第K个-链表长度】