假设链表节点的数据结构为:
typedef struct node
{
int data;
struct node* next;
}ListNode,*pListNode;
要求实现将一个单向链表逆置并检测如果链表成环返回FALSE,从参数返回新的链表头。
BOOL Reverse(pListNode& head);
这个问题看似简单,实际上太能发挥了。首先因为是单向链表,next不能简单的改变方向,要记录下即将被断开的节点,而即将被断开的节点它的next也是同样的问题。再者就是要如何检测有环。
单看逆置问题,通常的方法应该要用到3个指针:当前的p,下一个q,下一个的下一个head。 记下第三个指针,用第二个指向第一个,然后后移一步重复动作直到q没有下一个,再封口。
{
if (head != NULL && head->next != NULL)
{
pListNode p = head;
pListNode q = head->next;
p->next = NULL;
while (q->next != NULL)
{
head = q->next;
q->next = p;
p = q;
q = head;
}
head->next = p;
}
return; //此法未处理成环问题
}
单看检测成环问题,需要两个指针,一个向后一次移动一步,一个向后一次移动两步。假如他们能够相遇就是成环,他们任意一个走完链表都不成环。
{
if( head==NULL)return;
pListNode step1 = head;
pListNode step2 = head;
while(step1->next)
{
step1 = step1->next;
if(step2->next)step2 = step2->next;
else return TRUE; //不成环
if(step2->next)step2 = step2->next;
else return TRUE; //不成环
if(step1==step2)return FALSE;
}
return TRUE; //不成环
}
以上两种方法都单独解决了部分问题,能不能同时解决两个问题呢?
于是我想到了递归。且不说它的弊端,单就解决问题而言是可行的。
BOOL Reverse(pListNode& head) //不能处理成环检测
{
pListNode p = head;
if(p->next)
{
head = p->next;
Reverse(head);
p->next->next = p;
}
else
{
return FALSE;
}
return FALSE;
}
这个方法的问题是没法处理成环的问题。 在递归的背景下,解决成环的思路就想到如何标记一下处理过的node。于是我想到一个另类的做法。使用一个辅助结构体记录前后两个节点,把节点本身的next标为’1’,如果处理过程中发现next为1就是成环啦。
struct aid
{
struct node* left;
struct node* next;
};
BOOL Reverse(pListNode& head) //同时处理逆置和环检测
{
if(!head)return FALSE;
pListNode p = head;
if(p->next)
{
if(p->next==1)return TRUE; //成环
aid *a = new aid;
a->left = p;
a->right = p->next;
head = p->next;
p->next = 1;
if(Reverse(head)==TRUE)
{
delete a;
return TRUE;
}
a->right->next = a->left;
a->left->next = 0;
delete a;
}
else
{
return FALSE;
}
return FALSE;
}
注意,以上代码都没有实测!请以思路为主自行参考尝试实现。