说起来真不是什么好事,由于本人编程水平和智力问题,考虑一个问题不是想要万能的回溯,就是想分段处理,(我也有不屑的递归)而且还是那种固定式的分段,比类似快速排序那样又差之千里.这个问题恐怕要长时间存在了.
但往往好的思路也来自于简陋的基础.等到"段"分的更加多样灵活,甚至到动态和2维(别问我这两词怎么讲,我也不知道), 大概就有前途了.
先来看一个问题,这是我的算法书上写的:给定数组a[0:n-1],试着设计一个算法,在最坏情况下用[3n/2-2]次比较找出其中元素最大值和最小值.
除去使用分治策略那样,在T(n)=kT(n/m)+O(n)上下下文章,减少算法时间(或者具体到这里的比较次数),大概就是要"珍惜"劳动成果了.普通的思想是两趟扫描求出最大和最小.但这样会浪费一些已经比较过的宝贵经验.要让每一个次比较都会对后面的再比较有用.所以此题就要将a中元素两个一对进行比较,大的参与最大值比较,小的参与最小值比较,才会符合条件.(但此题要求用分治法做,我可没有那样的瘾......)
此想法在我看某网站的编程比赛问题时候给予启发.题目很简单,就是要求求出一个数组前M大的数字.
我的想法是首先将数组a按照某个适合的间距s进行分段,第一步选出每小组中最大的元素,保存在数组b中.然后重复执行以下步骤:
选出b中最大元素,将其标志位置1,再将此元素所在小组进行重新扫描,讲新的最大元素放入b中原位置.
如此我在做的时候遇到了很多问题,除了一些小的疏忽,还有一个大的疏忽是有可能存在某组中元素都被选中过的情况,所以还要引进一个对应b的标志位,当b[i]对应的小组都被选完以后,将其标志位置1,不参与比较.
下面是我的程序,有些要提前说明,b中保存的其实是元素在a中的序号.
#include <stdio.h>
#include <stdlib.h>
#define N 16
#define M 5
int c[N];
int d[M];
int Max(int a[],int l,int r){
int i,t,flag=0;
for(;l<=r;l++){ //将第一个未被选取过的数字暂时作为最大保存在t中
if(c[l]==0){
t=l;
i=l+1;
flag=1;
break;
}
}
if(flag==0){ //此组都已经被选中
d[(l-1)/5]=1;
return -1;
}
while(i<=r){
if(c[i]==0&&a[i]>a[t])
t=i;
i++;
}
return t; //返回的为最大数的序号
}
void fird(int a[]){
int i,j,t,s,l;
int b[N/5+1];
int m[N];
i=j=0;
while(j+4<N){ //首先按组搜索出每组最大数的需要,填入b中
b[i]=Max(a,j,j+4);
j=j+5;
i++;
}
if (N%5==0)
s=N/5;
else{
s=N/5+1;
b[i]=Max(a,j,N-1);
}
i=0;
while(i<M){
for(j=0;j<s;j++){
if(d[j]==0){
t=j;
j++;
break;
}
}
for(;j<s;j++){ //选出b中最大数的序号
if(d[j]==0&&a[b[j]]>a[b[t]]){
t=j;
}
}
c[b[t]]=1;//改变其标志位
m[i]=a[b[t]];//保存第i+1大的数
//将其序号所在组重新排序
l=b[t];
l=l-l%5;
if(l+4<N)
b[t]=Max(a,l,l+4);
else
b[t]=Max(a,l,N-1);
i++;
}
for(i=0;i<M;i++)
printf("%d/n",m[i]);
}
还劲的过少量数据的测试.但也有许多问题:
1.为什么我编的东西可读性都这么差,变量数组一大堆?
2.空间占用太大了,但我也考虑过节省空间的办法,但那样在时间上牺牲更大,本人能力尚无法解决这问题.
3.我不会算这个的时间复杂度,应该不会超过O(nlogn),绝对比一般单纯排序快.
但这并没有结束,我还有些新的想法要说出来,同时也希望看到此问题经典解法的人请公布一下给我看看.