链表中倒数第K个节点
思路分析:由于未说明该链表为双向链表,故为单链表。单链表的遍历顺序是从头到尾依次遍历。所有我们很容易想到,从头到尾依次遍历该链表的长度n,第二次遍历就可以找到链表中倒数第K个节点,即从头结点往后走n-k+1。该方法代码如下:
/*
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 *pHead=PlistHead;
ListNode *pBehind=nullptr;
for(int i=0;i<k;i++)
pHead=pHead->next;
pBehind=pListNode;
while(pHead->next != nullptr)
{
pHead=pHead->next;
pBehind=pBehind->next;
}
return pBehind;
}
};
如上述代码虽然写出来了,但是有三种测试情况可以让代码崩溃
(1)输入的pListHead为空指针,由于代码会试图访问空指针指向的内存,会造成代码的崩溃;
(2)输入的已pListHead为头节点的链表的节点总数少于k,由于pHead指针会首先向前走k-1步,仍然会由于空指针造成代码的崩溃;
(3)输入的参数k为0。由于k是个无符号的整数,在第一个for循环中,执行k-1次,即-1无号的表现形式为4294967295(0xFFFFFFFF),使得循环的次数太多,远远超过我们的预计也会造成程序的崩溃。
针对上述三个问题,我们需要需要分别处理:当输入的头指针pListHead时,此时整个链表为空,故返回nullptr;当输入的k为0时,由于节点是从1开始的,故也返回nullptr;如果链表的节点少于k,,那么在for循环的遍历中就会出现指向nullptr的next,故当进行0到k-1次循环时,应该在for循环中加入if判断。代码如下:
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead==nullptr || k==0) return nullptr;
ListNode *pHead=pListHead;
ListNode *pBehind=nullptr;
for(int i=0;i<k-1;i++)
{
if(pHead->next != nullptr)
pHead=pHead->next;
else
return nullptr;
}
pBehind=pListHead;
while(pHead->next != nullptr)
{
pHead=pHead->next;
pBehind=pBehind->next;
}
return pBehind;
}
};
故由上述例子可以发现,在实际的代码编写过程中,代码的鲁棒性是评价代码性能的一个重要指标。容错性是代码鲁棒性的一个重要体现。故,在代码编写之前,作为面试者的我们应该做全面的考虑所有可能的输入,确保在完成代码基本功能外,还考虑了边界条件,并做好了错误处理。
综上,只有全面考虑到规范性、完整性、鲁棒性三个方面的代码才是完整的代码。另外,要确保自己写出的代码程序不会轻易崩溃,平时在写代码的时候,我们最好养成防御性编程的习惯,在函数的入口处判断是否有效,并对各种无效的输入做好相应的处理,这一点至关重要也是面试官比较看重的一部分编程素养。