微軟前端社招筆試詳解

又到了每一年的金三銀四招聘旺季,有幸得到了微軟的筆試機會,程序猿們應該都知道,老美的公司都喜歡懟數據結構與算法,微軟確定也不例外,我的以爲考數據結構對每個應聘者都公平,我此次投的是微軟蘇研院,筆試考察的不難,是最最多見的數據結構算法裸題,這裏記錄一下,也給出解法。前端

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

相關文章
相關標籤/搜索