问题 :
●假设有两个有序表 LA和LB , 将他们合并成一个有序表LC
●要求不破坏原有的表 LA和 LB
LA : 1 , 3 , 5
LB : 2,4,8,20
构思:
把这两个表, 合成一个有序表 , 不是简简单单吗 ?
就算是把他们先遍历不按顺序插入到表 C里面 , 然后再排序 ,就行了,
这个的确是最笨的办法 , 但是也能完成
题目上 ,给出的是这两个表都是有序表 , 所以就省去了 ,我们遍历所有节点排序的麻烦 , 那我们
也不能拿一个表进行强行插入, 题目上也不允许,
我们干脆假设 这两个列表是两幅卡片 , 我们都从第一张开始那,左手一直拿 A , 右手一直拿 B
对比这两个卡片 ,哪个小就先装到 LC ,装进去之后,如果是左手卡片小,就装到 Lc , 然后再拿一张A里面的卡片 ,再和右手对比 ,我们这样比较的前提是 ,这两个表里面的元素都是从小到大顺序排列的 ,
这样 LC 就不会出现需要插入的情况 ,如果一副卡片放完了,另一幅如果还有 ,那剩余的卡片一定是大的 ,直接放在 LC 后面就行了
至于我们用顺序表来存储LC ,还是用链表来存储LC, 基本思路还是一样的,操作有些许差别罢了
我们用这种交替的比较两个有序表 ,然后排序的好处是,空间复杂度低, 时间复杂度也低 ,
我们有的同学会有疑问, 难道交叉比较, 不会出现错乱吗?
答案是不会的 ,因为这两个表本身就是有序表 , 然后我们插入Lc之前 , 会让两个列表中小的进行插入 , 然后后面该插入的元素, 因为是有序表,所以都大于前面的元素 . 这就保证了插入的有序性.
下面开始实操:
采用顺序表存放有序表时的归并算法:
我们要将两个有序表的元素进行比较, 就需要各有一个指针指向有序表 , 然后再进行比较 ,
两个指针比较, 小的元素插入到 LC 里面 , 插入完元素的指针向后移动 .
由于两个有序表的长度不同 , 所以交替插入有三种情况 :
●LA 和 LB 均未到达末尾时,泽其小优先尾插
●LB已经扫描插入完了, LA尚未扫描插入完 , 将其余元素插入到 Lc中
●LB尚未扫描插入完 , 将其余元素插入到 Lc中
我们先传入 LA 和 LB , 建立的LC
void UnionLIst(SqList *LA, SqList *LB , SqList *&LC){
然后,如何判断 LA和LB有没有扫描插入完呢?
我们是根据指针指向两个有序表元素的 , 所以比较指针遍历的元素个数 和 表长度 就可以得出那个表有没有遍历完
所以,先定义指向两个列表的指针
int i = 0 , j=0 , k=0; //LA 插入的数组位序 ,LB插入的数组位序 , LC插入的坐标位序
因为列表是数组 , 所以次指针非彼指针 , 这是比较元素的位序
申请 LC 的空间
LC = (SqList *)malloc(sizeof(SqList));
所以我们要进行上面三种情况的处理 :
//LA 和 LB 均未到达末尾时,泽其小优先尾插
while(i<LA->length && j<LB->length){ 交替插入元素}
//LB已经扫描插入完了, LA尚未扫描插入完 , 将其余元素插入到 Lc中
while(i<LA->length){ 把A中剩余的元素全插入到LC }
//LB尚未扫描插入完 , 将其余元素插入到 Lc中
while(i<LB->length){ 把B中剩余的元素全插入到LC }
然后就把C的长度更新一下就行了\
开始代码实现:
void UnionList(SqList *LA , SqList *LB , SqList *&LC)
{
int i=0; //LA的坐标位序
int j=0; //LB的坐标位序
int k=0; //LC的坐标位序
LC = (SqList *)malloc(sizeof(SqList)); //为表C分配空间
//LA和LB 均未到达末尾 ,选择小的加入LC
while(i<LA->length && j<LB->length)
{
if(LA->data[i] < LB ->data[j])
{
LC->data[k] = LA ->data[i];
i++; //相应的LA指针也向后移动
k++; //元素加一,位序后移
}
else //LA->data[i] >= LB->data[j] ,交替比较无所谓的,反正小的会在前面
{
LC->data[k] = LB->data[j];
j++; //相应的LB指针也向后移动
k++; //元素加一,位序后移
}
}
//下面两步最多进行一步:
//经过上一轮循环插入,LB已经扫描插入完了, LA尚未扫描插入完 , 将LA其余元素插入到 Lc中
while(i<LA->length)
{
LC->data[k] = LA->data[i];
k++;
i++; //相应的LA指针向后移动
}
//经过第一轮循环插入,,LA已经扫描插入完了, LB尚未扫描插入完 , 将LB其余元素插入到 Lc中
while(j<LB-length)
{
LC->data[k] = LB->data[j];
j++; //相应的LB指针向后移动
k++; //元素加一,位序后移
}
}
采用单链表存放有序表时的归并算法:
和上面那个问题一样 , 同样的思路 ,不多赘述了 ,区别就是 遍历两个链表的指针不一样 ,插入的赋值操作不一样 , 其他的算法思路都是一样的, 下面直接上代码:
//传入单链表LA,LB,和创建的链表LC void UnionList1(LinkList *LA, LinkList*LB , LinkList *&LC) { //定义指向LA和LB的指针 LinkList *pa = LA ->next, *pb = LB->next; //构建链表C的头结点 , 我们把LA和LB的新元素插入到LC 需要利用尾插法, //尾插法需要尾指针定位到LC的最后一个节点 LinkList *r; //指向LC的尾结点的指针 LinkList *s; //向LC的新节点的指针 LC = (LinkList *)malloc(sizeof(LinkList)); //刚开始,尾指针指向LC的头结点,LC的尾指针置空 r=Lc; LC->next = NULL; //下面开始插入: //LA 和 LB 均未到达末尾时,泽其小优先尾插 while(pa!=NULL && pb !=NULL) { //为了不破坏LA和LB,要另起灶炉,构建新节点 s = (LinkList *)malloc(sizeof(LinkList)); //如果指向LA的指针的数据小于LB的指针数据, 就把La的指针pa 数据赋值给新节点数据 if(pa->data < pb -> data) { s->data = pa ->data; //LA指针后移 pa = pa ->next; //对LC进行尾插法 r ->next=s; r=s; } else //同理: { s->data = pb->data; //LB指针后移 pb = pb->next; //对LC进行尾插法 r ->next=s; r=s; } } //经过上一步的交替插入,要么两个表交替插入完了,要么是下面两者之一 //LB已经扫描插入完了, LA尚未扫描插入完 , 将其余元素插入到 Lc中 while(pa!=NULL) { s = (LinkList *)malloc(sizeof(LinkList)); //LA指针送给的数据指向新节点 s->data = pa->data; //下面开始尾插法 r->next = s; r = s; //LA指针后移 pa = pa ->next; } //LA已经扫描插入完了, LB尚未扫描插入完 , 将其余元素插入到 Lc中 while(pb!=NULL) { s = (LinkList *)malloc(sizeof(LinkList)); //LA指针送给的数据指向新节点 s->data = pb->data; //下面开始尾插法 r->next = s; r = s; //LB指针后移 pb = pb ->next; } s->next = NULL; }