微软面试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))
其中k()表示求斜率。
所以程序的基本步骤就是:
1.N个点按x坐标排序。
2.遍历,求相邻的两个点的斜率,找最大值。
时间复杂度Nlog(N)

斜率公式是:k = (y2 – y1) / (x2 – x1);

我的思路是:先按照X将所有的point进行排序,然后对他们进行遍历,由于数学上有讲过-à if 排序为A,B,C那么ABBC中必然有一个比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;
          }
      }
}
12、一个整数数列,元素取值可能是0~65535中的任意一个数,相同数值不会重复出现。0是例外,可以反复出现。
请设计一个算法,当你从该数列中随意选取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<= N则可以,否则 不能组成序列  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;
        }
    }

 



posted @ 2013-08-09 09:37  一枚程序员  阅读(616)  评论(0编辑  收藏  举报