1、实践题目
7-1最优合并问题
2、问题描述
给定k 个排好序的序列, 用 2 路合并算法将这k 个序列合并成一个序列。 假设所采用的 2 路合并算法合并 2 个长度分别为m和n的序列需要m+n-1 次比较。试设计一个算法确定合并这个序列的最优合并顺序,使所需的总比较次数最少。 为了进行比较,还需要确定合并这个序列的最差合并顺序,使所需的总比较次数最多。
3、算法描述
定义两个整型变量ans1、ans2来分别记录最少和最多的比较次数。
在计算最少的比较次数时,利用了哈夫曼树的思想,把最短的两个序列合并成一个序列,然后把合并的结果放入序列集合中,继续寻找当前序列集合中最短的序列并进行合并,直到所有序列合并在一起,结束。
1 while(len>1){ 2 sort(a,a+len); 3 ans1+=a[0]+a[1]-1; 4 tem = a[0]+a[1]; 5 a[0] = tem; 6 for(int i=1;i<len-1;i++){ 7 a[i] = a[i+1]; 8 } 9 len=len-1; 10 }
在计算最多的比较次数时,思路和最少次数是一样的,将哈夫曼树的思想逆转过来,把最长的两个序列合并成一个序列,然后把合并的结果放入序列集合中,继续寻找当前序列集合中最长的序列并进行合并,直到所有序列合并在一起,这样就可以得到最多的比较次数了。
bool cmp(int a,int b){ return a>b; }
1 sort(b,b+n,cmp); 2 for(int i=0;i<n-1;i++){ 3 ans2+=b[i]+b[i+1]-1; 4 b[i+1]=b[i]+b[i+1]; 5 }
4、算法时间及空间复杂度分析
在计算最少的比较次数时,每次合并都要合并当前序列集合中两个最小的序列,总共要合并(n-1)次,每次合并完成后,要进行排序,因为用到的是快排,所以是O(nlogn)级的时间复杂度,(同时还有一个数组元素往前移动的处理为 O(n)级的时间复杂度),综合起来,计算得到最少比较次数的时间复杂度是O(n^2logn)。在计算最多的比较次数时,由于最大值合并肯定还是新生成序列集合的最大值的特性,只需要进行一次排序,是O(nlogn)的时间复杂度,而数组元素前移的处理仅为O(n)级的时间复杂度,综合起来,计算得到最多比较次数的时间复杂度是O(nlogn)。所以总的时间复杂度是O(n^2logn)。
由于申请了两个数组分别用来处理最多和最少的比较次数,所以空间开销是O(n)级别。
5、心得体会
一开始和队友秒A了第三题,觉得很激动,所以说剩下两题一人看一道,结果我在看第一题的时候因为纠结在二路合并是什么东西这个点上,一直没有看懂这道题目想考察的知识点,结果很沮丧地和转战第二道删数问题和队友一起想。开始我们迸发出很多思路,有错误的,有太复杂的,有不知道怎么实现的,最后经过筛选再结合贪心算法的知识,我们得到了一个比较简单明了的操作算法,就是不断遍历这串数,当遍历到某个数,如果发现这个数比它下一个数要大,就把它删除。这就是贪心的思想,证明很简单,假设这个数是第n位,它的下一个数比它小,如果不删这个数是最优解,那我们可以发现,删了这个数,那么它的下一个数就是第n位了,得到一个比最优解更小的数,推出矛盾,所以这个数一定是要删除的。可惜的是这道题花的时间有点长,下课前没有足够的时间完成第一题。当听到其他同学说到第一题用哈夫曼树时,懵了一下,然后瞬间又是一波深深地自我鄙视。所以特别地把这道题拿出来进行分析。这次结对编程队友很给力,第二题像开挂了一样想出各种思路和方法,值得膜拜一下,这次躺分了,下次换我来带。