线性表的合并、有序表(利用顺序表)的合并

线性表的合并

【例1】求解一般集合的并集问题

【问题描述】

已知两个集合 A 和 B ,现要求一个新的集合 A=A U B ,例如,设
A=(7,5,3,11)
B=(2,6,3)
合并后,A=(7,5,3,11,2,6)

【问题分析】

可以利用两个线性表 LA 和 LB 分别表示集合 A 和 B (即线性表中的数据元素为集合中的成员),这样只需要扩大线性表 LA ,将存在于 LB 中而不存在于 LA 中数据元素插入到 LA 中去。只要从 LB 中依次取得每个数据元素,并依值在 LA 中进行查访,若不存在,则插入之。
(上述操作过程可用算法描述。具体实现时既可采用顺序形式,也可采用链表形式)

【算法步骤】

1.分别获取 LA 的表长 m 和 LB 的表长 n
2.从 LB 中第1个数据元素开始,循环 n 次执行以下操作:

  • 从 LB 中查找第 i (1 ≤ i ≤ n)个数据元素赋给 e
  • 在 LA 中查找元素 e ,如果不存在,则将 e 插在表 LA 的最后

【算法描述】

void MergeList(List  &LA,List LB)
{
     m=ListLength(LA);  n=ListLength(LB);   //求线性表的长度
     for(i=1 ;i<=n ;i++ )
     {
         GetElem(LB ,i ,e);                //取LB中第i个元素赋值给e
         if( !LocateElem(LA ,e) )          //LA中不存在和e相同的数据元素
            ListInsert(LA,++m,e);          //将e插在LA的最后
      } 
}           

【算法分析】

上述算法的时间复杂度取决于抽象数据类型List定义中基本操作的执行时间,假设 LA 和 LB 的表长分别 m 和 n ,循环执行 n 次,则:

当采用顺序存储结构时, 在每次循环中,GetElem 和 ListInsert 这两个操作的执行时间和表长无关,LocateElem 的执行时间和表长 m 成正比,因此,此算法的时间复杂度为O(m×n)

当采用链式存储结构时,在每次循环中,GetElem 的执行时间和表长 n 成正比,而 ListInsert 和 LocateElem 这两个操作的执行时间和表长 m 成正比,因此,若假设 m 大于 n ,此算法的时间复杂度也为 O(m×n)


有序表的合并

若线性表中的数据元素相互之间可以比较,并且数据元素在线性表中依值非递减或非递增有序排列,则该线性表为有序表
解释:
1,2,3,4,5… : 递增排列
9,8,7,6,5… : 递减排列
1,2,3,3,4,5,8 ,8,… : 非递减排列
9,8,7,7,6,5,5,2,1,… : 非递增排列

【例2】求解有序集合的并集问题

【问题描述】

有序集合是指集合中的元素有序排列。
已知两个有序集合 A 和 B ,数据元素按值非递减有序排列,现要求一个新的集合 C = A U B ,使集合 C 中的数据元素仍按值非递减有序排列。例如,设
A=(3,5,8,11)
B=(2,6,8,9,11,15,20)
则,C=(2,3,5,6,8,9,11,11,15,20)

【问题分析】

与【例1】一样,利用两个线性表 LA 和 LB 分别表示集合 A 和 B,不同的是,此例中的 LA 和 LB 有序,这样便没有必要从 LB 中依次取得每个数据元素,到 LA 中进行查访。
如果 LA 和 LB 两个表长分别记为 m 和 n,则合并后表长应为 m+n 。 先设 LC 为空表,然后将 LA 或 LB 中的元素逐个插入到 LC 中即可。可设两个指针 pa 和 pb 分别指向 LA 和 LB 中的某个元素,若设 pa 当前所指的元素为 a ,pb 当前所指的元素为 b ,则当前应插入到 LC 中的元素 c 为
在这里插入图片描述
显然,指针 pa 和 pb 的初值分别指向两个有序表的第一个元素,在所指元素插入 LC 之后,在 LA 或 LB 中顺序后移

【算法步骤】

  • 创建一个表长为 m+n 的空表 LC
  • 指针 pc 初始化,指向 LC 的第一个元素
  • 指针 pa 和 pb 初始化,分别指向 LA 和 LB 的第一个元素
  • 当指针 pa 和 pb 均未到达相应的表尾时,则依次比较 pa 和 pb 所指向的元素值,从 LA 或 LB 中“摘取”元素值较小的结点插入到 LC 的最后
  • 如果 pb 已到达 LB 的表尾,依次将 LA 的剩余元素插入 LC 的最后
  • 如果 pa 已到达 LA 的表尾,依次将 LB 的剩余元素插入 LC 的最后

【算法描述】

void MergeList_Sq(SqList  LA,SqList  LB,SqList  &LC)
{
     LC.length=LA.length+LB.length;        //新表长度
     LC.elem=new ElemType[LC.length];      //为新表分配一个数组空间
     pc=LC.elem;                           //指针pc指向新表的第一个元素
     pa=LA.elem;  pb=LB.elem;              //指针pa和pb的初值分别指向两个表的第一个元素
     pa_last=LA.elem + LA.length-1;        //指针pa_last指向LA最后一个元素
     pb_last=LB.elem + LB.length-1;        //指针pb_last指向LB最后一个元素
     while((pa<=pa_last) && (pb<=pb_last)) //LA 和 LB 均未达到表尾
     {
           if(*pa<=*pb)   *pc++=*pa++;     //依次“摘取”两表中较小的结点插入到LC的最后
           else           *pc++=*pb++;
     }
     while(pa<pa_last)   *pc++=*pa++;      //LB已到达表尾,依次将LA的剩余元素插入到LC的最后
     while(pb<pb_last)   *pc++=*pb++;      //LA已到达表尾,依次将LB的剩余元素插入到LC的最后

【算法分析】

若对于上算法中第一个循环语句的循环体做如下修改:分出元素比较的第三种情况,当 *pa==*pb 时,只将两者中之一插入 LC ,则该算法完成的操作和【例1】相同,但时间复杂度却不同。【例2】算法中,由于 LA 和 LB 中元素依值非递减,则对 LB 中的每个元素,不需要在 LA 中从表头到表尾进行全程搜索。如果两个表长分别记为 m 和 n ,则【例2】算法中的循环最多执行的总次数为 m+n。
所以算法的时间复杂度为O(m+n)
此算法在归并时,需要开辟新的辅助空间,所以空间复杂度也为O(m+n) ,空间复杂度较高。利用链表来实现上述归并时,不需要开辟新的存储空间,可以时空间复杂度达到最低。


借鉴:《数据结构》 严蔚敏

发布了5 篇原创文章 · 获赞 22 · 访问量 1648

猜你喜欢

转载自blog.csdn.net/wmy0217_/article/details/103914610