那么多学技术的都可以成功,凭什么我不行
链表中的倒数第K个节点
本文基于《剑指Offer》 使用Java代码实现
题目
输入一个链表,输出该链表中倒数第k个结点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第1个结点。例如一个链表有6个结点,从头结点开始它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个结点是值为4的结点。
思路
第一直觉是先从头开始遍历,计算链表个数n,然后重新遍历,第n-k+1个结点即为所需要的结点。但是需要遍历2次。后面采用了栈进行实现该方法,空间复杂度比较大。书中的方法则是:设置两个指针,第一个指针先遍历k-1步;从第k步开始,第二个指针指向头结点,两个结点同时往后遍历,当第一个指针到达最后一个结点时,第二个指针指向的正好是倒数第k个结点。
代码实现
利用栈实现
/**
* @ClassName: KthNodeFromEnd
* @description: 面试题22: 链表中的倒数第K个节点
* @author: XZQ
* @create: 2020/4/27 9:27
**/
//方法1:利用栈
public ListNode FindKthToTail1(ListNode head, int k) {
if (head == null || k <= 0)//如果头节点为空指针或者k<=0
return null;
Stack<ListNode> stack = new Stack<>();
ListNode curNode = head;
int listLength = 0;
while (curNode != null) {
listLength++;
stack.push(curNode);
curNode = curNode.next;
}
if (k > listLength) {
return null;
}
for (int i = 0; i < k; i++) {
curNode = stack.pop();
}
return curNode;
}
利用两个指针
//方法2:利用两个指针,
// 一个指针先向前走k-1步,第二个开始遍历,当第一个指针走到链表尾部时,第二个指针恰好在倒数第k个节点
public ListNode FindKthToTail2(ListNode head, int k) {
if (head == null || k <= 0)//如果头节点为空指针或者k<=0
return null;
ListNode node1 = head;
ListNode node2 = null;
/*node1先向前走k-1步*/
for (int i = 0; i < k - 1; i++) {
if (node1.next != null) {
node1 = node1.next;
} else {
return null;
}
}
node2 = head;
while (node1.next != null) {
node1 = node1.next;
node2 = node2.next;
}
return node2;
}
测试
public static void main(String[] args) {
ListNode p6 = new ListNode(6, null);
ListNode p5 = new ListNode(5, p6);
ListNode p4 = new ListNode(4, p5);
ListNode p3 = new ListNode(3, p4);
ListNode p2 = new ListNode(2, p3);
ListNode p1 = new ListNode(1, p2);
ListNode node = new KthNodeFromEnd().FindKthToTail2(p1, 4);
if ((node != null)) {
System.out.println(node.val);
} else {
System.out.println("test failed!");
}
}
输出
4
收获
1.注意代码的鲁棒性,开始思考前都需要注意特殊输入测试;
2.一个指针遍历链表无法解决问题时,可以考虑使用两个指针来遍历链表:两个指针先后遍历(即该题目)、或者两个指针遍历速度不同(如:求链表中的中间结点,可以令一个指针一次走一步,另一个指针一次走两步来实现)