《剑指offer》面试题22:链表中导数第K个节点(C++实现)

《剑指offer》面试题22:链表中导数第K个节点(C++实现)

题目描述

输入一个链表,输出该链表中倒数第k个结点。

解决思路

最简单的方法是让我们遍历这个链表两次。
第一次遍历计算出我们的链表有n个节点。
第二次遍历时,当走到第n-k+1个节点时,输出对应节点的值即可。

但是,我们能不能用一次遍历就完成呢?
当然可以!

我们可以定义两个指针。
我们不妨这么去解决。
一、第一个指针先从第一个节点处出发,走k-1步。直到走到第K个节点。
二、走到第K个节点时,第二指针指向第一个节点。此时这两个指针间隔为k-1。然后两个节点开始一同遍历。
三、当最早出发的节点走到了链表的末端第n个节点,这个时候你会发现第二个指针正好走到了n-(k-1)=n-k+1,即倒数第K个节点。

这里我们把第一个指针定义为r,第二个指针定义为l。因为第二个指针先出发,所以总是在第一个指针的左边。
我们可以反过来验证。
最后的结果一定是左指针指向倒数第K 个节点,右指针指向第n个节点。反过去推挥发现最初的状态真如上述那样。

如果看到这里还不太明白,建议你可以用笔在草稿纸上画一画就清楚了。

双指针遍历输出

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) 
    {   
            ListNode* pRight = pListHead;
            ListNode* pLeft = pListHead;
        //一:从第一个节点走K-1步走到第K个节点
        for(int i=1;i<=k-1;i++)
        {   
               pRight = pRight->next;
        }
        
        //二:从现在开始双指针遍历,循环条件为当右指针到最后一个节点时,循环中止
        while(pRight->next!=nullptr)
        {
            pRight = pRight->next;
            pLeft = pLeft->next;
        }
        return pLeft;
    }
};

好了,现在我们就大功告成了!
运行然后提交一下吧!

不通过
您的代码已保存
段错误:您的程序发生段错误,可能是数组越界,堆栈溢出(比	如,递归调用层数太多)等情况引起
case通过率为0.00%

???
怎么回事,说好的恭喜您已通过呢?
具体的原因我们到下面分析

补充增加代码鲁棒性

包括博主在内,在得到用两个指针遍历的思路来解决这道题时,心中窃喜,结果发现提交结果不通过,百思不得其解,通过查询才知道,自己写的代码鲁棒性不够好。
根据《剑指offer》上的提示,有以下三种方法可以使得这段代码崩溃:
(1)输入的pListHead为空指针。由于代码会尝试访问空指针指向的内存,从而造成程序崩溃。
(2)输入的以pListHead为头节点的链表总数少于K。由于在for循环中会在链表上向前走K-1步,仍然会由于空指针而造成程序崩溃。
(3)输入的参数K为0。由于K是一个无符号整数,那么for循环中K-1得到的将不是-1,儿时294967295(无符号的OxFFFFFFFF)。因此,for循环执行的次数将远远超过我们的预计,同时也会造成程序崩溃。
那么我们对于以上三种问题,需要在程序中体现出来:

//运行通过
//运行时间:3ms
//占用内存:480K
/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) 
    {    if(pListHead==nullptr||k<=0)//如果遇到头节点为空指针和k的输入不规范(<=0)时,直接返回空指针就可以了(针对解决上述的(1)和(3))
            return nullptr;
            ListNode* pRight = pListHead;
            ListNode* pLeft = pListHead;
        //一:从第一个节点走K-1步走到第K个节点
        for(int i=1;i<=k-1;i++)
        {   if(pRight->next!=nullptr)//当节点总数n小于我们所给出的K时,我们需要保证不会走到空指针上(针对解决上述的(2))
               pRight = pRight->next;
         else return nullptr;
        }
        
        //二:从现在开始双指针遍历,循环条件为当右指针到最后一个节点时,循环中止
        while(pRight->next!=nullptr)
        {
            pRight = pRight->next;
            pLeft = pLeft->next;
        }
        return pLeft;
    }
};

小结

在解决问题的时候,我们一定要充分的考虑代码的鲁棒性,否则很有可能与offer失之交臂。

参考文献

《剑指offer》

牛课网刷题链接link.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值