剑指offer_面试题15_链表中倒数第k个节点(考虑问题要全面)

题目:输入一个链表,输出该链表中第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第一个节点。例如一个链表有6个节点,从头结点开始它们的值依次是1,2,3,4,5,6。这个链表的倒数第3个节点是值为4的结点。

本题收获:考虑问题要全面,保持代码的鲁棒性(健壮性)。

提高代码的鲁棒性的有效途径是进行防御性编程。

防御性编程 是 一种编程习惯,是指预见在什么地方可能会出现问题,并为这些可能出现的问题制定处理方式。

本题需要考虑的注意事项:

1、考虑传入的头指针为NULL;

2、考虑链表结点总数 少于 k;

3、考虑输入的 k 值小于等于0;

这三点一定要考虑,防止潜在的崩溃风险。

算法如下:

#include <iostream>
#include <cstdlib>

using namespace std;

typedef struct Node{
    int m_nValue;
    struct Node *m_pNext;
}ListNode;

/*创建空链表*/
ListNode * creat_List()
{
    ListNode *pHead;
    pHead = new ListNode;
    pHead->m_nValue = 0;    /**头结点中保存数据节点总数*/
    pHead->m_pNext = NULL;

    return pHead;
}

/*在链表尾部插入节点*/
ListNode * insert_Node_behind(ListNode *pHead,int pElem)
{
    if(NULL == pHead)
    {
        cout << "头结点为空" << endl;
        exit(1);
    }

    ListNode *temp, *current;
    current = pHead;
    while(NULL != current->m_pNext)
    {
        current = current->m_pNext;
    }
    temp = new ListNode;
    temp->m_nValue = pElem;
    temp->m_pNext = NULL;
    current->m_pNext = temp;

    pHead->m_nValue += 1;

    return pHead;
}

/*输出整个链表*/
void show_List(ListNode *pHead)
{
    if(NULL == pHead)
    {
        cout << "头结点为空" << endl;
        return;
    }
    ListNode *temp;
    temp = pHead->m_pNext;

    cout << "该 list 为:" ;
    while(NULL != temp)
    {
        cout << temp->m_nValue << ' ';
        temp = temp->m_pNext;
    }
    cout << endl;
}

/*找到第k个结点,并输出*/
void input_k_Node(ListNode *pHead,int k)
{
    if(NULL == pHead || k <= 0)     /**不要忘记头指针为空,和 k <= 0 的情况*/
    {
        cout << "头结点为空 或 k 小于等于0" << endl;
        return;
    }
    if(k > pHead->m_nValue)
    {
        cout << "倒数第k个节点已超出链表范围" << endl;
        return;
    }

    int num = pHead->m_nValue - k;   /*pHead->mValue里保存的链表结点总数,倒数第 k 个结点之前的结点数*/
    int i = 0;
    ListNode *temp = pHead;
    while (i <= num)
    {
        temp = temp->m_pNext;
        i++;
    }
    cout << "倒数第k个结点: " << temp->m_nValue << endl;
}

int main()
{
    int n_of_list = 6;
    int i = 1;
    ListNode *pHead;
    pHead = creat_List();
    while(n_of_list > 0)
    {
        insert_Node_behind(pHead,i++);
        n_of_list--;
    }
    show_List(pHead);
    input_k_Node(pHead,10);   /*改变这里的 k 值来测试*/
    return 0;
}
部分结果如下:

相关题目,可做思考:

1、求链表的中间结点。如果链表中结点总数为奇数,返回中间结点;若结点总数是偶数,返回中间结点的任意一个。

为了解决这个问题,我们可以定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。当走的快的指针走到链表的末尾时,走的慢的指针正好在链表的中间。

2、判断一个单向链表是否形成了环形结构。

和前面的问题一样,定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走得慢的指针,那么链表就是环形链表;如果走得快得指针走到了链表的末尾(即pNext->NULL)都没有追上第一个指针,那么链表就不是环形链表。

通过这两个相关题目的解决思路,可以看出当我们用一个指针遍历链表不能解决问题时,可以尝试用两个指针来遍历链表,一个走得快些,另一个慢些。

因此对于本题,寻找倒数第k个结点,也可以这样做:

定义两个指针,第一个指针从链表的头指针开始遍历向前走 k-1,第二个指针保持不动,从第 k 步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在 k-1,当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第 k 个结点。

/*点滴积累,我的一小步O(∩_∩)O~*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值