【正确答案】
【答案解析】输入一个链表的头结点,反转该链表,并返回反转后链表的头结点。链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
}
为了正确地反转一个链表,需要调整指针的指向,而与指针操作相关代码总是容易出错的。先举个例子看一下具体的反转过程。例如,1、m、n是3个相邻的结点,假设经过若干步操作,已经把结点1之前的指针调整完毕,这些结点的m pNext指针都指向前面一个结点。现在遍历到结点m,当然需要调整结点的m_pNext指针,让它指向结点1,但需要注意的是,一旦调整了指针的指向,链表就断开了,因为已经没有指针指向结点n,没有办法再遍历到结点n了,因此为了避免链表断开,需要在调整m的m pNext之前要把n保存下来。接下来试着找到反转后链表的头结点,不难分析出反转后链表的头结点是原始链表的尾结点,即m pNext为空指针的结点。
具体的实现过程如下:
ListNode* ReverseIteratively(ListNode* pHead)
{
ListNode* pReversedHead=NULL;
ListNode* pNode=pHead;
ListNode* pPrev=NULL;
while(pNode!=NULL)
{
ListNode* pNext=pNode->m_pNext;
if(pNext==NULL)
pReversedHead=pNode;
pNode->m_pNext=pPrev;
pPrev=pNode;
pNode=pNext;
}
return pReversedHead;
}
如果本题简化为逆序输出单链表元素,那么递归将是最简单的方法。在递归函数之后输出当前元素,这样能确保输出第n个结点的元素语句永远在第n+1个递归函数之后执行,也就是说第n个元素永远在第n+1个元素之后输出,最终先输出最后一个元素,然后是倒数第2个、倒数第3个,直到输出第一个元素位置。具体实现过程如下:
void PrintReverseLink(ListNode* head)
{
if(head->Next != null)
{
PrintReverseLink(head->m_pNext);
printf("%d/n",head->m_Next->m_nKey);
}
}
本题不是要求逆序输出,而是需要把单链表逆序,所以在使用递归思想的时候,还需要处理递归后的逻辑问题。具体而言,是在反转当前结点之前先调用递归函数反转后续结点,不过该方法存在一个问题,就是反转后的最后一个结点会形成一个环,所以必须将函数返回的结点的m pNext域设置为NULL,同时考虑到链表反转时需要改变head指针,所以在进行参数传递时,需要传递引用。
具体的实现过程如下:
ListNode* Reverse(ListNode* p,ListNode* & head)
{
if(p==NULL‖p->m_pNext==NULL)
{
head=p;
return p
}
else
{
ListNode* temp=Reverse(p->m_pNext,head);
temp->m_pNext=p;
return temp;
}
}
需要注意的是,当单链表有环时,就会无法反转,因为如果单链表有环,则存在两个结点指向同一结点的情况。如果反转就变成一个结点指向两个了,而这对于单链表是不可能的。