题目分析:我们可以将算法实现分为三个部分:找到中间位置的结点,将后半部分的新链表逆置,将逆置的链表插入前半部分链表中。但这之间也会产生许多问题,比如如果链表结点是奇数个呢? 如何在空间复杂度是O(1)用代码实现呢?
解题设计:第一部分找到中间位置结点,正常思维都是遍历两遍链表一次找尾节点长度一次找中间节点,但秉持着能优则优的原则,我们选择双指针法一次遍历链表即可,命名两个指针ppre与pcur,我们让pcur一次走两步,而ppre一次走一步,这样当pcur遍历完链表之后ppre此时在中间结点。有人会担心奇数个结点怎么办,在经过画图论证后我发现其实并不会影响结果。
void middle_find(LinkList L,LinkList &L2)
{
LinkList ppre,pscr;
L2=(LinkList) malloc(sizeof (LNode));
ppre=L->next;
pscr=L->next;
while(pscr)
{
pscr=pscr->next;
if(pscr==NULL)
{
break;
}
pscr=pscr->next;
if(pscr==NULL)
{
break;
}
ppre=ppre->next;
}
L2->next=ppre->next;
ppre->next=NULL;
}
在实现代码的时候,我发现了一些小问题并改正了过来,比如pcur一定要移动一步就判断一次是否为null,L的尾部要切断。
第二部分将L2逆置。首先我们要判断什么情况下才需要逆置,链表为空和一个结点肯定不需要(考研不可能这样但最好考虑全面)。接下来使用三指针法,命名三个指针r,s,t,使其分别对应链表里待处理的三个指针。接下来让第二个指针指向第一个指针,再将三个指针均向后移一位,重复上述操作,这样就会得到一个“反向的”链表,此时L2还指向第一个结点(现在是最后一个结点),t为空时结束循环,但要记得此时最后一个结点还未逆置,所以要在循环外加上s->next=r。
void reverse_list(LinkList L)
{
LinkList r,s,t;
r=L->next;
if(r==NULL)
{
return;
}
s=r->next;
if(s==NULL)
{
return;
}
t=s->next;
while(t)
{
s->next=r;
r=s;
s=t;
t=t->next;
}
s->next=r;
L->next->next=NULL;
L->next=s;
}
第三部分L与L2合并。考虑到空间复杂度是O(1),我们不能申请新的空间,所以要直接处理现存的链表。我们需要 3 个指针(pcur,p,q),合并后的新链表我们让 pcur 指针始终指向新链表尾部,初始化为 pcur=L->next,使用 p 指针始终指向链表 L 待放 入的结点,初始化值为 p=L->next,q 指针始终指向链表 L2 待放入的结点,初始化值为 q=L2->next。因为链表 L 的第一个结点不动,所以 p=p->next。 开启循环 while(p!=NULL&&q!=NULL),首先将 pcur->next=q,然后 q=q->next 和 pcur=pcur->next,接着 pcur->next=p,然后 p=p->next 和 pcur=pcur->next,直到循环结 束。循环结束后,有可能 L 还剩余一个结点,也可能 L2 剩余一个结点,但是只会有一个剩 余的有结点,因此我们判断 p 不为 NULL,把 p 放入,如果 q 不为 NULL,把 q 放入即可。
void merge(LinkList L,LinkList L2)
{
LinkList ppre,p,q;
ppre=L->next;
p=ppre->next;
q=L2->next;
while(p!=NULL&&q!=NULL)
{
ppre->next=q;
ppre=ppre->next;
q=q->next;
ppre->next=p;
p=p->next;
ppre=ppre->next;
}
if(p!=NULL)
{
ppre->next=p;
}
if(q!=NULL)
{
ppre->next=q;
}
}
接下来是测试结果(尾插法新建链表):
运行成功!
总结:一下就打出完整代码是不可能的,一定要先出思路,写代码的过程中要考虑各种各样的情况防止bug。