Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1170418
  • 博文数量: 173
  • 博客积分: 4048
  • 博客等级:
  • 技术积分: 2679
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-12 18:53
文章分类

全部博文(173)

文章存档

2018年(1)

2016年(1)

2013年(1)

2012年(118)

2011年(52)

分类: C/C++

2012-05-03 15:05:09

    某笔试题:内存中有一个长数组,有100W条记录, 每个记录为一个struct array, sizeof( array ) = 512, 在这个struct里有一个int型成员变量weight, 现要取得按个weight值从大到小排序的前500个数组单元(求N里的前K个大的数)

 

直接贴代码吧,废话少讲(先说方法吧)~~~~解释在注释里:)

 

const static long N = 10000000; 
const static long K = 100;
static long cnt;


struct Elem
{
 Elem()
 {
  weight = rand()%100000;
 }
 long weight;
 //char data[512];
};

void main()
{
 srand(time(NULL));

 //直接用数据会越过程序初始栈大小,所在要在堆里申请空间~~~运行时可以断一下点,看一下任务管理器占用多少内存
 Elem  * p = new Elem[N];
 long take = GetTickCount();  
 //time_t   first,   second; 
 //first   =   time(NULL);     /*   Gets   system */

 //方法一
 //先将最大的k个交换到前面,再对其全排
 DirectSearch( p, K );
 QuickSort( p, 0, K - 1 );
 PrintElem( p, K );


 //方法二
 //先用半快排,求出长度为k的区间后,再对其全排
//  PartQuickSort( p, 0, N - 1, K );
//  QuickSort( p, 0, K - 1 );
//  PrintElem( p, K );


 //方法三
 //用一个小根堆保存这k个数, 遍历一次即可,每次与第一个比较, 如果更新了就调整
 //HeapK( p, K );
 
 //估计做出以上三种方法的实现,面试官都基本满意啦,一般软件公司的offer也是八九不离十了.
 //但是,继续还有方法...

 //解法四: 如果加上限制条件(1) 所有数为整数 (2) 所有数的变化范围不大 这样就可以利用记数排序法的思想
 //Countsort( p, K )
 
 //second   =   time(NULL);   /*   Gets   system   time    again   */
 cout<<"tick="<< GetTickCount() - take < cout<<"count="<< cnt <

 //cout<<"tick="<< difftime(second,first)< 
 delete p;
}

 

 

下面者是实现~~~

 

  1. //解法一: 直接查找 O( N*k), 其实当 k < log2(N) 时是可以用的, 因为要完全排序需要 O( n* log2(n) )  
  2. //但是如果要求k个数排序的话,还要至少加上 k*log2(k)的时间  
  3. //直接将其交换到前面位置  
  4. void DirectSearch( Elem * src, int k )  
  5. {  
  6.   
  7.     long i =0, j, l,idx;  
  8.   
  9.     long max;  
  10.     Elem t;  
  11.     for( l=0; l
  12.     {  
  13.         max = src[l].weight;  
  14.         for( j=l; j 
  15.         {  
  16.             if( max < src[j].weight )  
  17.             {  
  18.                 max = src[j].weight;  
  19.                 idx = j;  
  20.             }  
  21.   
  22.             //计数器  
  23.             ++cnt;  
  24.         }  
  25.   
  26.         //交换到前面  
  27.         memcpy( &t, &src[idx], sizeof(Elem) );  
  28.         memcpy( &src[idx],  &src[l], sizeof(Elem) );  
  29.         memcpy( &src[l],  &t, sizeof(Elem) );  
  30.   
  31.     }  
  32.   
  33. }  
  34.   
  35. //解法二: 部分快排  
  36.   
  37. //前置声明  
  38. int PartPartion( Elem * src, int start, int end );  
  39.   
  40. //部分快排,实现将前K个最大的放在前端(N个无素无序)  
  41. //完成后前k个最大数将会放在数组的前k的位置,无序  
  42. void PartQuickSort( Elem * src, int start , int end, int k  )  
  43. {  
  44.     //按照快排的思想,以第一个为标记元素M,大于M的放交换到左边,小于M放到右边, 当左分区的要少于N时结束  
  45.     //返回上一次分区的大小  
  46.     //---------------  
  47.     //其实上面的想法,会导致分组过大的,如要在10个里找出前5个,当计算到m为3时,但前一个可能为8,  
  48.     //这里就直接全排这个8个元素就会做很我多无用的排序了  
  49.     //优化:   
  50.     //(1) 当找到 m < N 时, 继续在大的分区找出 N - m个  
  51.     //(2)递归进行,真到有m == N 时结束   
  52.     //(3)算法复杂度为O(N* log2(k) )  
  53.   
  54.     //if( k == 0 )  
  55.         //return;  
  56.   
  57.     int m = 0;  
  58.     //m = PartPartion( src, cur , ElemLen - 1 );  
  59.     //if( m <= k )  
  60.         //PartQuickSort( src + m + 1, ElemLen - m -1 , k - m - 1, m + 1);  
  61.   
  62.     if( start < end )  
  63.     {  
  64.         m = PartPartion( src, start , end );  
  65.   
  66.         if( m <= k )  
  67.             PartQuickSort( src, m+1 , end, k - m + 1 );  
  68.         else  
  69.             PartQuickSort( src, start , m - 1, k );  
  70.   
  71.     }  
  72.   
  73. }  
  74. //部分快排的分解  
  75. int PartPartion( Elem * src, int start, int end )  
  76. {  
  77.     Elem t, mid;  
  78.   
  79.     int i,j;  
  80.       
  81.     i = start;  
  82.     j = end;  
  83.     mid = src[start];  
  84.       
  85.     while( i< j )  
  86.     {  
  87.         while( i
  88.             j--;  
  89.   
  90.         while( i < j && src[i].weight >= mid.weight )   
  91.             i++;  
  92.   
  93.         if( i
  94.         {  
  95.             //如果Elem结构里有数组,就不能直接赋值,所以改用memcpy直接复制内存  
  96.             memcpy( &t, &src[i], sizeof(Elem) );  
  97.             memcpy( &src[i],  &src[j], sizeof(Elem) );  
  98.             memcpy( &src[j],  &t, sizeof(Elem) );  
  99.         }  
  100.     }  
  101.   
  102.     memcpy( &t, &src[i], sizeof(Elem) );  
  103.     memcpy( &src[i],  &src[start], sizeof(Elem) );  
  104.     memcpy( &src[start],  &t, sizeof(Elem) );  
  105.     ++cnt;  
  106.   
  107.     return i;  
  108. }  
  109. //对前N个元素进行快排  
  110. void QuickSort( Elem * src, int start , int end  )  
  111. {  
  112.     //完全排序  
  113.     if( start < end )  
  114.     {  
  115.         int m = PartPartion( src, start , end );  
  116.         QuickSort( src, start, m -1 );  
  117.         QuickSort( src, m +1, end );  
  118.     }  
  119. }  
  120.   
  121. //解法三: 将一个K个数据的堆,遍历一次即可,以最小堆形式,这样只要比较第一个元素F, 如果比F大,就替找F,之后调整堆  
  122. //同样算法复杂度为 O(N* log2(k) ), 而且实现简单  
  123.   
  124. //前置声明  
  125. bool AdjHeap( int * WArray, int i, int len );  
  126. void MinHeap( int * WArray, int len );  
  127. void HeapSort( int * WArray, int len );  
  128.   
  129. void HeapK( Elem * src, int k )  
  130. {  
  131.     //所有元素以一个数组表示, 再按堆操作调整  
  132.     //这里只保存weight值  
  133.     int * weight = new int[ k ];  
  134.     memset( weight, 0, k );  
  135.   
  136.     long i;  
  137.     int idx = 0;  
  138.   
  139.     //先将前k 个元素的weight加入到堆中  
  140.     for( i=0; i
  141.         weight[i] = src[i].weight;  
  142.   
  143.     //调整为小根堆,方便比较  
  144.     MinHeap( weight, k );  
  145.   
  146.   
  147.     //遍历一次即可  
  148.     for( i=k; i
  149.     {  
  150.         if( weight[0] < src[k].weight )  
  151.         {  
  152.   
  153.             //每置换一次堆顶就要重新调整  
  154.             weight[0] = src[k].weight;  
  155.             AdjHeap( weight, 0, k - 1 );  
  156.   
  157.         }  
  158.     }  
  159.   
  160.   
  161.     //最后weight数组为前k个最大的元素,如果要排序输出,那么要做一次堆排序  
  162.     HeapSort( weight, k  );  
  163.   
  164.      for( i=0; i
  165.         cout<< weight[i] << " ";  
  166.      cout<
  167.   
  168.   
  169.   
  170.     delete weight;  
  171. }  
  172.   
  173. void HeapSort( int * WArray, int len )  
  174. {  
  175.     int i;  
  176.     int t;  
  177.     for( i=0; i
  178.     {  
  179.   
  180.         t = WArray[0];  
  181.         WArray[0] = WArray[len - i -1];  
  182.         WArray[len - i - 1] = t;  
  183.           
  184.         //当没有调整时,完成排序  
  185.         AdjHeap( WArray, 0, len - i - 2 );  
  186.               
  187.     }  
  188. }  
  189.   
  190.   
  191. bool AdjHeap( int * WArray, int i, int len )  
  192. {  
  193.     bool change = false;  
  194.     int j,t;  
  195.     for( j =i*2+1; j <= len; j = j*2+1 )  
  196.     {  
  197.   
  198.         if( j +1 <= len && WArray[j] > WArray[ j +1 ] )  
  199.             j++;  
  200.   
  201.         if( WArray[j] < WArray[(j-1)/2] )  
  202.         {  
  203.             change = true;  
  204.             t = WArray[(j-1)/2];  
  205.             WArray[(j-1)/2] = WArray[j];  
  206.             WArray[j] = t;  
  207.         }  
  208.           
  209.     }  
  210.       
  211.     return change;  
  212. }  
  213.   
  214. void MinHeap( int * WArray, int len )  
  215. {  
  216.     if( len <= 0 )  
  217.         return;  
  218.       
  219.     //根据堆的性质: 左孩子为 2i +1 , 右孩为 2i + 2( 因为从0开始计数 )  
  220.       
  221.     int i;  
  222.       
  223.       
  224.     //从最后一个有孩子的结点开始,向前调整  
  225.     len--;  
  226.     for( i = (len-1)/2; i >=0; i-- )  
  227.         AdjHeap( WArray, i, len );  
  228.       
  229. }  
  230.   
  231.   
  232. //解法四: 如果加上限制条件(1) 所有数为整数 (2) 所有数的变化范围不大 这样就可以利用记数排序法的思想  
  233. //遍历一次N, 找出最大值MAX  
  234. //开一个数组 T[MAX];   
  235. //再遍历一次N, 对每个数计数 T[ A[i] ]++; ( 0<= i <= N )   
  236. //由于是求前K个最大的数,所以就可以从后面起取  
  237.   
  238. void Countsort( Elem * src, int k )  
  239. {  
  240.     long i,j;  
  241.     int max;  
  242.     int wt;  
  243.     int * kArray = new int[k];  
  244.     long take = GetTickCount();    
  245.     //遍历一次N, 找出最大值MAX  
  246.     max = src[0].weight;  
  247.     for( i=0; i
  248.     {  
  249.         wt = src[i].weight;  
  250.         max < wt  ? max = wt:max;  
  251.     }  
  252.     //开一个数组 T[MAX];   
  253.     int * T = new int[ max + 1 ];  
  254.     memset( T, 0, sizeof(int)*(max + 1) );  
  255.     //再遍历一次N, 对每个数计数 T[ A[i] ]++; ( 0<= i <= N )   
  256.     for( i=0; i
  257.     {  
  258.         wt = src[i].weight;  
  259.         T[ wt ]++;  
  260.     }  
  261.       
  262.     //由于是求前K个最大的数,所以就可以从后面起取  
  263.     int n = k;  
  264.     int idx = 0;  
  265.     for( i = max; i>0 ; i-- )  
  266.     {  
  267.         //跳过没有的数  
  268.         if( !T[i] )  
  269.             continue;  
  270.         if( T[i] >= n )  
  271.         {  
  272.             //保存结果在另一个数组,以免影响计算时间  
  273.             for( j =0; j
  274.             {  
  275.                 kArray[idx++] = i;  
  276.                 //cout<  
  277.             }  
  278.             break;  
  279.         }  
  280.         else  
  281.         {  
  282.             //输出这么多个计数  
  283.             for( j =0; j
  284.             {  
  285.                 kArray[idx++] = i;  
  286.                 //cout<  
  287.             }  
  288.             n -= T[i];  
  289.         }  
  290.     }  
  291.     //输出结果  
  292.     for( j =0; j
  293.         cout<" ";  
  294.     cout<
  295.     cout<<"tick="<< GetTickCount() - take <
  296.     delete T;  
  297.     delete kArray;  
  298. }  
  299.   
  300. //输出  
  301. void PrintElem( Elem * src, int Len )  
  302. {  
  303.     forint i =0; i< Len; i++ )  
  304.         cout<" ";  
  305.     cout<
  306. }  

   

    呵呵,相信在面试时能详细说出以上几钟算法的优缺点,并现场写出一两个,估计他会当场拍板:我找的就是你!

呼~~~当时就是衰在这个上啦, 所以决定完整的做一遍, 基础还是很重要的!!大家也加油啦~~~

阅读(4783) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~