经典面试题

来源:互联网 发布:excel数据条渐变填充 编辑:程序博客网 时间:2024/05/21 15:42

转自:http://blog.csdn.net/hackbuteer1/article/details/6886021
1、编程实现两个正整数的除法,当然不能用除法操作符。

view plain
  1. //编程实现两个正整数的除法,当然不能用除法操作符  
  2. int div(const int x, const int y)  
  3. {  
  4.     int left_num = x;  
  5.     int result = 0;  
  6.     int multi;  
  7.     while (left_num >= y)    //模拟小学学过的竖式除法运算  
  8.     {  
  9.         multi = 1;  
  10.         while (y * multi <= (left_num >> 1))  
  11.         {  
  12.             multi = multi << 1;  
  13.         }  
  14.         result += multi;  
  15.         left_num -= y * multi;  
  16.     }  
  17.     return result;  
  18. }  

2、现在有一个数组,已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数。

第1种方法:

创建一个hash_map,key为数组中的数,value为此数出现的次数。遍历一遍数组,用hash_map统计每个数出现的次数,并用两个值存储目前出现次数最多的数和对应出现的次数。
这样可以做到O(n)的时间复杂度和O(n)的空间复杂度,满足题目的要求。
但是没有利用“一个数出现的次数超过了一半”这个特点。也许算法还有提高的空间。
第2种方法(推荐):

使用两个变量A和B,其中A存储某个数组中的数,B用来计数。开始时将B初始化为0。   遍历数组,如果B=0,则令A等于当前数,令B等于1;如果当前数与A相同,则B=B+1;如果当前数与A不同,则令B=B-1。遍历结束时,A中的数就是要找的数。
这个算法的时间复杂度是O(n),空间复杂度为O(1)。

view plain
  1. int main(void)  
  2. {  
  3.     int i, A, B;  
  4.     int a[10] = {7,1,3,1,2,1,1,6,1,1};  
  5.       
  6.     A=a[5];  
  7.     B=0;  
  8.     for(i=0; i<10; i++)  
  9.     {  
  10.         if(B==0)  
  11.         {  
  12.             A = a[i];  
  13.             B = 1;  
  14.         }  
  15.         else if( A == a[i] )  
  16.             B++;  
  17.         else if(A != a[i])  
  18.             B--;  
  19.     }  
  20.     printf("%d\n", A);  
  21.     return 0;  
  22. }  

 3、求取字符串长度,不使用while、for等循环语句和字符串处理函数。

view plain
  1. int mystrlen(const char *str)  
  2. {  
  3.     if(*str=='\0')  
  4.         return 0;  
  5.     else  
  6.         return 1+mystrlen(str+1);  
  7. }  

4、两个单向链表,有可能交叉,请设计算法判断是否交叉,如果交叉,返回交叉点!算法复杂度o(n)
两个链表最后是合并成一个而不是交叉,所以:
(1)先找到p1,p2的最后一个节点,同时记录节点数量a,b;
(2)判断最后一个节点是否相同,

如果不相同则没相交。如果相同,则从第一个节点和|a-b|+1个节点开始比较,看是否相等,不相等都寻找下一个节点,直到找到交叉点。

如果两个单向链表有公共的结点,也就是说两个链表从某一结点开始,它们的m_pNext都指向同一个结点。但由于是单向链表的结点,每个结点只有一个m_pNext,因此从第一个公共结点开始,之后它们所有结点都是重合的,不可能再出现分叉。所以,两个有公共结点而部分重合的链表,拓扑形状看起来像一个Y,而不可能像X。

看到这个题目,第一反应就是蛮力法:在第一链表上顺序遍历每个结点。每遍历一个结点的时候,在第二个链表上顺序遍历每个结点。如果此时两个链表上的结点是一样的,说明此时两个链表重合,于是找到了它们的公共结点。如果第一个链表的长度为m,第二个链表的长度为n,显然,该方法的时间复杂度为O(mn)。

接 下来我们试着去寻找一个线性时间复杂度的算法。我们先把问题简化:如何判断两个单向链表有没有公共结点?前面已经提到,如果两个链表有一个公共结点,那么 该公共结点之后的所有结点都是重合的。那么,它们的最后一个结点必然是重合的。因此,我们判断两个链表是不是有重合的部分,只要分别遍历两个链表到最后一 个结点。如果两个尾结点是一样的,说明它们用重合;否则两个链表没有公共的结点。

在上面的思路中,顺序遍历两个链表到尾结点的时候,我们不能保证在两个链表上同时到达尾结点。这是因为两个链表不一定长度一样。但如果假设一个链表比另一个长l个结点,我们先在长的链表上遍历l个结点,之后再同步遍历,这个时候我们就能保证同时到达最后一个结点了。由于两个链表从第一个公共结点考试到链表的尾结点,这一部分是重合的。因此,它们肯定也是同时到达第一公共结点的。于是在遍历中,第一个相同的结点就是第一个公共的结点。

在这个思路中,我们先要分别遍历两个链表得到它们的长度,并求出两个长度之差。在长的链表上先遍历若干次之后,再同步遍历两个链表,知道找到相同的结点,或者一直到链表结束。此时,如果第一个链表的长度为m,第二个链表的长度为n,该方法的时间复杂度为O(m+n)。

基于这个思路,我们不难写出如下的代码:

view plain
  1. struct ListNode      //链表的结点  
  2. {  
  3.     int m_nKey;  
  4.     ListNode*   m_pNext;  
  5. };  
  6.   
  7. ///////////////////////////////////////////////////////////////////////  
  8. // Find the first common node in the list with head pHead1 and   
  9. // the list with head pHead2  
  10. // Input: pHead1 - the head of the first list  
  11. //        pHead2 - the head of the second list  
  12. // Return: the first common node in two list. If there is no common  
  13. //         nodes, return NULL  
  14. ///////////////////////////////////////////////////////////////////////  
  15. ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2)  
  16. {  
  17.     // Get the length of two lists  
  18.     unsigned int nLength1 = ListLength(pHead1);  
  19.     unsigned int nLength2 = ListLength(pHead2);  
  20.     int nLengthDif = nLength1 - nLength2;  
  21.   
  22.     // Get the longer list  
  23.     ListNode *pListHeadLong = pHead1;  
  24.     ListNode *pListHeadShort = pHead2;  
  25.     if(nLength2 > nLength1)  
  26.     {  
  27.         pListHeadLong = pHead2;  
  28.         pListHeadShort = pHead1;  
  29.         nLengthDif = nLength2 - nLength1;  
  30.     }  
  31.   
  32.     // Move on the longer list  
  33.     for(int i = 0; i < nLengthDif; ++ i)  
  34.         pListHeadLong = pListHeadLong->m_pNext;  
  35.   
  36.     // Move on both lists  
  37.     while((pListHeadLong != NULL) &&  (pListHeadShort != NULL) && (pListHeadLong != pListHeadShort))  
  38.     {  
  39.         pListHeadLong = pListHeadLong->m_pNext;  
  40.         pListHeadShort = pListHeadShort->m_pNext;  
  41.     }  
  42.   
  43.     // Get the first common node in two lists  
  44.     ListNode *pFisrtCommonNode = NULL;  
  45.     if(pListHeadLong == pListHeadShort)  
  46.         pFisrtCommonNode = pListHeadLong;  
  47.     return pFisrtCommonNode;  
  48. }  
  49.   
  50. ///////////////////////////////////////////////////////////////////////  
  51. // Get the length of list with head pHead  
  52. // Input: pHead - the head of list  
  53. // Return: the length of list  
  54. ///////////////////////////////////////////////////////////////////////  
  55. unsigned int ListLength(ListNode* pHead)  
  56. {  
  57.     unsigned int nLength = 0;  
  58.     ListNode* pNode = pHead;  
  59.     while(pNode != NULL)  
  60.     {  
  61.         ++nLength;  
  62.         pNode = pNode->m_pNext;  
  63.     }  
  64.     return nLength;  
  65. }  

5、在if里面请写入语句,使得打印出  Hello  World。

view plain
  1. int main(void)  
  2. {  
  3.     if()    //应该填入!printf("Hello "),会先打印出Hello,然后进行if()判断,!printf()取反就是0,所以不成立只能运行else,接着打印出World  
  4.     {  
  5.         printf("Hello ");  
  6.     }  
  7.     else  
  8.     {  
  9.         printf("World");  
  10.     }  
  11.     return 0;  
  12. }  

6、我们通常登陆或者注册要输入验证码,今天注册某个网站的验证码不是直接给出来的,它给出了一道程序,让我写出输出结果,题目如下:(输出:4321)

view plain
  1. int main(void)  
  2. {  
  3.     int i=43;  
  4.     printf("%d",printf("%d",printf("%d",i)));    //这个是嵌套的,应该先运行最里面的那个printf,输出43,然后printf返回2,在输出2后printf返回值为1,最后输出1  
  5.     return 0;  
  6. }  

printf函数返回一个int值,表示被打印的字符数。

view plain
  1. int main(void)  
  2. {  
  3.     int i = 43, m, n;  
  4.     m = printf("%d",i);         //printf函数打印43后,返回被打印的字符个数,2  
  5.     n = printf("%d\n",i);       //printf函数打印43及回车后,返回被打印的字符个数,3  
  6.     printf("%d %d\n",m,n);      //输出2、3  
  7.     return 0;  
  8. }  
view plain
  1. //double类型的例子  
  2. int main(void)  
  3. {  
  4.     int m, n;  
  5.     double i;  
  6.     i = 0.27;              //小数点后面不足6位的要补足6位  
  7.     m = printf("%lf",i);          //printf函数,返回被打印的字符个数,小数点后面6位加上0.共是8个字符  
  8.     n = printf("%lf\n",i);        //小数点后面6位加上回车,再加上0.共是9个字符  
  9.     printf("%d %d\n",m,n);  
  10.       
  11.     i = 345.27;   
  12.     m = printf("%lf",i);          //小数点后面6位加上345.共是10个字符  
  13.     n = printf("%lf\n",i);        //小数点后面6位加上回车,再加上345.共是11个字符  
  14.     printf("%d %d\n",m,n);  
  15.     return 0;  
  16. }  

7、百度面试题目,现在有1千万个随机数,随机数的范围在1到1亿之间。现在要求写出一种算法,将1到1亿之间没有在随机数中的数求出来。

view plain
  1. 解决办法:  
  2.   
  3. 一)用一个32位的整数32位表示32个数,1亿/32 = 3125000,使用3.125 * 4M byte空间即可保存1亿个数,即index[3125000].  
  4.   
  5. 二)对于数n,(n-1) / 32 为其在数组中的下标,table[(n - 1) % 32]与数组中下标(n-1)/32的值使用或操作。  
  6.   
  7. 三)表table中值为   table[ 0 ]=0x00000001,  
  8.   
  9.                                 table[ 1 ]=0x00000002,   
  10.   
  11.                                 ... ...  
  12.   
  13.                                 table[29]=0x20000000,  
  14.   
  15.                                 table[31]=0x80000000,   等这样的表示方式,具体的数值使用查表法加快速度。  
  16.   
  17. 四)最后算某值是否存在,使用与操作即可计算出。   
  18.   
  19. 数据存储比如:  
  20.   
  21. 第一个N=30是一个随机数,则存储可以表示为:index[(30-1)/32] = index[0] = index[0] || table[(30-1)%32] /*刚开始时候初始化index[32]={0}*/  
  22.   
  23.                                          =  0 || 0x20000000 = 0x20000000;  
  24.   
  25. 第二个N=31是一个随机数,则存储可以表示为:index[(31-1)/32] = index[0] = index[0] || table[(31-1)%32] /*第30位1,其他位为0*/  
  26.   
  27.                                          =  0x20000000 || 0x40000000 = 0x60000000;  
  28.   
  29. ... ...  
  30.   
  31. 依次类推,即可。   
  32.   
  33. 数据验证比如:  
  34.   
  35. 1. 当要查询30是否存在的时候,由于:(30-1)/32 = 0;(30-1)%32=29;我们只需要计算:index[0] & table[29] 是真还是假,就可以得出30是否存在。  
  36.   
  37. 2. 当要查询31是否存在的时候,由于:(31-1)/32 = 0;(31-1)%32=30;我们只需要计算:index[0] & table[30] 是真还是假,就可以得出31是否存在。  
  38.   
  39. ... ...  
  40.   
  41. 依次类推,即可。   
  42.   
  43. 小结:  
  44.   
  45.         通过分析此题目,首先这种思路和方法,在一定程度上用相对小的空间存储了大量的数据,节省了比较大的内存空间;在运算方面,位运算的速度相当来说效率是比较高的,因而也再一定程度上节省了时间复杂。  
  46.   
  47.         总之,这种存储方式和思维方式,在一定方面能够有效的解决海量数据存储与运算。基于此题目,凡是大量数据筛选,判断是否存在等问题,我们都可以借鉴此题目的思维和方法。  

8、判断点是否在多边形内的问题?  
我知道有两种方法:     
 1、累计角度法    
 过此点连接多边形的每一顶点,各相邻边角度之和为360度,则此点在多边形内。   否则为0度,在多边形外部。    
2、射线法    
过此点向任意角度发一条射线,若与多边形的各条边交点个数之和为偶数,则此点在多边形之外,否则在多边形之内。  若有交点为多边形顶点则要另选一条射线重算。       
请问哪种方法好一点(时间复杂度)?    
方法一对凹多边形可能会出现问题吧。

原创粉丝点击