文章来源:http://blog.youkuaiyun.com/hackbuteer1/article/details/6886021
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};
- 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是否存在。
- ... ...
- 依次类推,即可。
- 小结:
- 通过分析此题目,首先这种思路和方法,在一定程度上用相对小的空间存储了大量的数据,节省了比较大的内存空间;在运算方面,位运算的速度相当来说效率是比较高的,因而也再一定程度上节省了时间复杂。
- 总之,这种存储方式和思维方式,在一定方面能够有效的解决海量数据存储与运算。基于此题目,凡是大量数据筛选,判断是否存在等问题,我们都可以借鉴此题目的思维和方法。
我知道有两种方法:
1、累计角度法
过此点连接多边形的每一顶点,各相邻边角度之和为360度,则此点在多边形内。 否则为0度,在多边形外部。
2、射线法
过此点向任意角度发一条射线,若与多边形的各条边交点个数之和为偶数,则此点在多边形之外,否则在多边形之内。 若有交点为多边形顶点则要另选一条射线重算。
请问哪种方法好一点(时间复杂度)?
方法一对凹多边形可能会出现问题吧。