微软面试15道
1. 有一个整数数组,请求出两两之差绝对值最小的值, 记住,只要得出最小值即可,不需要求出是哪两个数。
方法1:暴力的方式。遍历所有的两个数的差,记录最小值。算法的复杂度O(n2)
方法2:两个数要想差的绝对值最小,肯定是需要两个数大小相近。故有思路:先对数组进行排序,然后遍历一遍,相邻的数相减,记录绝对值最小的数。
方法3:将现在的问题进行转化:
设这个整数数组是a1,a2,...,an
构造数组B=(b1,b2,...,bn-1)
b1 = a1-a2, b2 = a2-a3, b3 = a3-a4, ... bn-1 = an-1 - an
那么原数组中,任意两整数之差ai-aj(1<=i,j<=n)可以表示成
B中第i个到第j-1个元素的连续求和
例如b2+b3+b4 = (a2-a3) + (a3-a4) + (a4-a5) = a2-a5
O(n)构造出B序列后
用类似“最大子段和”算法求“最小绝对值子段和”
int GetMinAbsoluteSubsequence(int B[],int begin,int nLen) { int nGlobal=INT_MAX; int nSuffix=0; for(int i=begin; i<nLen; i++) { nSuffix+=B[i]; if(abs(nSuffix)<abs(nGlobal)) { nGlobal=nSuffix; } if(i+1<nLen) { if(nSuffix*B[i+1]>0) //何时清零 ? nSuffix=0; } } return abs(nGlobal); }
方法4:如果没有空间复杂度的限制啊,可以借助于桶排序的思想。
数组为a[]
遍历得到最大max,遍历得到最小min。
位图长度为abs(max)+abs(min),即为byte b[]
遍历a,遍历到a[i],则将b[a[i]-min]置为1;
然后遍历b,比较相邻两个为1的下标差值。
复杂度为O(abs(max)+abs(min))
ps:
直接用桶排序就可以了~~
考虑到可能有重复数字的情况,可以用两个bit位表示
#include<iostream> using namespace std; int MinErrorSuquence(int* a,int length) { int bigest1=0;//记录正数的最大值 int bigest2=0;//记录负数的最小值 int Error=65536;//用于比较 for(int i=0;i<length;i++)//bigest1为数组中最大的正数,bigest2为最小的负数 { if(a[i]>=0&&a[i]>bigest1) bigest1=a[i]; if(a[i]<0&&a[i]<bigest2) bigest2=a[i]; } int timesOfNumber[bigest1-bigest2];//定义每个整数出现的次数,存放在数组里面。。 //例如5出现2次,那么a[5]=2 for(int i=bigest2;i<=bigest1;i++) { timesOfNumber[i]=0; } for(int i=0;i<length;i++) { int tmp=a[i]; ++timesOfNumber[tmp]; } int index=0; for(int i=bigest2;i<=bigest1;i++)//排序 { for(int j=0;j<timesOfNumber[i];j++) { a[index]=i; ++index; } } for(int i=0;i<length-1;i++)//求差的最小值 { if(a[i+1]-a[i]<Error) Error=a[i+1]-a[i]; } return Error; } int main(void) { int a[9]={-2,-1,-6,5,-12,8,-4,-7,-3}; cout<<MinErrorSuquence(a,9)<<endl; for(int i=0;i<9;i++)//重新排列后的数组 cout<<a[i]<<endl; return0; }
2. 写一个函数,检查字符是否是整数,如果是,返回其整数值。(或者:怎样只用4行代码编写出一个从字符串到长整形的函数?)
思路:按位递归处理 每次处理最后一位 直到结束 ~~ 注意考虑第一个字符为负数的情况
int str2int(char* pstr,int len) { if (len > 1) { return pstr[0] == '-'?str2int(pstr,len - 1)*10 - (pstr[len - 1] - '0'):str2int(pstr,len - 1)*10 + (pstr[len - 1] - '0'); } else return pstr[0] == '-'?0:pstr[0] - '0'; }
3、给出一个函数来输出一个字符串的所有排列。
思路: DFS 排列
//num[] 为可用的字符 ch[i]用来标记是否已经放入num[i] void dfs(char ar[],int len,bool ch[],char num[],int dp) { if (dp == len) { for (int i = 0 ; i< len;++i) { cout<<ar[i]; } cout<<endl; return; } for (int i = 0;i < len;++i) { if (!ch[i]) //如果num[i]还没放入 { ch[i] = true; ar[dp] = num[i]; dfs(ar,len,ch,num,dp + 1); ch[i] = false; } } }
#include <stdio.h> #include <string.h> void swapElem( char * c1, char * c2 ) //! 交换两个元素 { char temp; temp = *c1; *c1 = *c2; *c2 = temp; } void permSort( char str[], int maxsize, int startIndex ) //! 全排列 { if ( startIndex == maxsize - 1 ) //! 最后只剩下一个元素的遍历咯,就输出吧! { puts( str ); return; } for ( int i = startIndex; i < maxsize; i++ ) //! { swapElem( &str[i], &str[startIndex] ); //! 注意此处交换后面的几个元素,需要用递归 permSort( str, maxsize, startIndex + 1 ); swapElem( &str[startIndex], &str[i] ); //! 记得要交换回来!!! } } int main() { char str[] = "12345"; int maxsize = strlen( str ); permSort( str, maxsize, 0 ); return 0; }
4、请编写实现malloc()内存分配函数功能一样的代码。
给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。
#include "stdio.h" #define MAX_SIZE 1000 /*仅有1000个字节可供申请*/ static char buf[MAX_SIZE]; /*开辟静态数组*/ void *mymalloc(unsigned int size); int main() { /*测试*/ int i; int *arry=(int *)mymalloc(10*sizeof(int)); for(i=0;i<10;i++) arry[i]=i+1; for(i=0;i<10;i++) printf("%d\t",*arry++); getch(); } void *mymalloc(unsigned int size) { static int count=0; if((count+size)>MAX_SIZE) /*申请空间不足,返回NULL*/ return NULL; else { int temp=count; count+=size; return (void *)((char *)buf+temp); } }
5、怎样编写一个程序,把一个有序整数数组放到二叉树中?
pTree t ; //! 给一个全局的tree来先 void putInBinTree( int a[], int low, int high, pTree t ) { if( low >= high ) { t->value = a[low]; t->lChild = NULL; t->rChild = NULL; return; } int mid = ( low + high ) / 2; t->value = a[mid]; if( mid – 1 >= low ) { putInBinTree( a, low, mid – 1, t->lChild ); } if( mid + 1 <= high ) { putInBinTree( a, mid + 1, high , t->rChild ); } }
6、怎样从顶部开始逐层打印二叉树结点数据?请编程。
思路:二叉树的层次遍历 利用队列 记录层次
//! ( tail – head + MaxNum ) % MaxNum == 实际元素个数 treeNode queue[100]; //! 节点队列 int head = 0; int tail = 1; queue[tail++] = rootTree; //! 先把头放进去 while( !queue.isEmpty()) { printf(“%d”, queue[++head]->value); if(queue[head]->lChild != NULL ) { queue[tail++] = queue[head]->lChild; } if(queue[head]->rChild != NULL ) { queue[tail++] = queue[head]->rChild; } }
7、怎样把一个链表掉个顺序(也就是反序,注意链表的边界条件并考虑空链表)?
void reverseLinkList( pLinkList ls ) { pLinkList head = NULL; linkList tempStack; int top = -1; head = ls->next; while( head != NULL ) { tempStack[++top] = head; head = head->next; } ls->next = head; While( top >= 0 ) { head = tempStack[top--]; head->next = NULL; head = head->next; } }
>>> 当然我现在又想到另一种方法,就是不需要额外的栈空间的,利用链表插入法:遍历每个元素都往头head后面插入,那么完成后就是倒置的了
pLinkList newHead = NULL; pLinkList head = ls->next; while( head != NULL ) { newHead->next = head; head = head->next; } //!那么最后 newHead 就是倒置的链表
Nod* rev(Nod* head) //head 当前 { Nod* pre =NULL; //pre 前续 Nod* nex = NULL; //nex 后续 while (head) { nex = head->next; head->next = pre; pre = head; head = nex; } return pre; }
8、请编写能直接实现int atoi(const char * pstr)函数功能的代码。
#include <iostream> #include <cassert> using namespace std; int Myatoi(const char *pstr) { assert(pstr != NULL); //判断不为空 const char *temp = pstr; while (*temp == ' ') // 去除开头的空字符 temp++; int result = 0; int flag = 1; // 正负值标志位 if(*temp == '-') //若为负数 { flag = -1; temp++; } while (*temp != '/0') { result = result * 10 + (*temp - '0'); temp++; } return result * flag; } int main() { const char* str1 = "1234"; const char* str2 = "-567"; const char* str3 = " 455"; const char* str4 = " -45677"; cout<<Myatoi(str1)<<endl; cout<<Myatoi(str2)<<endl; cout<<Myatoi(str3)<<endl; cout<<Myatoi(str4)<<endl; }
9、编程实现两个正整数的除法
编程实现两个正整数的除法,当然不能用除法操作符。
#include <iostream> using namespace std; /************************************************************************/ /* 函数功能:编程实现两个正整数的除法,当然不能用除法操作符 ×/ /************************************************************************/ int Mydiv(const int x, const int y) { int result = 0; int temp = x; while (temp >= y) { result++; temp = temp - y; } return result; } int main() { cout<<Mydiv(20, 4)<<endl; cout<<Mydiv(20, 7)<<endl; }
10、在排序数组中,找出给定数字的出现次数 比如 [1, 2, 2, 2, 3] 中2的出现次数是3次
思路:二分查找 找到后左右遍历 这样复杂度为O(n + s) ; 如果需要多次查找 还是建立hash比较好
网上有思路说 用二分查找起始点和终点的位置 再相减即可
#include <iostream> using namespace std; /************************************************************************/ /* 函数功能:在排序数组中,找出给定数字的出现次数 比如 [1, 2, 2, 2, 3] 中2的出现次数是3次。 陷阱:一个看到题,首先想到的方法是进行遍历统计,这样时间复杂度为O(N), 但是题目明确说明是“排序数组”,所以使用二分查找的方法分别找出给定数字 的开始和结束位置,最坏情况下时间复杂度为O(logn)。×/ /************************************************************************/ //函数功能:返回最后一个等于x的位置 int getUpper(int arr[], int length, int x) { int low = 0; int high = length - 1; int mid = 0; while (low <= high) { mid = (low + high) / 2; if(arr[mid] <= x) //向后查找 low = mid + 1; else high = mid - 1; } return high; } //函数功能:返回第一个等于x的位置 int getLower(int arr[], int length, int x) { int low = 0; int high = length - 1; int mid = 0; while (low <= high) { mid = (low + high) / 2; if(arr[mid] >= x) //向前查找 high = mid - 1; else low = mid + 1; } return low; } //函数功能:返回x的次数 int GetTimes(int arr[], int length, int x) { int low = getLower(arr, length, x); int high = getUpper(arr, length, x); return (high - low + 1); } int main() { int arr[] = {1, 2, 2, 2, 3}; cout<<GetTimes(arr, sizeof(arr)/sizeof(arr[0]), 2)<<endl;; }
11、平面上N个点,每两个点都确定一条直线,求出斜率最大的那条直线所通过的两个点(斜率不存在的情况不考虑)。时间效率越高越好。
3个点A,B,C,把它们的按x坐标排序。假设排序后的顺序是ABC,那么有两种情况:
1.ABC共线,则k(AB)=k(BC)=k(AC)2.ABC不共线,则ABC将形成一个三角形,那么k(AC)<max(k(AB), k(BC))
1.把N个点按x坐标排序。2.遍历,求相邻的两个点的斜率,找最大值。
斜率公式是:k = (y2 – y1) / (x2 – x1);
我的思路是:先按照X将所有的point进行排序,然后对他们进行遍历,由于数学上有讲过-à if 排序为A,B,C那么AB和BC中必然有一个比AC斜率大,一个比他小,所以需要进行计算,然后遍历推进直到点结束,最后找到最大!不知道还有么有更好的办法( 此办法不能保证是百分百正确,只是自己的想法而已、、、 )
首先我们假设已经有了point的结构体:
typedef struct point { int s_x; int s_y;s }point; point allPoint[N]; //! 有N个点 void getMaxSlopePoint( point allPoint[] ) { point A, B, C; double maxSlope = 0; point p1, p2; //! 此处的排序就不说了 for( i = 0; i < N - 2; i++ ) //! 由于下面 i+2 所以遍历到N-2就OK了!!! { A = allPoint[i]; B = allPoint[i+1]; C = allPoint[i+2]; double Kab = ( A.y – B.y ) / ( A.x – B.x ); double Kbc = ( B.y – C.y ) / ( B.x – C.x ); if( Kab > Kbc ) { maxSlope = Kab; p1 = a; p2 = b; } else { maxSlope = Kbc; p1 = b; p2 = c; } } }
请设计一个算法,当你从该数列中随意选取5个数值,判断这5个数值是否连续相邻。
注意:
- 5个数值允许是乱序的。比如: 8 7 5 0 6
- 0可以通配任意数值。比如:8 7 5 0 6 中的0可以通配成9或者4
- 0可以多次出现。
- 复杂度如果是O(n2)则不得分。
思路: 排序 如 8 7 5 0 6 排序后: 0 5 6 7 8 ; 得到0的个数 N0,非0的个数为 N1 ,非0个序列首尾 5 和 8,非0序列的长度 LEN = arr[4] - ar[非0第一个] + 1
若LEN - N1<= N0 则可以,否则 不能组成序列 O(NlogN) + O(N)
sort(arr,arr + n); //首先排序 int pos = 0; //pos 为0的个数 int num = arr[pos]; while (!num) //num为第一个非0值 { ++pos; num = arr[pos]; } if (arr[4] - num + 1 - 5 + pos<= pos) //非0值序列长度-已有元素 <= 0的元素 { cout<<"CAN"<<endl; } else cout<<"No"<<endl;
13、设计一个算法,找出二叉树上任意两个结点的最近共同父结点。复杂度如果是O(n2)则不得分。
思路:网上标准答案是LCA的算法 http://kmplayer.iteye.com/blog/604518
不过感觉对本人来说理解有点困难,如果真遇到面试这种题,O(n)的方法也不是没有,那就是找到两个结点的祖先序列VecA VecB, 其中,长的序列的多出的部分可以抛弃,因为
一定不是公共的部分,(举例,VecA > VecB , 则 VecA的前几个元素都可以删除),然后查找VecA和VecB的第一个相同元素就是最近公共祖先.
bool dfs(NODE* head,int val,vector<NODE*> &vp)//找val节点的祖先序列 { if (head == NULL) { return false; } if (head->val == val) { vp.push_back(head); return true; } if (dfs(head->lchild,val,vp)) { vp.push_back(head); return true; } else if (dfs(head->rchild,val,vp)) { vp.push_back(head); return true; } else return false; } // 1 2 4 -1 -1 5 -1 -1 3 6 7 -1 9 -1 -1 8 -1 -1 -1 int main() { NODE* head = creat_tree(); // cenci(head); // preDis(head); int a = 9, b = 8; vector<NODE*>vp; dfs(head,a,vp); vector<NODE*>vp2; dfs(head,b,vp2); int len = abs(int(vp.size() - vp2.size())); if (vp.size() > vp2.size()) { while (len--) { vp.erase(vp.begin()); } } else { while (len--) { vp2.erase(vp2.begin()); } } int pos = 0; while (vp[pos] != vp2[pos]) { ++pos; } cout<<vp[pos]->val<<endl;
14、一棵排序二叉树,令 f=(最大值+最小值)/2, 设计一个算法,找出距离f值最近、大于f值的结点。复杂度如果是O(n2)则不得分。
思路: 优化GP: 中序遍历,找到第一个大于f的节点即是 (因为一定存在比f大的数)
bool have = false; //是否找到大于f的数 int f = 4; //f void midDis(NODE* head) //中序遍历 { if (have||head == NULL) { return; } midDis(head->lchild); if (head->val > f) { cout<<head->val<<endl; have = true; return; } midDis(head->rchild); }
15、一个整数数列,元素取值可能是1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于N+1。 复杂度最好是O(n),如果是O(n2)则不得分。
思路: 位图排序?然后扫描 时间复杂度是O(n) 但是需要O(n)的空间
bool arr[n + 1]; //位图 memset(arr,0,sizeof(arr)); for (int i = 0;i < m;++i) //位图排序 { arr[ar[i]] = true; } int i = 1, j = n; //起点和终点(最小值 最大值) int ff = 1 + n; while (i < j) //O(n)遍历求和 { if (i + j < ff) ++i; else if (i + j > ff) --j; else { if (arr[i]&&arr[j]) { cout<<i<<"+"<<j<<"="<<ff<<endl; } ++i; --j; } }