• 微软面试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	",*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;
            }
        }


  • 相关阅读:
    luogu P1073 最优贸易 |分层图最短路
    luogu P1901 发射站 |单调队列
    luogu P1759 通天之潜水 |背包
    luogu P1801 【黑匣子_NOI导刊2010提高(06)】|堆+分块
    bzoj1642[Usaco2007 Nov]Milking Time 挤奶时间*
    bzoj1616[Usaco2008 Mar]Cow Travelling游荡的奶牛*
    bzoj1623[Usaco2008 Open]Cow Cars 奶牛飞车*
    bzoj1612[Usaco2008 Jan]Cow Contest奶牛的比赛*
    bzoj1639[Usaco2007 Mar]Monthly Expense 月度开支*
    bzoj1601[Usaco2008 Oct]灌水*
  • 原文地址:https://www.cnblogs.com/sooner/p/3247399.html
Copyright © 2020-2023  润新知