題目
劍指 Offer 22. 連結串列中倒數第k個節點
思路1(棧)
- 既然要倒數第k個節點,那我們直接把所有節點放到棧(先進後出)裡面,然後pop彈出k個元素就可以了
程式碼
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
LinkedList<ListNode> stack = new LinkedList<>();
// 把整個連結串列入棧
while (head != null) {
stack.push(head);
head = head.next;
}
// 先將前n-1個元素出棧
for (int i = 1; i < k; i++) {
stack.pop();
}
// 此時位於棧頂的就是倒數第k個元素啦
return stack.pop();
}
}1
複雜度分析
- 時間複雜度:\(O(N)\)
- 空間複雜度:\(O(N)\),使用棧的額外空間要花掉\(O(N)\)
思路2(遞迴+回溯)
- 和使用棧的思想差不多。。
- 先遞迴到連結串列末尾,然後依次回溯,回溯k次的時候就得到我們需要的節點了
程式碼
class Solution {
ListNode res;
// 代表當前處於倒數第幾個節點
int cur = 0;
public ListNode getKthFromEnd(ListNode head, int k) {
dfs(head, k);
return res;
}
public void dfs(ListNode node, int k) {
// 先遞迴到末尾
if (node == null) {
return;
}
dfs(node.next, k);
// 倒數第cur個節點
cur++;
// 等於k代表找到了
if (cur == k) {
res = node;
}
}
}
複雜度分析
- 時間複雜度:\(O(N)\)
- 空間複雜度:\(O(N)\),需要遞迴整個連結串列
思路3(雙指標)
- 定義
fast
快指標和slow
慢指標兩個指標:讓快指標fast
先走k
步,然後再讓兩個指標同時移動,等快指標fast
走道連結串列末尾時候(為null
),此時慢指標slow
所指向的節點就是倒數第k
個節點
程式碼
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
// 讓快指標先走k步,和慢指標相差k個距離
for (int i = k; i > 0; i--) {
fast = fast.next;
}
// 此時讓慢指標和快指標同時走,知道快指標到達連結串列末尾為null時,慢指標就在倒數第k個位置上了
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
// 直接返回慢指標即為答案
return slow;
}
}
複雜度分析
- 時間複雜度:\(O(N)\)
- 空間複雜度:\(O(1)\)
思路4(差值法)
- 因為要求的是倒數第k個節點,那如果我們知道了連結串列的總長度,那麼這個節點在連結串列中的順序位置也就知道了
- 先計算出連結串列的總長度,則可以得出:
倒數第k個節點
等於 連結串列總長度-k+1
。但是在題目中我們已經指向了連結串列的第一個節點,所以只需要再走n-k
個節點即可到達倒數第k個節點
程式碼
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
int length = 0;
ListNode p = head;
// 先獲取連結串列的總長度
while (p != null) {
p = p.next;
length++;
}
// 其實倒數第k個的位置就相當於 length-k
// 然後我們從連結串列頭部開始遍歷 length-k 個節點,此時所在的節點位置就是答案了
k = length - k;
for (int i = k; i > 0; i--) {
head = head.next;
}
return head;
}
}
複雜度分析
- 時間複雜度:\(O(N)\)
- 空間複雜度:\(O(1)\)