算法 线性表的删除
与插入运算类似,在顺序表上实现删除运算也必须移动结点,这样才能反映出结点间的逻辑关系的变化。若i=L->last+1,则移位语句L->elem[k-1]=L->elem[k]不执行,因为循环变量的初值大于终值,此时不需要移动元素,仅将表长度减1即可。显然,删除算法中移位语句L->elem[k-1]=L->elem[k]的执行频度也与删除位置i有关。
在顺序表中插入或删除一个数据元素时,其时间主要耗费在移动数据元素上。对于插入算法而言,设Pi为在第i个元素之前插入元素的概率,并假设在任何位置上插入的概率相等,即Pi=1/(n+1),i=1,2,…,n+1。。设Eins为在长度为n的表中插入一元素所需移动元素的平均次数,则
同理,设Qi为删除第i个元素的概率,并假设在任何位置上删除的概率相等,即Qi=1/n,i=1,2,…,n。删除一个元素所需移动元素的平均次数Edel为
由上式分析可知,在顺序表中插入和删除一个元素时,其时间主要耗费在移动数据元素上。 做一次插入或删除平均需要移动表中一般元素,当n加大是效率较低。
例2-1 有两个顺序表LA和LB,其元素均为非递减有序排列,编写一个算法,将它们合并成一个顺序表LC,要求LC也是非递减有序排列。例如LA=(2,2,3),LB=(1,3,3,4),则LC=(1,2,2,3,3,3,4)。
算法思想:设表LC是一个空表,卫士LC也是非递减有序排列,可设两个指针i、j分别指向表LA和LB中的元素,若LA.elem[i]>LB.elem[j],则当前先将LB.elem[j]插入到表LC中;若LA.elem[i]≤LB.elem[j],者当前先将LA.elem[i]插入到表LC中,如此进行下去,直到其中一个表被扫描完毕,然后再将未扫描的表中剩余的所有元素放到表LC中。
void merge(SeqList *LA,SeqList *LB,SeqList *LC) { int i,j,k,l; i=0;j=0;k=0; while(i<=LA->last&&j<=LB->last) if(LA->elem[i]<=LB->elem[j]) { LC->elem[k]=LA-elem[i]; i++;k++; } else { LC->elem[k]=LB->elm[j]; j++;k++; } while(i<=LA->last)/*当表LA有剩余元素时,则将表LA余下的元素赋给表LC*/ { LC->elem[k]=LA->elem[i]; i++;k++; } while(j<=LB->last)/*当表LB有剩余元素时,则将表LB余下的元素赋给表LC*/ { LC->elem[k]=LB->elem[j]; j++;k++; } LC->last=LA->last+LB->last+1; } |
算法 线性表的合并运算
算法分析:由于两个待归并的表LA、LB本身就是有序表,且表LC的建立采用的是尾插法建表,插入时不需要移动元素,因此算法的时间复杂度为0(LA->last+LB->last)。
由上面的讨论可知,线性表顺序表示的优点是:
(1)无需为表示结点间的逻辑关系而增加额外的存储空间(因为逻辑上相邻的元素其存储的物理位置也是相邻的);
(2)可方便地随机存取表中的任意元素。
其缺点是:
(1)插入或删除运算不方便,出表位的位置外,在表的其它位置上进行插入或删除操作都是必须移动大量的元素,其效率较低;
(2)由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配,因此当表长变化较大时,那一确定合适的存储规模。若按可能达到的最大长度预先分配表空间,则可能造成一部分空间长期闲置而得不到充分利用;若事先对表长估计不足,则插入操作可能是表长超过预先分配的空间而造成溢出。