经典面试题

1、编程实现两个正整数的除法,当然不能用除法操作符。

//编程实现两个正整数的除法,当然不能用除法操作符 int div(const int x, const int y) { int left_num = x; int result = 0; int multi; while (left_num >= y) //模拟小学学过的竖式除法运算 { multi = 1; while (y * multi <= (left_num >> 1)) { multi = multi << 1; } result += multi; left_num -= y * multi; } return result; }

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)。

int main(void) { int i, A, B; int a[10] = {7,1,3,1,2,1,1,6,1,1}; A=a[5]; B=0; for(i=0; i<10; i++) { if(B==0) { A = a[i]; B = 1; } else if( A == a[i] ) B++; else if(A != a[i]) B--; } printf("%d\n", A); return 0; }

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

int mystrlen(const char *str) { if(*str=='\0') return 0; else return 1+mystrlen(str+1); }

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)。

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

struct ListNode //链表的结点 { int m_nKey; ListNode* m_pNext; }; /// // Find the first common node in the list with head pHead1 and // the list with head pHead2 // Input: pHead1 - the head of the first list // pHead2 - the head of the second list // Return: the first common node in two list. If there is no common // nodes, return NULL /// ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) { // Get the length of two lists unsigned int nLength1 = ListLength(pHead1); unsigned int nLength2 = ListLength(pHead2); int nLengthDif = nLength1 - nLength2; // Get the longer list ListNode *pListHeadLong = pHead1; ListNode *pListHeadShort = pHead2; if(nLength2 > nLength1) { pListHeadLong = pHead2; pListHeadShort = pHead1; nLengthDif = nLength2 - nLength1; } // Move on the longer list for(int i = 0; i < nLengthDif; ++ i) pListHeadLong = pListHeadLong->m_pNext; // Move on both lists while((pListHeadLong != NULL) && (pListHeadShort != NULL) && (pListHeadLong != pListHeadShort)) { pListHeadLong = pListHeadLong->m_pNext; pListHeadShort = pListHeadShort->m_pNext; } // Get the first common node in two lists ListNode *pFisrtCommonNode = NULL; if(pListHeadLong == pListHeadShort) pFisrtCommonNode = pListHeadLong; return pFisrtCommonNode; } /// // Get the length of list with head pHead // Input: pHead - the head of list // Return: the length of list /// unsigned int ListLength(ListNode* pHead) { unsigned int nLength = 0; ListNode* pNode = pHead; while(pNode != NULL) { ++nLength; pNode = pNode->m_pNext; } return nLength; }

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

int main(void) { if() //应该填入!printf("Hello "),会先打印出Hello,然后进行if()判断,!printf()取反就是0,所以不成立只能运行else,接着打印出World { printf("Hello "); } else { printf("World"); } return 0; }

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

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

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

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值