上一期我给大家讲解和介绍了单链表的头插法和尾插法,今天我将为大家带来综合性的代码考研真题实战,依次作为一个机会来锻炼自己的代码能力。此次我们将实战考研2019年的408。
首先我们先分析题干理解题干,重点词带头结点的单链表,并且空间复杂度为O(1),分析最初的L与L~的区别。下面我们开始正式实战:
首先设计思路:(1)空间复杂度为O(1)的意思为我们设计的程序不会在过多的去申请空间,如果在原来链表的基础上重新申请了空间则空间复杂度不为O(1)。
(2)观察线性表中数据的变化,可知将原有的单链表一分为二,然后将后半部分的单链表逆放置,最后将逆放置的单链表重新插入前半部分的表,从而形成一个新的单链表。
(3)在将线性表一分为二,我们可以使用双指针法进行整个单链表的便利,从而使其空间复杂度始终为(1),两个指针的运行思路为,其中一个指针走两步(P2),另外一个指针走一步(P1),这个地方不好理解,希望读者在阅读过程中自己画图去切身的感受,学习最重要的就是知行合一,大家一定要自己去动手画图理解感悟。
(4)我们需要一个新的头结点去接收我们将原来单链表断开的后半部分,然后使这个新的单链表原地逆置,原地逆置我们需要用到三个指针,代码中分别用的为r、s、t。
(5)最后将原地逆置好的单链表按照题中的意思进行插入组合,最后组合完成释放新申请的头结点,释放新申请的头结点是为了代码的严谨性,在真正考场上为了赶时间我们可以不去释放。
代码的实现:
(1)在上一期我们学会了尾差法创建一个单链表,详情去看上一篇文章这里不做过多赘述。
(2)下面我们开始将我们创建好的单链表一分为二
我们写了一个函数,首先注意C++语言的引用用法,上一篇文章已经说清楚这里不再赘述,
void find_middle(LinkList L,LinkList &L2) {//首先声明我把代码放到这里不是为了让大家复制粘贴的,更多的是去了解思路,感悟方法。 L2=(LNode *)malloc(sizeof(LNode));//第二条链表的头结点 LinkList p1,p2;//双指针法可以解决很多题型,是考研初试常考 p1=p2=L->next;//让我们所申请的两个指针都指向单链表的第一个节点 while(p2!=NULL){ p2=p2->next;//要时刻判断指向是否为空,p2指针遍历走第一步 if(p2==NULL)//为了防止程序崩溃 { break; } p2=p2->next;p2指针遍历走第二步 if(p2==NULL)//为了防止程序崩溃 { break; } p1=p1->next;//p1指针遍历走第一步 } L2->next=p1->next;//由L2头结点指向后面一半链表 p1->next=NULL;//前一个节点的最后一个节点的尾部节点为null; }
这个函数就让我们实现了L单链表一分为二,下面我们开始原地逆置L2链表
void reverse(LinkList L2){ LinkList r,s,t;//定义三个指针 r=L2->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; L2->next->next=NULL; L2->next=s; };//希望大家好好画图理解,感悟。
下面我们实现插入合并
void merge(LinkList L,LinkList L2)//合并两个链表 { LinkList pcur,p,q; pcur=L->next;//pcur始终指组合后的链表尾部,也就是新链表的尾部 p=pcur->next; q=L2->next;//q指向L2第一个结点 while(p!=NULL&&q!=NULL){ pcur->next=q; q=q->next;//指向L2的下一个链表 pcur=pcur->next; pcur->next=p; p=p->next; pcur=pcur->next; } if(p!=NULL){ pcur->next=p; } if(q!=NULL){ pcur->next=q; } }
这三个函数写完我们本次大题就算结束了,大家一定要动手画思路图,然后敲代码实现。
我们终会成为更好的自己,充满希望的活下去。