免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: samhoo
打印 上一主题 下一主题

挑战数组腾挪大法 [复制链接]

论坛徽章:
0
31 [报告]
发表于 2002-10-26 22:11 |只看该作者

挑战数组腾挪大法

我的算法是从两头向中间找不属于那个位置的数据,从左边开始找到的是大于目标数据(X)的位置,从右边开始找到的是小于X的位置,把他们互换。
如果遇到等于X的数,找出其后最后一个顺序适合的数,将他们进行互换。
这样向中间逼近,就能吧X放到合适的位置上。

早上贴的程序不能处理数组中有多个等于X的数的情况,所以我又进行了改进。
(if (my_array[last_less_room] == my_array[last_more_room]) ......这一段)
如果两头都停止在等于X的数的位置,那么从它们之间找到一个不等于X的数,
与两端的数进行互换,这样循环就能够继续下去,直至last_less_room和last_more_room之间全部都是X。

论坛徽章:
0
32 [报告]
发表于 2002-10-27 11:15 |只看该作者

挑战数组腾挪大法

首先,先要说明一下,本贴的前几个 aegis 回复不是我写的
是谁写的呢?就是看谭浩强的书学 c, 不得其法的那个朋友,
他和我在一个单位工作,知道我的账号;当然了,把谭浩强封为笨蛋,
也是从我这里学的。现在他的 c/c++ 写得很不错了,自然对谭好强
没什么好印象。请不要误会,这里没有推卸责任的意思,他的措辞虽然
太激烈了一点,但观点我还是赞同的。

先回答第一个问题:谭好强的编程书我看过 basic, fortran, c 这三本看得都
非常仔细,其它的也草草看过。对 basic 那本, 好像是和田淑清合作的吧,
印象还可以,fortran 一般,c 那一本纯粹是误人子弟。为什么这么说呢?
我当然知道这些都是针对初学者的书。我们知道,每种语言,都只不过是对
算法的表述。如何表述特定算法,表现了语言创造者对计算机系统核算法语义
的基本看法和理解,以及他们的个性和编程习惯。我们面向的环境不同,任务不同,
当然,个人的理解也不同,所以 basic, c, pascal, sql, smalltalk ...
各种语言都有其存在价值和适用范围,也各有其特点。我的观点,谭的书
对各种语言的理解没有表现出来,看得越多,越发现作者本人恐怕是不太
了解这些语言的。当然也就写得千篇一律,全都 basic 了。初学者如果学习
basic, fortran 我会让他去看谭的书,因为这两本书的思想是 basic 和
fortran 的。

当然,看他的书学习 c 是不行的。为什么呢?如果从 basic 程序员的角度,
使用 c 语法,写出来的东西会怎么样呢?当然,c 编译是能通过的,程序是
可以使用的。但这样的程序,能称为 c 程序吗?所以说,c 初学者学习他的书,
坏处很大。学得慢、学得累倒也罢了,学歪了再想纠正是很困难的。tom
swan 的 c 还是不错的,c++ 也还行。当然,学习 c 最好还是清高手讲,蛤蟆
老兄给你讲三天,什么书都可以扔了。另外一点,就是要多读别人的程序,
不要怕累,多比较同一问题不同 programer 的实现,进步是很快的。

关于谭嘛,他自己理解还不够,就写了那么多 《* 语言编程》,至少有点
不负责任吧?如果我是他,宁愿被人骂是笨蛋,这样不过是年纪大了,理解
不到了,水平不够了。否则,不成了为了赚钱出垃圾产品,成品德问题了。

论坛徽章:
0
33 [报告]
发表于 2002-10-27 13:55 |只看该作者

挑战数组腾挪大法

第二,对于这个问题的解法

对于 L 中只有一个 x 的情况,他贴出的程序(修改过 >;= 的)是一个比较简洁的算法。
也符合时间复杂度的要求,即使是在最不利情况下。他跟我说题目中要求指出 x 位置,
所以认为 x 只有一个

对于 L 中有多个 x 的情况,有两种比较简单,比较容易理解的方法
第一个,是从他的思路出发进行修改,程序长了一点,但效率比较高

第二个,其实很简单,不要把思路限制在 one pass 的范围内就知道了
效率虽然低一些,但容易理解:

0) 先数出比 x 小的数和比 x 大的数的个数
当然也就知道 x 的个数了,也就知道 小数、x、大数 的位置范围了,

for ( sm = gm = l = 0&#59; l < 1000&#59; l ++ )
{
    if ( X >; L [ l ] ) sm ++&#59;
    if ( X < L [ l ] ) gm ++&#59;
}
em = 1000 - sm - gm&#59;

以后的事情就简单了

1) 从 0 开始遍历到 sm - 1&#59; 发现 >;= X 的就和 sm 后面的小数交换
for ( i = sm, s_s = 0&#59; s_s < sm&#59; s_s ++ )
    if  ( X <= L [ s_s ] )
    {
        while ( X <= L [ i ] ) i ++&#59;
        l = L [ i ]&#59; L [ i ] = L [ s_s ]&#59; L [ s_s ] = l&#59;
    }

2) 从 sm 开始遍历 em 个&#59; 发现 != X 的就和 sm + em 后面的 X 交换
for ( i = sm + em, e_s = sm&#59; e_s < sm + em&#59; e_s ++ )
    if  ( X != L [ e_s ] )
    {
        while ( X != L [ i ] ) i ++&#59;
        L [ i ] = L [ e_s ]&#59; L [ e_s ] = X&#59;
    }

0), 1), 2) 时间复杂度都 O(n)

以上步骤是一个算法说明,没有经过编译验证 (目前手头没有 c compiler)

对于 假 aegis 算法的改进,让他自己写吧 (他有 compiler)

论坛徽章:
0
34 [报告]
发表于 2002-10-27 14:28 |只看该作者

挑战数组腾挪大法

对于 beggar 的意见:

第一,真正具有开设计算机系能力的大学,其计算机系一般不讲授 c 语言,
是由学生自学的;其计算机系作为公共课程讲授的计算机语言,使用谭的书
作教材的也不多。请不要凭想象猜测。

第二,从来没人说有一本书可以适用于一个程序员的各个阶段。但一般来说,
作为程序员,学习一门语言只需要一本书就够了,了解其思路、特性和语法特点,
其它可以查手册和联机文档。


就以这个贴子来说吧:不能否认,题目本身措辞并不是很严格,我的朋友
将其理解为数组中没有 X 重复值也可理解:
1)题目中要求给出 x 下标;
2)他的程序目的是提供一个思路,而不是严格的实现
当然,题目要求决定算法。有重复 x 的算法我想并不难,即使对于初学者,
自己动脑子想想也能解决

对于算法,我不想多说,在我们概念里,不仅“快速傅氏变换”是算法,
for (i=0&#59;i<10&#59;i++) L=0&#59; 这也是一个算法。

当然要考虑空间复杂度和时间复杂度,因为这是题目里明确提出了要求的。
另外,如果要求时间复杂度 O(n),不仅交换的次数要 O(n),比较的次数
也要 O(n),并且在最不利的情况下也保证 O(n),这样才算符合题目要求。

beggar 老兄,说了这么多,其实对你我都没什么用处。我们还是可以坚持
自己的看法。也许,多针对具体问题讨论讨论更加实际,我很想听听你对
这个题目的看法

论坛徽章:
0
35 [报告]
发表于 2002-10-27 14:53 |只看该作者

挑战数组腾挪大法

下面是 假aegis 的算法:

#deinfe LL 1000

const int x = X&#59;
long L [ LL ]&#59;
int s, e, s0, e0, l&#59;

for ( s = 0, e = LL - 1&#59; s < e&#59; )
{
    while ( x >; L [ s ] ) s ++&#59;  // 左面跳过小数
    while ( x < L [ e ] ) e ++&#59;  // 右面跳过大数
    if  ( s >;= e ) break&#59;

    if  ( x < L [ s ] ) // 大数交换到最右面
        { l = L [ e ]&#59; L [ e -- ] = L [ s ]&#59; L [ s ] = l&#59; }
    else if ( x >; L [ e ] ) // 小数交换到最左面
        { l = L [ e ]&#59; L [ e ] = L [ s ]&#59; L [ s ++ ] = l&#59; }
    else // 左、右端都是 x, 中间可能有小数或大数
    {   // 进行交换将两端的 x 移至中间
        for ( s0 = s, e0 = e&#59; s0 < e0 )
        {   // s0, e0 为非 x 数位置范围
            while ( x == L [ s0 ] ) s0 ++&#59;
            while ( x == L [ e0 ] ) e0 --&#59;
            if  ( s0 >; e0 ) break&#59;

            if  ( x >; L [ s0 ] ) // 发现小数
            {   // 交换至左端
                L [ s ++ ] = L [ s0 ]&#59;
                L [ s0 ++ ] = x&#59;
            }
            else // 发现大数
            {   // 当前位置 右端数字(e位置) e0位置,三个数字向右轮换
                L [ e -- ] = L [ s0 ]&#59;
                L [ s0 ] = L [ e0 ]&#59;
                L [ e0 -- ] = x&#59;
            }
        }
        break&#59; // 这样交换完就满足要求了
    }
}

// s, e 分别指向 x 的起始和终止位置


以上程序手工录入,可能有错,欢迎指正

论坛徽章:
0
36 [报告]
发表于 2002-10-27 19:52 |只看该作者

挑战数组腾挪大法

[这个贴子最后由beggar在 2002/10/28 10:07am 编辑]

怎么那时我们学校开设的课程第一学期就有<C程序设计>;?
我不知道你那个&quot;朋友&quot;就是你本人,一人作事一人当.老套了.不过这样的人太多了.我想如果没有这些垃圾存在的话.现在中国的软件产业一定会非常发达.
再次声明.谭老先生的书面向没有一点儿基础的学习者,如果你目前还在看他的书我在想我有没有必要来和你计论这个数组问题.听道说没有一本书可以便随一个程序员的各个阶段.我还以为有一本书可以让人看了之后再也不用看其它书了.谢谢又让我明白一件事.

引用你的原文&quot;...明一下,本贴的前几个 aegis 回复不是我写的.是谁写的呢?就是看谭浩强的书学 c, 不得其法的那个朋友..&quot;
引用你所说的假aegis的原文&quot;菜鸟啊菜鸟,一群菜鸟!谭笨蛋的书看多了吧?只会照抄,没有一点脑子&quot;
你认为有这种白痴吗?自相茅盾.

再次,对原始数据的要求把责任归于命题不严??我不知道你有没有脑子.引用原文&quot;  ...元素,如何使得小于X的元素位于数组左边,大于X的元素位于数组右边,并得...&quot;,我想人家已经说得很清楚了.要说到分厘不差.有专门的行业----律师去作这些事情.

呵呵.你说的理论好复杂啊.我听不懂哦.看起来是个高手哦.
不过我可以告诉你或你朋友.你的程序是垃圾.写程式或你说的&quot;算法&quot;应该避免的问题你全用上了.不知道是否真的写过程序.还不知道能不能编译.

再次.请不要说用你这种&quot;算法&quot;来处理数据重复问题很容易.让菜鸟&quot;随便&quot;改,我希望你能把你的&quot;算法&quot;完整实现拿来学习一下.在此先表示感谢

论坛徽章:
0
37 [报告]
发表于 2002-10-27 19:52 |只看该作者

挑战数组腾挪大法

[这个贴子最后由beggar在 2002/10/28 01:09pm 编辑]

#include <sys/types.h>;
#include <stdio.h>;

#define DATA_LEN 10

main()
{
int total_mov_time=0&#59; //this value used for count the exchange times by program.
int loop,loopstart,looplen,tmp,found_loc&#59;
int big_count,equ_count,big_loc,equ_loc,test_num&#59;//the location of ori data.
int ori_data[DATA_LEN]={16838,5758,10113,17515,31051,5627,23010,7419,16212,4086}&#59;
looplen=DATA_LEN&#59;  //e.g: here is 10.
test_num=31051&#59; //try to find 31051
printf(&quot;===ori data===\n&quot&#59;
for(loop=0&#59;loop<looplen&#59;loop++)
printf(&quot;%d &quot;,ori_data[loop])&#59;
printf(&quot;\n&quot&#59;
loopstart=big_count=equ_count=big_loc=0&#59;
for(loop=loopstart&#59;loop<looplen&#59;loop++){
if(test_num>;ori_data[loop]){
if(big_count!=0){
//exchange small to big locate.
tmp=ori_data[big_loc]&#59;
ori_data[big_loc]=ori_data[loop]&#59;
ori_data[loop]=tmp&#59;
total_mov_time++&#59;
}
big_loc++&#59;
continue&#59;
}
if(test_num<ori_data[loop]){
big_count++&#59;
continue&#59;
}
if(test_num==ori_data[loop]){
tmp=ori_data[loop]&#59;
ori_data[loop]=ori_data[looplen-1]&#59;
ori_data[looplen-1]=tmp&#59;
equ_count++&#59;
looplen--&#59;
loop--&#59;
total_mov_time++&#59;
}
}

if(equ_count==0){
printf(&quot;ERROR:test number not found.\n&quot&#59;
exit(1)&#59;
}else{
found_loc=big_loc+1&#59;
equ_loc=DATA_LEN-equ_count&#59;
if(equ_count<big_count){
looplen=DATA_LEN&#59;
tmp=equ_loc&#59;
loopstart=big_loc&#59;
}else{
looplen=big_loc+big_count&#59;
tmp=big_loc&#59;
loopstart=DATA_LEN-big_count&#59;
}
for(loop=tmp&#59;loop<looplen&#59;loop++){
tmp=ori_data[loop]&#59;
ori_data[loop]=ori_data[loopstart]&#59;
ori_data[loopstart]=tmp&#59;
loopstart++&#59;
total_mov_time++&#59;
}
}
printf(&quot;\n===Total===\n&quot&#59;
printf(&quot;find data:%d location:%d foundtimes:%d total exchage:%d\nok\n&quot;,
test_num,found_loc,equ_count,total_mov_time)&#59;
printf(&quot;\n===Last array===\n&quot&#59;
for(loop=0&#59;loop<DATA_LEN&#59;loop++)
printf(&quot;%d &quot;,ori_data[loop])&#59;
printf(&quot;\n&quot&#59;
exit(0)&#59;
}

论坛徽章:
0
38 [报告]
发表于 2002-10-27 19:54 |只看该作者

挑战数组腾挪大法

[这个贴子最后由beggar在 2002/10/28 00:56am 编辑]

看得懂吗?不要说看不懂.

程序先把原始数据分成三部分,用下标将这三部分分开.
[ 比X小的 ] [ 比X大的 ] [ X ]

再将第三部分移到第二部分之前,完成处理.
[ 比X小的 ] [ X ] [ 比X大的 ]

这样作在处理数据较多时可以减少数据比较和移动的次数.
你可以把你那个&quot;算法&quot;和我的作1000个数time测试.
以上程式在FB上cc编译通过.

我希望你不要只是说叫菜鸟随便改改.贴出你的源程序来time test试试,让我们看看&quot;高手&quot;的算法实现啊

论坛徽章:
0
39 [报告]
发表于 2002-10-28 11:51 |只看该作者

挑战数组腾挪大法

感谢各位的关注!
1)关于这个帖子的一些意气之争,这不是本贴、也不是本坛所感兴趣的范围,有兴趣的网友看前面的帖子即可,这里不再做评论。
2)为了大家有一个集中主题、高效的交流平台,下面的以及随后的帖子将试图重新给出一个良好的讨论前提,并总结前面各位的发言。
3)欢迎任何有对题目表述,解答模板有建设性的批评与建议。


题目:给定一个数组long L[n],且已知X是其中的一个元素(等于X的元素可能不止一个),如何使得小于X的元素位于数组左边,大于X的元素位于数组右边,并得到X在数组中的下标。
要求:1)如何不使用额外的数组;2)时间复杂度不大于O(n)

提示:
A)为了能使讨论能集中于算法本身,而不是无关主旨的细节,下面附上模板程序,只需实现Partition函数即可验证程序是否正确。
B)如果大家能遵守一定的“八股”,我们沟通的效率会高些。(事实上,没有多少人可以有耐心看别人的代码,能看看算法以及测试结果就不错了,下面的“八股”直面这一事实)


八股:
一)算法说明
二)编译运行平台,机器类型/主频
三)Partition函数代码
四)至少给出前4组测试数据的结果
   i  )L[]={5,5,5,5,5,5,7,1,6,8}&#59; n=10&#59; X=5&#59; (在main函数提供,注释掉其中一些语句即可)
   ii )n=10的随机数组
   iii)n=10,000,000的随机数组
   iv )n=50,000,000的随机数组(小心,别影响别人工作)
   v  )其他自己认为具有“边界”特性的数组。


模板程序:
#include<string.h>;
#include<stdio.h>;
#include<stdlib.h>;
#include<time.h>;

#define DISPLAY \
   { \
   int i&#59; \
   printf(&quot;L[]=&quot&#59; \
   for (i = 0&#59; i < num&#59; i++){ \
      if (i >; 20 ){ \
          printf(&quot;...(too much number, ignore)&quot&#59; \
          break&#59; \
      } \
      printf(&quot;%d &quot;, L)&#59; \
   } \
   printf(&quot;\n&quot&#59; \
   }


int  Partition(long *L, int n, long X)&#59;  /* 实现这个函数 */
long *MakeRandomList(int *num)&#59;
long *CreateList(int n)&#59;
int  Check(long *L, int num, int X, int position)&#59;
int  SWAP(long *L, int a, int b)&#59;

int main()
{
  long X&#59;
  long *L&#59;
  //long L[]={5,5,5,5,5,5,7,1,6,8}&#59;
  int  num = 10&#59;
  int  pos = 0&#59;
  clock_t start_time, end_time&#59;
  double  duration&#59;

  
  L=MakeRandomList(&amp;num)&#59;
  X = L[(num - 1) / 2]&#59;
  
  printf(&quot;X=%ld\n&quot;, X)&#59;
  DISPLAY
  printf(&quot;-----------------------------------------------------------------\n&quot&#59;

  start_time=clock()&#59;  
  pos = Partition(L, num, X)&#59;
  end_time=clock()&#59;

  printf(&quot;Check :&quot&#59;  
  if (Check(L, num, X, pos))
      printf(&quot;incorrect.\n&quot&#59;
  else
      printf(&quot;correct.\n&quot&#59;
      
  duration = (double)(end_time - start_time) / CLOCKS_PER_SEC&#59;
  printf(&quot;Time  : %.3f seconds.\n&quot;, duration)&#59;
  
  printf(&quot;-----------------------------------------------------------------\n&quot&#59;
  printf(&quot;position=%ld\n&quot;, pos)&#59;
  DISPLAY
  
  free(L)&#59;
  return 0&#59;
}


/*
* L:输入数组
* n:数组大小
* X:X元素
* 返回X在数组中的下标
*/
int  Partition(long *L, int n, long X)
{
  //在这里插入你的代码
}


long *CreateList(int n)
/* Allocates memory for an array of n longs */
{
  long *L&#59;

  L = (long *) malloc( n * sizeof(long) )&#59;
  if( L == NULL) { /* Exit program if memory cannot be allocated */
     printf(&quot;\nCannot allocate enough memory for list.\n&quot&#59;
     exit(0)&#59;
  }

  return (L)&#59;
}

long *MakeRandomList(int *num)
{
  long *L&#59;               /* pointer to List */
  unsigned int seed&#59;     /* seed for random number generator */
  int i&#59;                 /* index of for loop */

  printf(&quot;\nNumber of elements to sort=>;&quot&#59;
  scanf(&quot;%d&quot;,num)&#59;

  printf(&quot;Seed for random number generator (integer)=>;&quot;)&#59;
  scanf(&quot;%d&quot;,&amp;seed)&#59;
  srand(seed)&#59;

  /* Allocate memory for # of elements. If memory cannot be allocated,
     display message and terminate program. Read in the elements. */
  L = CreateList(*num)&#59;
  if (L == NULL) {
     printf(&quot;\nCannot allocate enough memory for number list.\n&quot;)&#59;
     exit(0)&#59;
  }
  for (i = 0&#59; i < *num&#59; ++i)
     L = rand()&#59;

  return(L)&#59;           /* Return the List */
}

int Check(long *L, int num, int X, int position)
{
  int i, lpos = -1, rpos = -1&#59;
  
  for (i = 0&#59; i < num&#59; i++)
    if (L == X){
      lpos = i&#59;
      break&#59;
    }
    else if (L >; X)
      return -1&#59;

  for (i = num - 1&#59; i >;= 0&#59; i--)
    if (L == X){
      rpos = i&#59;
      break&#59;
    }
    else if (L < X)
      return -1&#59;
            
  if ((lpos == -1) || (rpos == -1) || (position < lpos) || (position >;rpos))
    return -1&#59;

  for (i = lpos&#59; i <=rpos&#59; i++)
    if (L != X)
      return -1&#59;

  return 0&#59;
}

int SWAP(long *L, int a, int b)
{
  long temp&#59;

  temp = L[a]&#59;
  L[a] = L&#59;
  L = temp&#59;
  return 0&#59;
}
Chinajiji 该用户已被删除
40 [报告]
发表于 2002-10-28 11:51 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP