"分段好啊分段好"求最大M个数的程序 (上)2006-07-18

  说起来真不是什么好事,由于本人编程水平和智力问题,考虑一个问题不是想要万能的回溯,就是想分段处理,(我也有不屑的递归)而且还是那种固定式的分段,比类似快速排序那样又差之千里.这个问题恐怕要长时间存在了.

 但往往好的思路也来自于简陋的基础.等到"段"分的更加多样灵活,甚至到动态和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),绝对比一般单纯排序快.

但这并没有结束,我还有些新的想法要说出来,同时也希望看到此问题经典解法的人请公布一下给我看看.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值