从单向无环链表中删除倒数第n个节点
题目
给定一个链表: 1->2->3->4->5, 和 n = 2。删除倒数第“n”个节点后,链表为:1->2->3->5
说明: 给定的 n 保证是有效的。
要求: 只允许对链表进行一次遍历。
分析
题目中的链表是单向链表,这样要删除倒数第n元素,则需要找到倒数n+1个元素,否则将无法删除倒数第n个节点。
-
如果倒数第n+1个节点存在,删除的核心代码为:
parent.next = parent.next.next
; -
如果倒数第n+1个节点不存在(即倒数第n个节点是链首),则将链首的子节点数据
复制
到链首即可完成删除链首的操作。
思路
参考判断链中是否有环中" 快慢指针 "的思路,这里用两个步长均为1的指针p1,p2,指针p2比指针p1 先行 n+1 步。p2指针指向尾部时,p1指针刚好指向倒数第n+1个节点。
实现代码如下
public static LinkNode remove(LinkNode head, int n) {
LinkNode p1 = head;
LinkNode p2 = head;
int step = 0;
//p2先行n+1步,然后将p2移动到链尾的子节点(即null)。
while (p2 != null) {
p2 = p2.next;
//当p2优先p1走了n+1步,则p1开始移动
if (++step > n + 1) {
p1 = p1.next;
}
}
//step从0开始记步数,且从head节点开始,表示走了step步,由此计算链表长度为step+1,
//又因为step跳出循环时,指针指向链表外的null,即多走了1步,
//故,链表的长度等于step+1-1=step。减去的那一步为多走(多计数)的一步。
if (step < n) {
System.out.println("链表长度为:"+step+",不够\t" + n);
return null;
}
LinkNode temp;
//要移除的节点为链表的头节点
if (head.equals(p1)) {
//将head数据复制到temp中;
temp = new LinkNode(head.data);
//将head子节点的数据复制到head中,完成head数据的移除。
head.data = head.next.data;
head.next = head.next.next;
}else {
//要移除存在父节点
temp = p1.next;
p1.next = p1.next.next;
}
return temp;
}
public static void main(String[] args) {
/**
* 构件链表 1->2->3->4->5
*/
LinkNode head = new LinkNode(1);
LinkNode h2 = new LinkNode(2);
LinkNode h3 = new LinkNode(3);
LinkNode h4 = new LinkNode(4);
LinkNode h5 = new LinkNode(5);
head.next = h2;
h2.next = h3;
h3.next = h4;
h4.next = h5;
int n = 5;//测试6,5,2,1情况下输出结果
LinkNode remove = remove(head, n);
if (remove != null) {
System.out.println("被移除的倒数第" + n + "个元素是:" + remove.data);
} else {
System.out.println("被移除的倒数第" + n + "个元素不存在。");
}
System.out.println("移除操作完成后,链表如下:");
while (head.next != null) {
System.out.print(head.data + "->");
head = head.next;
}
}
结果:
- 删除倒数第6个节点,结果如下
链表长度为:5,不够:6
被移除的倒数第6个元素不存在。
移除操作完成后,链表如下:1->2->3->4->5
- 删除倒数第5个节点,结果如下
被移除的倒数第5个元素是:1
移除操作完成后,链表如下:2->3->4->5
- 删除倒数第2个节点,结果如下
被移除的倒数第2个元素是:4
移除操作完成后,链表如下:1->2->3->5