微软前端社招笔试详解

又到了每一年的金三银四招聘旺季,有幸得到了微软的笔试机会,程序猿们应该都知道,老美的公司都喜欢怼数据结构与算法,微软确定也不例外,我的以为考数据结构对每个应聘者都公平,我此次投的是微软苏研院,笔试考察的不难,是最最多见的数据结构算法裸题,这里记录一下,也给出解法。前端

1 快速排序

快排应该是最网红的题了,从校招到社招,从后端到前端再到移动端,都会问,可是估计能手撕出来的不超过一半,能讲清楚思路的估计不超过一半的一半,其实用两个词归纳,就是双指针+递归分治,在每一轮递归的时候找到每一个基准数排完序后的位置,将小于这个基准数的全部数放到它的左边,将大于这个基准数的全部数放到它的右边,而后再分别递归它左边的数组与它右边的数组。好比说数组[2,3,1,5,6,4]:算法

初始状态是这样,我如今给两个指针出来,一个指针指向头,即arr[0] = 2, 一个指针指向尾,及arr[5] = 4,规定一个基准数,这里直接用第一个数(arr[0] = 2)便可。开始第一次的递归处理,尾指针先从右往左扫,扫到第一个小于(注意是小于,而不是小于等于哦)基准数的位置停住,这时候头指针再从左往右扫,扫到第一个大于基准数的位置停住,这时候是下面的图示状态:

交换两个指针所指的数,成为了下面的状态:

两个数交换完毕,右指针此时指的是arr[2] = 3, 左指针指着arr[1] = 1;交换完毕后右指针继续从当前位置往左扫,扫到1的时候发现和左指针相遇了,那么这个时候就结束左右指针的扫描,左右指针同时指着arr[1] = 1,即:

此时退出循环扫描的过程,交换基准数与左右指针同时所指的数的位置,开头说了,基准数我选择的是arr[0] = 2, 指针指的是arr[1] = 1; 交换事后就变成了:

这时候就发现基准数已经出如今了它排完序后应该在的位置(排完序后是[1,2,3,4,5,6],2出如今了第2位),比这个基准数小的数组出如今了它的左边([1]出如今了2的左边),比基准数大的出如今了它的右边([3,5,6,4]出如今了2的右边)。以后的过程就是对左右数组的分别递归处理。附上代码:

function quickSort(arr, begin, end) {
           //递归出口
           if(begin >= end)
               return;
           var l = begin; // 左指针
           var r = end; //右指针
           var temp = arr[begin]; //基准数,这里取数组第一个数
           //左右指针相遇的时候退出扫描循环
           while(l < r) {
               //右指针从右向左扫描,碰到第一个小于基准数的时候停住
               while(l < r && arr[r] >= temp)
                 r --;
               //左指针从左向右扫描,碰到第一个大于基准数的时候停住
               while(l < r && arr[l] <= temp)
                 l ++;
               //交换左右指针所停位置的数
               [arr[l], arr[r]] = [arr[r], arr[l]];
           }
           //最后交换基准数与指针相遇位置的数
           [arr[begin], arr[l]] = [arr[l], arr[begin]];
           //递归处理左右数组
           quickSort(arr, begin, l - 1);
           quickSort(arr, l + 1, end);
       }

       var arr = [2,3,4,1,5,6]
       quickSort(arr, 0, 5);
       console.log(arr)
复制代码

思考:为何是右指针先扫而不是左指针先扫呢,你们本身想一想吧哈哈,模拟一下就知道了。后端

发散性思考

上文中我提到了左数组,右数组,还提到了基准数的概念,其中,左数组中的全部值都小于基准数,右数组中的全部值都大于基准数,并且快排仍是个递归的过程。我这里若是作一下类比,若是我把基准数当作是树根,把左数组当作是根的左孩子,右数组当作是根的右孩子,相信学过数据结构的童鞋都知道了,那不就是一颗BST吗。哈哈确实如此,其实你会发现BST的中序遍历出来就是一个有序数组,因此上文中快排的过程就是一个建立二叉搜索树的过程。数组

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具备下列性质的二叉树: 若它的左子树不空,则左子树上全部结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上全部结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树.bash

因此要是问你怎么建立一颗BST,想必也能信手拈来了吧,废话不说上代码:数据结构

function vNode(value) {
           this.val = value;
           this.left = this.right = null;
       }
     function createBST(arr) {
           var len = arr.length;
           if(len < 1)
               return;
           var l = 0;
           var r = len - 1;
           var temp = arr[0];
           while(l < r) {
               while(l < r && arr[r] >= temp)
                   r --;
               while(l < r && arr[l] <= temp)
                   l ++;
               [arr[l], arr[r]] = [arr[r], arr[l]];
           }
           [arr[0], arr[l]] = [arr[l], arr[0]];
           var root = new vNode(arr[l]);
           root.left = createBST(arr.slice(0, l));
           root.right = createBST(arr.slice(l + 1));
           return root;
       }
复制代码

二叉树的非递归中序遍历

我相信不少人都能写出二叉树的递归遍历:递归遍历左子树,输出根节点,递归遍历右子树,三行代码,完事,可是要问到非递归遍历,估计大多数人就傻眼了,可是人家就要考你不会的啊,非递归遍历其实就是借助栈来完成:ui

var inorderTraversal = function(root) {
    const res = [];
    const stack = [];
    while(root||stack.length !== 0)
     {
          while(root)
          {
              stack.push(root);
             root=root.left;
         }
         if(stack.length)
         {
             let p=stack[stack.length - 1];
             res.push(p.val);
             stack.pop();
             root = p.right;
         }
     }  
    return res;
};
复制代码

老美的公司,算法关必须过啊。因此,路漫漫其修远兮,刷题刷题多刷题吧this

相关文章
相关标签/搜索