两个升序单向链表的合并
2016-09-14 00:20
197 查看
1、问题描述:
有两个非空升序排列的单向链表pHeadA和pHeadB,将其合并为一个升序链表pHead,并剔除其中的重复值。2、问题分析:
(1)、两个升序链表合并成一个升序链表,其实就是原链表删除节点,但不释放内存,而是将要删除(移动)的节点根据数据域大小关系以及不重复原则,挂在新的头指针上,形成新的链表的过程。并且新的链表依然是升序,就要求我们正向建链合并。(2)、数据域大小关系:在链表删除的过程中,我们从只需要比较两个链表头指针指向的节点数据域大小,将指针p指向数据域小的那个头结点上,并将该链表头指针移至下一个节点,然后将p指向的节点与原链表断开连接,与新链表建立连接就可以完成一个节点的移动过程,每次比较的都是头结点,重复以上步骤即可实现合并。
(3)、不重复原则:那么如果遇到重复元素该怎么办?首先我们会想到比较数据域值,若数据域值相等,必然重复,释放重复节点的内存即可。然而,两条链表中可能某一条中自身有重复值,可能两链表中互相有重复值,情况比较复杂。那么我们选择一种较为简单的方法:由于新链表的建立(旧链表的合并删除)由空链表尾插节点一步步建成,所以每次尾插一个新的节点,其前面的节点必然是升序有序且无重复值的。将p指针指向的即将要删除(尾插在新链表或释放内存)的节点与新链表的尾节点数据域相比较,如果数据域相等释放p指向节点内存,如果不相等尾插在新的链表尾节点之后,成为新的尾节点即可。
3、代码实现:
关于链表的建立可以参考我的链表操作法则之逆向遍历与倒置算法将建链与遍历写入“link.h”文件中。
# include <stdio.h> # include <stdlib.h> # include "link.h" PNODE MergeFour_Link(PNODE pHeadA, PNODE pHeadB); int main (void) { PNODE pHead = NULL; PNODE pHeadA = NULL; PNODE pHeadB = NULL; int a[18] = {1,1,1,3,6,11,15,22,37,57,61,85,111,112,112,113,113,113}; int b[12] = {1,1,2,4,6,11,15,32,37,44,68,85}; printf("链表A:\n"); pHeadA = FowardCreate_Link(a,18);//A链表创建 ForwardTraversal_Link(pHeadA);//遍历A链表 printf("链表B:\n"); pHeadB = FowardCreate_Link(b,12);//B链表创建 ForwardTraversal_Link(pHeadB);//遍历B链表 printf("合并后:\n"); pHead = MergeFour_Link(pHeadA,pHeadB);//合并两链表 ForwardTraversal_Link(pHead);//遍历合并后的链表 return 0; } /*传递A、B链表头指针,返回新的合并后的链表头指针*/ PNODE MergeFour_Link(PNODE pHeadA, PNODE pHeadB) { PNODE pHead = NULL, pTail = NULL, p = NULL;//pHead新的头指针,pTail始终指向尾节点,p指向要移动或释放的节点 while(pHeadA && pHeadB){ if(pHeadA->data < pHeadB->data){ p = pHeadA; pHeadA = pHeadA->pNext; }//如果A链表头结点数据域小于B链表头结点数据域,p指向A链表头结点,pHeadA后移 else{ p = pHeadB; pHeadB = pHeadB->pNext; }//如果A链表头结点数据域不小于B链表头结点节点数据域,p指向B链表头结点,pHeadB后移 if(NULL == pHead){ pTail = pHead = p; }//如果新的链表为空,单独处理,否则,比较新链表尾节点数据域与要移动节点p数据域大小 else if(pTail->data == p->data){ free(p); }//如果相等释放p else{ pTail = pTail->pNext = p; }//如果不相等尾插即可 }//退出while循环后,必定只有一个链表节点已经删除完毕 p = pHeadA = (pHeadB) ? pHeadB : pHeadA; while(pHeadA){ pHeadA = pHeadA->pNext; if(pTail->data == p->data){ free(p); } else{ pTail = pTail->pNext = p; } p = pHeadA; }//对剩余的未删除完的那条链表进行处理,过程与上类似,仅仅少了A->data与B->data的比较 pTail->pNext = NULL;//最后一个节点可能会被删除导致pTail指针域不为空,故最后给pTail指针域赋空 return pHead; }
4、代码优化:
3中代码由于有两个while循环,且代码有重复,则有必要对其代码进行整合,只需要一个while循环,将情况分为p指向pHeadA和p指向pHeadB两种,如下:/* 进入while循环则pHeadA与pHeadB不可能都为空。故有四种情况: 1、pHeadA为空,pHeadB不为空,则p指向pHeadB; 2、pHeadA不为空,pHeadB不为空且pHeadA->data < pHeadB->data不成立,则p指向pHeadB; 3、pHeadA不为空,pHeadB为空,则p指向pHeadA; 4、pHeadA不为空,pHeadB不为空且pHeadA->data < pHeadB->data成立,则p指向pHeadA。 */ PNODE MergeFive_Link(PNODE pHeadA, PNODE pHeadB) { PNODE pHead = NULL, pTail = NULL, p = NULL; while(pHeadA || pHeadB) { if(!pHeadB || pHeadA && pHeadA->data < pHeadB->data) { p = pHeadA; pHeadA = pHeadA->pNext; } else { p = pHeadB; pHeadB = pHeadB->pNext; }//先从原链表中删除节点(取出来) if(NULL == pHead){ pTail = pHead = p; } else if(pTail->data == p->data){ free(p); } else{ pTail = pTail->pNext = p; } } pTail->pNext = NULL; return pHead; }
以上便是链表合并过程中的最初思路与代码优化的过程,写出来作为分享与学习过程的记录。晚安!
相关文章推荐
- 已有a,b两个链表,每个链表中的结点包括学号,成绩。要求把两个链表合并,按学号升序排列。
- 已有a,b两个链表,每个链表中的结点包括学号、成绩。要求把两个链表合并,按学号升序排列
- 合并两个有序单向链表
- 两个有序单向链表的合并
- 合并两个升序链表
- 已有a,b两个链表,每个链表中的结点包括学号,成绩。要求把两个链表合并。按学号升序排列.
- YTU-OJ-单链表(线性表)-合并两个升序链表后降序输出
- 已有 a、b 两个链表,每个链表中的结点包括学号、成绩。要求把两个链表合并,按成绩升序排列。
- 合并两个链表成一个升序链表
- 已有a,b两个链表,每个链表中的结点包括学号,成绩。要求把两个链表合并,按学号升序排列。
- 两个链表按升序合并
- 有a,b两个已按学号升序排序的链表,每个链表中的结点包括学号、成绩。要求把两个链表合并,仍按学号升序排列。
- 已有a,b两个链表,每个链表中的结点包括学号,成绩。要求把两个链表合并,按学号升序排列。
- offer面试题-----两个升序链表的合并
- 合并两个链表成一个升序链表
- 将两个升序排列的单链表合并为一个降序排列的单链表且不增加新的结点
- 数据结构 - 有两个链表,第一个升序,第二个降序,合并为一个升序链表(C++)
- 合并两个已有序(升序)链表的操作!
- leetcode 21. Merge Two Sorted Lists 两个单向链表合并 指针与解指针
- 合并两个排好序的单向链表