题目:输入一个链表,输出该链表中第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~*/