目录
要求:A和B是两个单链表(带头结点),其中元素递增有序。设计一个算法将A和B归并成一个按元素值递增的有序链表C,C由A和B中的结点组成。
定义链表结点为:
typedef struct node{
int val;
struct node *next;
}Node;
创建单链表:
/*
**创建一个size个结点的链表并返回头结点地址
*/
Node *creat(int size)
{
if(size <= 0) //对参数的有效性进行检查
{
printf("please input the size in the range!");
exit(-1);
}
Node *head,*r,*p;
MALLOC(head); //为了方便,我把molloc及其返回值检查封装成了一个宏
printf("input the 1 node val:\n");
scanf("%d",&head->val);
p=head;
for(int i=1; i<size; i++)
{MALLOC(r);
printf("input the %d node val:\n",i+1);
scanf("%d",&r->val);
p->next=r; //p的next指向r结点
p=r; //p指向r结点
}
p->next=NULL;
return head;
}
重点来了:假如通过 creat()函数创建了两个分别独立的链表,
链表1为1 -> 2 -> 3 -> 4 -> 5 -> NULL
链表2为1 -> 2 -> 3 -> NULL
很明显合并后的链表应该为:1 -> 1 -> 2 -> 2 -> 3 -> 3 -> 4 -> 5 -> NULL
那么你打算怎么修改两节点中的指针的指向呢?也就是 Node 结构体中的 struct node *next;
很多人第一想法就是既然要修改指针,那当然要传递一个二重指针进去(此处不考虑引用),这样才能改变指针的指向,从而达到修改指针的目的。但是,在这里,情况有点特殊,因为需要被修改的指针它是在结构体里,属于结构体成员。
现在要解决的问题是:修改结构体成员指针指向,应该传递结构体的二重指针吗?如果就传一重指针行吗?先来看一下测试函数再来下结论,注意:这部分仅为测试函数,测试完即删除,与题目要求无关。
测试:
//并不能修改结构体中指针的指向(不会修改结构体中变量的值)
void testpoint(Test node,int b)
{
printf("test in function b addr = %p\n",&b);
node.next = &b;
}
//会修改结构体中指针的指向(也会修改结构体中变量的值)
void testpoint2(Test *node2,int b)
{
printf("test*2 in function b addr = %p\n",&b);
node2->next = &b;
}
//会修改结构体中指针的指向(也会修改结构体中变量的值)
void testpoint3(Test **node3,int b)
{
printf("test*3 in function b addr = %p\n",&b);
(*node3)->next = &b;
}main函数:
int a = 100,b = 8;
Test test; //Test 和 Node 类似
test.val = 10;
test.next = &a;
printf("test next addr = %p\n",test.next);
testpoint(test,b);
printf("test next addr = %p\n",test.next);
printf("\n******************************************************\n");
Test *test2 = (Test *)malloc(sizeof(Test));
test2->next = &a;
test2->val = 10;
printf("\ntest*2 next addr = %p\n",test2->next);
testpoint2(test2,b);
printf("test*2 next addr = %p\n",test2->next);
printf("\n******************************************************\n");
Test *test3 = (Test *)malloc(sizeof(Test));
test3->next = &a;
test3->val = 10;
printf("\ntest*3 next addr = %p\n",test3->next);
testpoint3(&test3,b);
printf("test*3 next addr = %p\n",test3->next);
运行后,对比结果
发现 testpoint2(test2,b); 和 testpoint3(&test3,b); 都是可以达到修改结构体成员指针指向的效果,所以我们不需要把简单问题复杂化,直接传一个 一重的结构体指针 就可以达到修改结构体成员指针指向的效果了。
所以接下来的
合并链表代码
可以这样写:
Node *comlink(Node *link1,Node *link2) //一重的结构体指针作参
{
if(link1 == NULL || link2 == NULL)
{
printf("link have null");
exit(-1);
}
unsigned int flag = 0;
Node *comlink,*r,*p;Node *link1tmp = link1;
Node *link2tmp = link2;//当两个链表都没有到结尾,继续循环比较
while(link1tmp != NULL && link2tmp != NULL )
{
if(link1tmp->val > link2tmp->val) //插入较小的link2值
{
r = link2tmp;
link2tmp = link2tmp->next; //link2指向自己链表的下一个结点
}
else
{
r = link1tmp;
link1tmp = link1tmp->next;
}if(flag)
{
p->next = r; //修改原来链表中结点的指向(构建新的合并链表)
p = r; //指向当前结点
}
else //仅执行一次,确定头结点
{
comlink = r;
p = r;
flag++;
}
}
printf("while is done\n");if(link1tmp == NULL && link2tmp != NULL)
{
p->next = link2tmp;
}
else if(link1tmp != NULL && link2tmp == NULL)
{
p->next = link1tmp;
}
else //两链表刚好比较完
{
p->next = NULL;
}
return comlink;
}main函数:
int main()
{
Node *linklist1 = creat(5);
Node *linklist2 = creat(3);
printlink(linklist1); //该函数用于打印链表各结点内容信息,方便验证
printlink(linklist2);Node * comlnk = comlink(linklist1,linklist2);
printlink(comlnk);printlink(linklist1); //结点间的指向已经被更改了,但头指针并未被改动
printlink(linklist2);
return 0;
}
运行结果:
顺便打印出 linklist1 和 linklist2 链表各节点的值发现链表的结点指向已经发生了变化,因为传入的只是结构体一重指针,所以并不会更改结构体本身的地址(要区分上面提到的 结构体成员指针的修改),当函数 comlink(linklist1,linklist2); 执行完之后 linklist 这两个指针还是指向原来的地方,所以打印出来的数据怪怪的,但是分析一下还是很容易理解的。
合并处理详细图解: