一、链表中倒数第K个节点
方法一:假设整个链表有n个结点,那么倒数第K个结点就是从头结点开始的第n-k+1个结点。因此通过遍历链表获得结点数n,然后从头结点开始往后走n-k+1步就可以了。但是这种方法需要遍历两次链表。
//方法一
struct ListNode
{
int val;
struct ListNode *next;
ListNode(int x) :val(x), next(NULL) {}
};
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
{
if(pListHead==NULL||k<=0)
return NULL;
ListNode* pAhead=pListHead;
ListNode* pBhead=pListHead;
int n=0;
while(pAhead->next!=NULL)
{
pAhead=pAhead->next;
n++;
}
for(unsigned int i=0;i<n-k+1;i++)
{
if(pBhead->next!=NULL)
pBhead=pBhead->next;
else
return NULL;
}
return pBhead;
}
方法二:定义两个指针都指向链表头,第一个指针从链表头的头指针开始遍历向前走k-1,第二指针保存不动;从第k步开始,第二个指针也开始从链表的头指针开始遍历。由于两个节点的距离保存在k-1,当第一个指针达到链表的尾结点时,第一个结点正好是倒数第k个节点。如下图所示:
在有6个节点的链表中找倒数第3个节点的过程(a)P1先走两步
(b)P1走了两步后,P2指向链表头(c)两指针一同向前走,当P1指向链尾时,P2指向即为所求位置
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
{
if(pListHead==NULL||k<=0)
return NULL;
ListNode* pAhead=pListHead;
ListNode* pBhead=pListHead;
for(unsigned int i=0;i<k-1;i++)
{
if(pAhead->next!=NULL)
pAhead=pAhead->next;
else
return NULL;
}
while(pAhead->next!=NULL)
{
pAhead=pAhead->next;
pBhead=pBhead->next;
}
return pBhead;
}
提高代码的鲁棒性,需要考虑的问题:a、输入pListhead为空,输入参数K小于等于0;b、输入参数K大于链表结点数n
二、反转链表
题目:定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点,链表结点
struct ListNode
{
int val;
struct ListNode *next;
ListNode(int x) :val(x), next(NULL) {}
};
为完成反转,序定义3个指针,分别指向当前遍历到的结点、它的前一个结点及后一个结点(以防止链表断开)。图解如下:
ListNode* ReverseList(ListNode* pHead)
{
ListNode* pReversedHead=NULL;
ListNode* pPrev=NULL;
ListNode* pNode=pHead;
ListNode* pNext=NULL;
while(pNode!=NULL)
{
pNext=pNode->next;
if(pNext==NULL)
{
pReversedHead=pNode;
}
pNode->next=pPrev;
pPrev=pNode;
pNode=pNext;
}
return pReversedHead;
}