微软面试题4 (最长等差数列)

题:求随机数构成的数组中找到长度大于=3的最长的等差数列

输出等差数列由小到大: 

如果没有符合条件的就输出[0,0]

格式:

输入[1,3,0,5,-1,6]

输出[-1,1,3,5]

要求时间复杂度,空间复杂度尽量小

分析基本算法思路(采用动态规划思想):首先,只要得到数列的公差和一个首项就可以确定一个等差数列,因此我们要寻找最长等差数列的公差以及首项。其次,为了方便查找公差和首项,我们应该将原数组进行由小到大排序,这样各两数之间的公差也是成递增形势的,这样我们就可以避免回溯查找首项

因此,在搜寻公差及首项的过程中,我们可以分两三个决策阶段:

1、如果公差为0,应该做何处理。

2、如果公差不为0,应该做何处理。

3、如果找到的数列长度是当前最长的做相应的处理

      针对以上情况,我们应该选择一种合适的数据结构——平衡排序树,stl中的set,map,mutiset,multimap是以红黑树结构为形势的容器,无疑是非常合适的,根据题目情况,可能存在具有相同元素的数组,因此我们选择multiset,这样无论我们对数据进行插入排序,查找都是比较高效的,因此总体上是可以满意的。

      最后有几项时间上的优化不在这里说明,详情可看代码。若有不足之处,望能不吝指出!^_^


我的实现代码:


  1. /** 
  2. Author:花心龟 
  3. Blog:http://blog.csdn.net/zhanxinhang 
  4. **/  
  5. #include <iostream>  
  6. #include <ctime>  
  7. #include <set>  
  8. using namespace std;  
  9.   
  10. void show_longest_seq(const multiset<int>& myset)  
  11. {  
  12.     int maxLength = 0, curr_pos = 0, curr_d = 0, counter=0,i=0; //一些辅助变量  
  13.     int d_result, a1_result; //存储最长等差数列的公差以及首项  
  14.     multiset<int>::const_iterator set_it1,set_it2;  
  15.   
  16.       
  17.     /* 
  18.          (主题)寻找长度最长的等差数列,最坏情况下时间复杂度为O(n^3) 
  19.     */  
  20.     for(set_it1 = myset.begin(); set_it1 != myset.end();)  
  21.     {  
  22.         for(set_it2=set_it1,set_it2++; set_it2 != myset.end();)//第二层循环从set_it1所指的下一个元素开始遍历  
  23.         {  
  24.             curr_d = *set_it2 - *set_it1; //算得当前公差,注意由于set为自排序容器,从小到大排列,所以curr_d恒为正  
  25.   
  26.             if(curr_d == 0) // 如果公差为0  
  27.             {  
  28.                 counter = myset.count(*set_it1);  
  29.                 set_it2 = myset.upper_bound(*set_it1);//(优化项)跳过与set_it1相等的元素  
  30.             }  
  31.             else  
  32.             {  
  33.                 counter = 2; //(优化项)最小长度要求要不小于所以直接从开始累加  
  34.                 while(myset.find(*set_it1 + counter*curr_d) != myset.end()) //计算数列项个数  
  35.                     ++counter;  
  36.   
  37.                 set_it2 = myset.upper_bound(*set_it2);// (优化项)跳过与*set_it2相等的元素  
  38.             }  
  39.   
  40.               
  41.             if(counter > maxLength)  //如果新数列长度大于maxLength  
  42.             {  
  43.                 d_result = curr_d;  
  44.                 a1_result = *set_it1;  
  45.                         maxLength = counter;  
  46.             }  
  47.         }  
  48.   
  49.         curr_pos += myset.count(*set_it1);     //计算第一层循环遍历到的当前位置  
  50.         if(myset.size()-curr_pos < maxLength)  // (优化项)如果集合中剩下的元素小于最大数列长度,就退出循环  
  51.             break;  
  52.   
  53.         set_it1 = myset.upper_bound(*set_it1); //下一次set_it1 的位置,并跳过相同元素  
  54.     }  
  55.   
  56.   
  57.   
  58.   
  59.     /* 
  60.        打印最长等差数列 
  61.     */  
  62.     if(maxLength <= 2)  
  63.     {  
  64.         cout<<"longest_seq:[0,0]"<<endl;  
  65.     }  
  66.     else  
  67.     {  
  68.         cout<<"longest_seq:";  
  69.           
  70.         for(i = 0; i<maxLength;  i++)  
  71.             cout<<*(myset.find(a1_result + i*d_result))<<' ';  
  72.   
  73.         cout<<endl;  
  74.     }  
  75. }  
  76. //Blog:http://blog.csdn.net/zhanxinhang  
  77.  test in main  
  78. int main()  
  79. {  
  80.     int a[]={1,3,0,5,-1,6};  
  81.     multiset<int> myset;  
  82.     myset.insert(a,a+6);  
  83.     show_longest_seq(myset);  
  84.     cout<<endl;  
  85.   
  86.     int l;  
  87.     srand((unsigned)time(NULL));  
  88.     for(int j = 0; j < 5; j++)  
  89.     {  
  90.         myset.clear();  
  91.         cout<<"input:[ ";  
  92.         l=rand()%10;  
  93.         for(int i = 0; i < l; ++i)  
  94.         {  
  95.             int element = rand()%10;  
  96.             myset.insert(element);  
  97.             cout<<element<<' ';  
  98.         }  
  99.         cout<<']'<<endl;  
  100.         show_longest_seq(myset);  
  101.         cout<<endl;  
  102.     }  
  103.   
  104.   
  105.     return 0;  
  106. }  


附一张测试结果图:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值