面试算法题总结

来源:互联网 发布:收银软件pfpos 编辑:程序博客网 时间:2024/05/01 12:09

作为一个C++面试者,面试时难免少不了各种算法题,本人一边面试一边总结,把自己面到的算法题总结如下,希望自己能得到提升,也希望能帮助到一些面试者,有更好方法的希望能提出来供大家讨论:

面试题1    给定无序自然数数组,求最大连续自然数个数,时间复杂度为O(n)(阿里面试)

例如:[100,3,1,2]输出应该为3,最长连续自然数序列为:1,2,3 求解.[4,7,5,9,8]连续的串就是3,【1,100,5,4,2,3】是4,【7 3 2 4 6 5】答案是6

答案思路:

维持两个hash表tables: 
Start表,其中的条目都是如下格式(start-point,length),包含的某个连续序列起始数以及序列长度。 
End表,其中的条目都是如下格式(end-point,length),包含的某个连续序列结束数以及序列长度。  

扫描原始数组,做如下操作: 对于当前值value, 
判断value + 1是否存在于start表中。 
如果存在,删除相应的条目,创建一个新条目(value,length + 1),同时更新end表相应条目,结束数不变,该对应长度加一。
判断value - 1是否存在于end表中。 如果存在,删除相应的条目,创建一个新条目(value,length + 1),同时更新start表相应条目,开始数不表,该对应长度加一。 如果在两个表中都存在,则合并两个已经存在的连续序列为一个。将四个条目删除,新建两个条目,每两个条目代表一个连续序列。 如果都不存在,则只需要在两个表中创建一个新的长度为1的条目。 一直这样等到数组中所有元素处理完毕,然后扫描start表寻找length值最大的那个即可。 这里要达到O(n)时间复杂度,start表和end表都用hash表实现,而且必须满足相关操作查找/添加/删除能够在O(1)时间复杂度内完成。

实例分析: 
int[] input = {10,21,45,22,7,2,67,19,13,45,12, 11,18,16,17,100,201,20,101};  
初始化状态:
 Start table:{}
End table:{} 
开始遍历数组: 
10:两个数组中都不存在,添加条目。 
Start table:{(10,1)} 
End table:{(10,1)} 
21:两个数组中都不存在,添加条目。 
Start table:{(10,1),(21,1)} 
End table:{(10,1),(21,1)} 
45:两个数组中都不存在,添加条目。 
Start table:{(10,1),(21,1),(45,1)} 
End table:{(10,1),(21,1),(45,1)} 
22:22-1=21存在于end表中需要进行更新。 
Start table:{(10,1),(21,2),(45,1)} 
End table:{(10,1),(22,2),(45,1)} 
7:两个数组中都不存在,添加条目。 
Start table:{(10,1),(21,2),(45,1),(7,1)} 
End table:{(10,1),(22,2),(45,1),(7,1)} 
2:两个数组中都不存在,添加条目。 
Start table:{(10,1),(21,2),(45,1),(7,1),(2,1)} 
End table:{(10,1),(22,2),(45,1),(7,1),(2,1)} 
67:两个数组中都不存在,添加条目。 
Start table:{(10,1),(21,2),(45,1),(7,1),(2,1),(67,1)} 
End table:{(10,1),(22,2),(45,1),(7,1),(2,1),(67,1)} 
19:两个数组中都不存在,添加条目。
Start table:{(10,1),(21,2),(45,1),(7,1),(2,1),(67,1),(19,1)} 
End table:{(10,1),(22,2),(45,1),(7,1),(2,1),(67,1),(19,1)} 
13:两个数组中都不存在,添加条目。
Start table:{(10,1),(21,2),(45,1),(7,1),(2,1),(67,1),(19,1),(13,1)} 
End table:{(10,1),(22,2),(45,1),(7,1),(2,1),(67,1),(19,1),(13,1)} 
45:两个数组中都不存在,添加条目。 
Start table:{(10,1),(21,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,1),(13,1)} 
End table:{(10,1),(22,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,1),(13,1)} 
12:12+1=13存在start表中,更新。 
Start table:{(10,1),(21,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,1),(12,2)} 
End table:{(10,1),(22,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,1),(13,2)} 
11:11+1=12都存在,合并。 
Start table:{(10,4),(21,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,1)} 
End table:{(13,4),(22,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,1)} 
18:18+1=19存在start表中,更新。 
Start table:{(10,4),(21,2),(45,1),(45,1),(7,1),(2,1),(67,1),(18,2)} 
End table:{(13,4),(22,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,2)} 
16:都不存在,添加条目。 
Start table:{(10,4),(21,2),(45,1),(45,1),(7,1),(2,1),(67,1),(18,2),(16,1)} 
End table:{(13,4),(22,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,2),(16,1)} 
17:都存在,合并。 
Start table:{(10,4),(21,2),(45,1),(45,1),(7,1),(2,1),(67,1),(16,4)} 
End table:{(13,4),(22,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,4)} 
100:都不存在,添加条目。 
Start table:{(10,4),(21,2),(45,1),(45,1),(7,1),(2,1),(67,1),(16,4),(100,1)} 
End table:{(13,4),(22,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,4),(100,1)} 
201:都不存在,添加条目。 
Start table:{(10,4),(21,2),(45,1),(45,1),(7,1),(2,1),(67,1),(16,4),(100,1),(201,1)} 
End table:{(13,4),(22,2),(45,1),(45,1),(7,1),(2,1),(67,1),(19,4),(100,1),(201,1)} 
20:都存在,合并。 
Start table:{(10,4),(16,7),(45,1),(45,1),(7,1),(2,1),(67,1),(100,1),(201,1)} 
End table:{(13,4),(22,7),(45,1),(45,1),(7,1),(2,1),(67,1),(100,1),(201,1)} 
101:都存在,合并。 
Start table:{(10,4),(16,7),(45,1),(45,1),(7,1),(2,1),(67,1),(100,1),(201,1),(101,1)} 
End table:{(13,4),(22,7),(45,1),(45,1),(7,1),(2,1),(67,1),(100,1),(201,1),(201,1)}  
最后搜索start表,找到length值最大的,为7.连续自然数序列是:(16,17,18,19,20,21,22). 结束。


面试题2   设计个数据结构,方便的插入删除一个数并能方便的找出中位数(阿里面试)

           问题:   假如有一需求是这样的,它会较频繁的动态向数列中插入或删除数据,且又需要随时获取到数列的中位数,该如何设计数据结构和算法。

        先说下,中位数的定义:如下:

       所谓的是中位数是指:将数据按大小顺序排列起来,形成一个数列,居于数列中间位置的数叫中位数,且要分奇数与偶数两种情况来分析。

        1:如果总数个数是奇数,按从小到大的顺序,取中间的那个数

        2:如果总数个数是偶数,按从小到大的顺序,取中间那两个数的平均数

        例:数列:1、9、11中位数:9,而数列:1、9、11、39,则是(9+11)/2 = 10

 

答题思路:

思考过程1:

        由于中位数是位于中间位置,所以容易想到的思路就是,设计一种以中位数隔开,维护左右两边的数列,左边都是小于中位数的,右边都是大于中位数的数据结构

 

思考过程2:

        如何建立左右两边的数据结构,才能易于与中位数比较,又易于插入删除。

        显然,中位数的计算方式是,若数列为偶数,是左边的最大值和右边的最小值除2;若不是,也要维护这左右的最大值和右边的最大值,原因是,经常插入或删除,数据的总数,时常在奇数和偶数之间变换。

        所以,对于左边的数列,我们试图是设计出容易找到最小值的数据结构,同理,右边也一样,所以自然而然,就想到堆。即大顶堆和小顶堆。

 

得出简单结论:可以利用大堆和小堆来解决这个问题,大致过程如下:

 

1:用一个大顶堆存储数列中不大于中位数的元素

2:用一个小顶堆存储数列中不小于中位数的元素

 

      显然,这种方式读取中位数的时间复杂度为O(1),插入和删除元素(包括调整堆)的时间复杂度为:O(logN)。

三  求数组中两个元素差的最大值,要求必须是后面的元素减去前面的元素,O(N)时间复杂度O(1)空间复杂度

这道题的解决得益于求数组中连续元素的最大和的方法,那个问题剑指offer中有,不会做的可以自己去看。我的理解就是要想得到差值的最大值,那么肯定是要求当前值减去这个指前面的最小值,我们可以不停地更新这个差值和最小值,然后去遍历完一遍就可以了。这样就能做到遍历整个数组的时候,问题就解决了。达到了最快的速度O(N)

贴代码:

[cpp] view plaincopy
  1. int max_difference(const vector<int>& arr)  
  2. {  
  3.     if (arr.size()>=2)  
  4.     {  
  5.         int MIN=min(arr[0],arr[1]);  
  6.         int MAX_DIFFERENCE=arr[1]-arr[0];//第一个被求出的差值,暂时作为最大的差值  
  7.         for (vector<int>::size_type i=2;i<arr.size();i++)  
  8.         {  
  9.             if (arr[i]-MIN>MAX_DIFFERENCE)  
  10.             {  
  11.                 MAX_DIFFERENCE=arr[i]-MIN;  
  12.             }  
  13.             if (arr[i]<MIN)  
  14.             {  
  15.                 MIN=arr[i];//影响新的差值的关键在于已经找到的当前的最小元素是谁,只要将当前值减去这个最小元素就能更新MAX_DIFFERENCE  
  16.             }  
  17.         }  
  18.         return MAX_DIFFERENCE;  
  19.     }  
  20.     else  
  21.     {  
  22.         cout<<"size of arr is too small";  
  23.         return 0;  
  24.     }  
  25.       
  26. }  
  27. void give_result(int a[],int n)  
  28. {  
  29.     vector<int> arr(a,a+n);  
  30.     print(arr.begin(),arr.end());  
  31.     print(max_difference(arr));  
  32. }  
  33. int main( void )   
  34. {  
  35.     int a0[]={7,9,10};  
  36.     give_result(a0,3);  
  37.     int a1[]={10,9,7};  
  38.     give_result(a1,3);  
  39.     int a[]={3,10,1,9};  
  40.     give_result(a,4);  
  41.     int a2[]={3,9,2,10,1,8};  
  42.     give_result(a2,6);  
  43.     return 0;  
  44. }  

美团笔试题  给定一个凸四边形,如何判断一个点在这个凸四边形内还是外?

思路一:

如果一个点在这个凸四边形内,那么按照逆时针方向走的话,该点一定会在每一条的左边。
所以方法就是:按照逆时针方向遍历这个凸四边形的每一条边的两个顶点A(X1,Y1)和B(X2,Y2),然后判断给定点是否在AB矢量左边就可以了。
而判断一个点是否在一个矢量的左边,可以利用矢量叉积。
设A(x1,y1),B(x2,y2),给定点是C(x3,y3),构造两条矢量边:
AB=(x2-x1,y2-y1), AC=(x3-x1,y3-y1)
则AB和AC的叉积为(2*2的行列式):

|x2-x1, y2-y1|

|x3-x1, y3-y1|

值为:r = (x2-x1)(y3-y1) - (y2-y1)(x3-x1)
然后利用右手法则进行判断:
如果r > 0,则点C在矢量AB的左边
如果r < 0,则点C在矢量AB的右边
这样就可以判断点C与AB的相对位置了。然后按照逆时针方向,对凸四边形的每一条边都如此判断,如果每次都判断出C在左边,那么就说明这个点在凸多边形内部了。

思路二:

0
以那个点为交点划两条相互垂直的直线,如果与四边形有四个交点,则在四边形内,否则就在四边形外

原创粉丝点击