您的位置:首页 > 其它

链表中倒数第k个结点

2016-05-08 10:41 190 查看
题目:输入一个链表,输出该链表中倒数第k个结点。为了符合大多数人都习惯,本题从1开始计数,即链表的尾结点是倒数第1个结点。例如一个链表有6个结点,从头结点开始它们的值依次是1, 2, 3, 4, 5, 6。这个链表的倒数第3个结点是值为4的结点。链表结点定义如下:

typedef struct ListNode
{
int val;
struct ListNode *p_next;
}NODE, *PNODE;


为了找到倒数第k个结点,很自然的就可以想到先找到链表的尾结点,然后从后往前找1第k个结点。然而从链表结点的结构情况来看,它是一个单链表,所以这种方法直接就out了。

所以我们马上就又想到了,假设链表结点总数为n,那么倒数第k个结点不就是第 n-k+1 个结点喽!既然如此,我们只需先把整个链表遍历一遍,统计出总的结点个数,然后在从头开始遍历,找出第 n-k+1 个结点就ok了。

上边这个方法效率只能说是还行,但因为它需要把链表遍历两次,这样不是做了很多无用功嘛。所以我们要想着优化一下。怎么才能在只遍历一次的情况下,就找到倒数第k个结点呢?

like this:

定义两个指针,第一个指针从链表的头结点开始向后走k-1步,第二个指针不动;从第k步开始,第二个指针也开始从链表的头指针处向后遍历。由于这两个指针的距离始终保持着k-1的距离,所以当第一个指针走到了链表的尾结点时,第二个指针自然的就指向了倒数第k个结点。如图所示,这是一个链表,有两个指针指向它的头结点:



这里再假设我们要查找倒数第3个结点,也就是3这个结点。那么接下来就是让right指针向后移动 3-1 步,变成这样:



right现在指向了结点3,接下来,left指针和right指针同时开始右移,直到right指向尾结点,如下:



这时Right指向了尾结点,同时的,left也已经指向了倒数第k个结点。

另外,还有几点要注意的是,如果传进来的链表是一个空指针怎么办?传进来的k小于0怎么办?k大于总结点数怎么办?这些情况都需要进行处理。理清了总体思路,接下来就可以开始写代码了:

PNODE find_Kth_to_tail(PNODE head, int k)
{
PNODE left = NULL;
PNODE right = NULL;
int i = 0;

assert(head);

if (k <= 0)
return NULL;

left = right = head;

for (i = 0; (i < k-1) && (NULL != right->p_next); i++)
{
right = right->p_next;;
}
if (i != k-1)
return NULL;
while (NULL != right->p_next)
{
left = left->p_next;
right = right->p_next;
}

return left;
}
void distroy(PNODE head)
{
PNODE tmp = NULL;

while (NULL != head)
{
tmp = head;
head = head->p_next;
free(tmp);
}
}


写完代码一定不要忘记测试哟!

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef struct ListNode { int val; struct ListNode *p_next; }NODE, *PNODE;




int main()
{
PNODE p1 = (PNODE)malloc(sizeof(NODE));
PNODE p2 = (PNODE)malloc(sizeof(NODE));
PNODE p3 = (PNODE)malloc(sizeof(NODE));
PNODE p4 = (PNODE)malloc(sizeof(NODE));
PNODE ret = NULL;

p1->val = 1;
p1->p_next = p2;
p2->val = 2;
p2->p_next = p3;
p3->val = 3;
p3->p_next = p4;
p4->val = 4;
p4->p_next = NULL;

ret = find_Kth_to_tail(p1, 5);
if (ret)
printf("%d\n", ret->val);
else
printf("没找到\n");

distroy(p1);
return 0;
}


还有一些同类型的题目,比如返回链表的中间结点,判断链表是不是形成了环形结构等,都可以用这种两个指针的思路来实现。

当我们用一个指针遍历链表不能解决问题的时候,可以尝试用两个指针来遍历链表。可以让其中一个指针遍历的速度快些,比如一次让这个指针走两步,或者干脆让它先走若干步。(《剑指offer》)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: