1.题目
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第一个节点。例如,一个链表有6个节点,从头节点开始,他们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。链表的定义如下:
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
当然对于python而言,链表的定义如下:
class ListNode(object):
def __init__(self, value, next_node):
self.value = value
self.next = next_node
2. 解题思路
我看过这个题之后的思路就是,因为这个单链表,不是双向链表,因此要获得某一个节点,那么只能遍历,而且,一开始我们并不知道倒数第k个节点是哪一个,只有在遍历过一边链表之后,才能知道倒数第k个节点在哪儿。过程就是,第一次遍历链表得到链表的长度,计算出倒数第k个节点是顺数第几个节点(因为我们无法按照倒数来获得这个节点), 然后在再一次遍历这个链表,知道到达这个节点。时间复杂度O(n^2),空间复杂度S(1)。
上面的方法很简单,但是时间复杂度是O(n^2),面试官不会满意的,那么能不能降低时间复杂度呢?定义两个指针:第一个指针从链表头部开始遍历向前走k-1步,第二个指针保持不动;从第k步开始,第二个指针也开始从链表的头部开始遍历。两个指针的距离是k-1,当第一个指针到达链表的尾节点时,由于这两个节点的距离始终是k-1,这时第二个指针正好指向倒数第k个节点。
3. 代码实现
3.1 常规思路
def find_kth_to_tail(head, k):
count = 0 # 链表的长度
if k <= 0:
return
if head:
cur = head # 当前节点
while cur:
count += 1
cur = cur.next
# 倒数第k个节点是顺数的第n-k+1这个节点
index = count - k + 1
if index < 1:
return
cur = head
while index > 1:
cur = cur.next
index -= 1
return cur
3.2 更优思路
def find_kth_to_tail(head, k):
count = 1 # 第几个节点
cur = head # 指针1
k_node = head # 指针2
if k <= 0: # 负值条件,返回None
return
while cur:
cur = cur.next
if count > k: # 从顺数第k+1个节点开始,同时移动cur和k_node
k_node = k_node.next
count += 1
if count < k: # 若k>链表长度,应该返回None
return
return k_node
def find_kth_to_tail(head, k):
count = 1 # 第几个节点
cur = head
k_node = head
if k <= 0:
return
while count < k: # count最后的值为k
if cur:
cur = cur.next
count += 1
else:
return
# 此时cur指向顺数第k个节点,而k_node指向第一个节点(头节点),当cur指向尾节点时终止。
while cur.next:
cur = cur.next
k_node = k_node.next
return k_node
4.总结
当我们用一个指针遍历链表不能解决问题的时候,可以考虑使用两个指针来遍历链表,然后控制两个指针的速度或者某个指针先出发。
而对于列表而言,指针一般是从两端往中间移动。
5.参考文献
[1]剑指offer丛书