给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
解答:
遍历链表节点,使用栈存储节点。出栈n-2个节点,然后将倒数第n+1个节点的next指针连接到第n-1个节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public static ListNode removeNthFromEnd(ListNode head, int n) {
// 记录头指针
ListNode root = head;
// 将链表反向记录到栈中
Stack<ListNode> stack = new Stack<>();
while (head != null) {
stack.push(head);
head = head.next;
}
// 将栈中的不用的节点出栈(n-1)
for (int i = 0; i < n - 2; i++) {
stack.pop();
}
// 删除最后一个节点
if (n == 1) {
stack.pop();
try {
stack.pop().next = null;
} catch (Exception e) {
return null;
}
return root;
}
ListNode p = stack.pop();
stack.pop();
try {
// 如果删除第一个节点,它前面没有节点,所以返回第二个节点
ListNode q = stack.pop();
q.next = p;
return root;
} catch (Exception e) {
return root.next;
}
}
}
特殊情况处理:
// 删除最后一个节点
if (n == 1) {
stack.pop();
try {
// 如果只含有一个节点,删除最后一个节点后,链表为空
stack.pop().next = null;
} catch (Exception e) {
return null;
}
return root;
}
ListNode p = stack.pop();
stack.pop();
try {
// 如果删除第一个节点,它前面没有节点,所以返回第二个节点
ListNode q = stack.pop();
q.next = p;
return root;
} catch (Exception e) {
return root.next;
}
使用两个指针,第一个指针遍历n个节点后,第二个指针开始遍历,即两个指针相差n个节点。当第一个节点遍历完时,第二个节点到达倒数第n+1个节点,然后将倒数第n+1个节点的next指针连接到第n-1个节点。
public static ListNode removeNthFromEnd(ListNode head, int n) {
ListNode headHead = new ListNode(0);
headHead.next = head;
ListNode fast = headHead;
ListNode slow = headHead;
for (int i = 0; i < n; i++) {
fast = fast.next;
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
System.out.println("huahu");
slow.next = slow.next.next;
return headHead.next;
}
技巧:
- 对于边界问题可以自行添加边界进行简化
ListNode headHead = new ListNode(0);
headHead.next = head;
n = num.length;
m = num[0].length;
int[][] a = new int[n+1][m+1];
- 尽量使用
obj.next != null
判断是否遍历完成