那麼多學技術的都可以成功,憑什麼我不行
鏈表中的倒數第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.一個指針遍歷鏈表無法解決問題時,可以考慮使用兩個指針來遍歷鏈表:兩個指針先後遍歷(即該題目)、或者兩個指針遍歷速度不同(如:求鏈表中的中間結點,可以令一個指針一次走一步,另一個指針一次走兩步來實現)