1、系列文章目录
题目详情:
解题思路: 先找到链表的中间结点,前面一半是链表 L,将链表的后半部分给一个新的头结点 L2,然后将链表 L2 进行原地逆置,然后再将 L 和 L2 链表进行合并
2、如何找到链表的中间结点?
定义两个指针 pcur,ppre,我们让pcur 指针每次走两步,ppre 指针每次走一步,这样当 pcur 指针走到最后,那么 ppre 指针刚好在中间
图片如下
代码如下:
//找到中间结点并分开
void find_middle(LinkList L, LinkList& L2)//因为L2头指针会改变,需要&,L则已经分配好了头结点地址
{
L2 = (LinkList)malloc(sizeof(LNode));//先为第二条链表申请空间
LinkList ppre, pcur;//双指针法遍历链表
ppre = pcur = L->next;//都在第一个结点位置
while (pcur != NULL)//pcur偏转大,利用它作条件
{
pcur = pcur->next;
if (NULL == pcur)//先偏转一次,防止链表为0的情况
{
break;
}
pcur = pcur->next;//继续偏转
if (NULL == pcur)//为了使偶数个结点的链表时,pcur已经为NULL了,ppre依然指向a1,a2,到a6 中的a3结点
{
break;
}
ppre = ppre->next;
}
L2->next = ppre->next;//由L2头结点指向后面一半链表
ppre->next = NULL;//前面一半链表的最后一个结点,next要为NULL
}
3、如何让L2逆置?
定义三个指针r、s、t,使它们分别指向链表开始的三个结点,先让s->next=r,则a2指向a1,之后r、s、t依次后移,依次完成a3指向a2,a4指向a3,再后移发现t为空,此时结束循环,但最后的a5还没指向,所以在循环外要再执行依次s->next=r。
最后需要让L2->next->next=NULL(此时L2->next->next拿到的是a1),因为原有链表的头结点变成最后一个结点,需要让最后一个结点的next为NULL,再让L2->next=s,使s成为逆置后的链表的第一个结点。
图片如下
代码如下:
//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;//当t指针为空时,还差最后一次连接倒置
L2->next->next = NULL;//意思是(L2->next)为第一个结点,然后作为新链表最后一个结点时,它的->next为空
L2->next = s;//s变成了新链表的第一个结点,头结点L2指向它
}
4、如何轮流合并?
定义三个指针pcur、p、q,让pcur始终指向合并后的新链表的尾部,初始化为pcur=L->next,让p始终指向L待插入的结点,初始化为p=pcur->next,q始终指向L2待插入的结点,初始化为q=L2->next
循环到最后,L或L2总会有一个链表剩一个结点没放进去,此时判断p不为NULL或者q不为NULL,再将p或q放进去即可
图片如下:
代码如下:
//进行轮流合并
void merge(LinkList L, LinkList L2) {
LinkList pcur, p, q;
pcur = L->next;//pcur从L的第一个结点开始,始终指向组合后链表的链表尾
p = pcur->next;//p指向L的第二个结点,因为第二个位置要插入L2的结点,所以他指向第二个结点
q = L2->next;//q指向L2的第一个结点
while (p != NULL && q != NULL) {
pcur->next = q;//合并L2的结点
q = q->next;//q继续偏转
pcur = pcur->next;//pcur也继续偏转
pcur->next = p;//合并L的结点
p = p->next;//p继续偏转
pcur = pcur->next;//pcur也继续偏转
}
//L和L2链表都可能剩余一个结点,要判断后放进来
if (p != NULL) {
pcur->next = p;//pcur始终指向组合后链表的链表尾
}
if (q != NULL) {
pcur->next = q;
}
/*因为L和L2尾部都有NULL,不需要再操作了*/
}
5、完整代码:
#include <stdio.h>
#include <stdlib.h>
typedef int Elemtype;
typedef struct LNode {
Elemtype data;
struct LNode* next;
}LNode,*LinkList;
void list_tail_insert(LinkList& L) {
L = (LinkList)malloc(sizeof(LNode));
LinkList s;
Elemtype x;
scanf("%d", &x);
LinkList r = L;
while (x != 9999) {
s = (LinkList)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf("%d", &x);
}
r->next = NULL;
}
void print_list(LinkList L) {
LinkList r = L->next;
while (r != NULL) {
printf("%3d", r->data);
r = r->next;
}
printf("\n");
}
//找到中间结点并分开
void find_middle(LinkList L, LinkList& L2)//因为L2头指针会改变,需要&,L则已经分配好了头结点地址
{
L2 = (LinkList)malloc(sizeof(LNode));//先为第二条链表申请空间
LinkList ppre, pcur;//双指针法遍历链表
ppre = pcur = L->next;//都在第一个结点位置
while (pcur != NULL)//pcur偏转大,利用它作条件
{
pcur = pcur->next;
if (NULL == pcur)//先偏转一次,防止链表为0的情况
{
break;
}
pcur = pcur->next;//继续偏转
if (NULL == pcur)//为了使偶数个结点的链表时,pcur已经为NULL了,ppre依然指向a1,a2,到a6 中的a3结点
{
break;
}
ppre = ppre->next;
}
L2->next = ppre->next;//由L2头结点指向后面一半链表
ppre->next = NULL;//前面一半链表的最后一个结点,next要为NULL
}
//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;//当t指针为空时,还差最后一次连接倒置
L2->next->next = NULL;//意思是(L2->next)为第一个结点,然后作为新链表最后一个结点时,它的->next为空
L2->next = s;//s变成了新链表的第一个结点,头结点L2指向它
}
//进行轮流合并
void merge(LinkList L, LinkList L2) {
LinkList pcur, p, q;
pcur = L->next;//pcur从L的第一个结点开始,始终指向组合后链表的链表尾
p = pcur->next;//p指向L的第二个结点,因为第二个位置要插入L2的结点,所以他指向第二个结点
q = L2->next;//q指向L2的第一个结点
while (p != NULL && q != NULL) {
pcur->next = q;//合并L2的结点
q = q->next;//q继续偏转
pcur = pcur->next;//pcur也继续偏转
pcur->next = p;//合并L的结点
p = p->next;//p继续偏转
pcur = pcur->next;//pcur也继续偏转
}
//L和L2链表都可能剩余一个结点,要判断后放进来
if (p != NULL) {
pcur->next = p;//pcur始终指向组合后链表的链表尾
}
if (q != NULL) {
pcur->next = q;
}
/*因为L和L2尾部都有NULL,不需要再操作了*/
}
int main() {
LinkList L;
list_tail_insert(L);
print_list(L);
printf("----------------------------------------\n");
LinkList L2;//先定义第二条链表
find_middle(L, L2);
printf("找到中间结点并分开后\n");
print_list(L);
print_list(L2);
printf("----------------------------------------\n");
reverse(L2);
printf("L2逆置后为\n");
print_list(L2);
printf("----------------------------------------\n");
merge(L, L2);
free(L2);//因为组合后的链表是L,L2没用需要释放
printf("合并后为\n");
print_list(L);
return 0;
}
制作不易,如果可以帮到你,请多多支持并指正!!