求单链表中倒数第k个结点

发火题目:实现一个算法,找出单链表中倒数第k个结点。

解法:

       下面以递归和非递归的方式解决这个问题。一般来说,递归解决更简洁,但是效率比较差。

例如,就这个问题来说,递归解法的代码量大概只有迭代解法的一半,但要占用O(n)空间,其中n为链表结点个数。

注意:

        在下面的解法中,k定义如下:传入k=1将返回最后一个结点,k=2返回倒数第二个结点,依此类推。当然,也可以将k定义为k=0返回最后一个结点。

惊恐解法一:链表长度已知

        若链表长队已知,那么第k个结点就是第(length-k)个结点。直接迭代访问链表就能找到这个节点的。

惊恐解法二:递归

       这个算法会递归访问整个链表,当抵达链表末端时,该方法会回传一个置为0的计时器。之后的每次调用都会将这个计时器加1,,当计时器等于k时,表示我们访问的是链表倒数第k个元素。

         实现代码比较简单,但前提是我们要有办法通过栈”回传“一个整数值。可惜,我们无法用一般的返回语句回传一个结点和一个计数器,那该怎么办?

 方法A:不返回该元素

             一种方法是对这个问题略作调整,只打印倒数第k个结点的值。然后,直接通过返回值传回计数器值。

int nthToLast(LinkedListNode head, int k)
{
if (head == NULL)
return 0;
int i = nthToLast(head->next, k) + 1;
if (i == k)
{
cout << head->data;
}
return i;
}

 方法B:使用c++

       第二种方法是使用c++,并通过引用传值。这样就可以返回结点值,而且能够通过传递指针更新计数器

node* nthToLast(node* head, int k, int& i)
{
if (head == NULL)
return NULL;
node *nd = nthToLast(head->next, k, i);
i = i + 1;
if (i == k)
{
return head;
}
return nd;
}

 方法C:创建包裹类

     这里难点在于我们无法同时返回计数器和索引值,如果用一个简单的类(或一个单元素数组)包裹计数器值,就可以模仿按引用传递。

class IntWrapper{
public:
int value = 0;
};
LinkedListNode nthToLastR2(LinkedListNode head, int k, IntWrapper i)
{
if (head == NULL)
return NULL;
LinkedListNode node = nthToLastR2(head->next, k, i);
i.value = i.value + 1;
if (i.value == k)
{
return head;
}
return node;
}

因为有递归调用,这些递归解法都需要占用O(n)空间。

        还有不少其它解法,可以将计数器放在静态变量中,或者,可以创建一个类,存放结点和计数器,并返回这个类的实例。不论选用哪种解法,我们都要设法更新结点和计数器,并在每层递归调用的栈都能访问到。

惊恐解法三:迭代法

      一种效率更高但不太直观的解法是以迭代方式实现,我们可以使用两个指针p1和p2,并将它们指向链表中相距k个结点的两个结点,具体做法是先将p1和p2指向链表首结点,然后将p2向前移动k个结点。之后,我们以相同的速度移动这两个指针,p2会在移动LENGTH-K步后抵达链表尾结点。此时,p1会指向第LENGTH-K个结点,或者说倒数第k个结点。

LinkedListNode nthToLast(LinkedListNode head, int k)
{
if (k <= 0)
return NULL;
LinkedListNode p1 = head;
LinkedListNode p2 = head;
//P2向前移动k个结点
for (int i = 0; i < k - 1; i++)
{
if (p2 == NULL)
return NULL;
p2 = p2->next;
}
if (p2 == NULL)
return NULL;
/*现在以同样的速度移动p1和p2,当p2抵达链表末尾时,p1刚好指向倒数第k个结点*/
while (p2->next != NULL)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
}

这个算法的时间复杂度为O(N),空间复杂度为O(1).





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值