1. 程式人生 > 其它 >2022/03/10 位元組後端實習面試覆盤

2022/03/10 位元組後端實習面試覆盤

位元組視訊面

資料結構

  • 雜湊表的實現方式
    一開始以為是問STL裡面map和unordered_map的底層資料結構,後來面試官追問了一下才明白是在問雜湊表這個資料結構的底層實現,其實就是陣列+連結串列,但是一開始沒有說對,面試官問了一下雜湊碰撞的解決,然後想起來是用陣列+連結串列來搞定。

  • 追問:連結串列過長時如何解決?
    把連結串列換成二叉樹,這樣可以降低搜尋的時間複雜度

  • 追問:應該用哪種二叉樹?
    這裡答成二叉搜尋樹了,其實應該是紅黑樹,我把二叉搜尋樹的性質記錯了,以為有左右子樹的平衡

  • 追問:二叉搜尋樹的性質?查詢時間複雜度是多少?二叉搜尋樹最壞情況下查詢時間複雜度是多少?
    左子樹節點值比根節點的值小,根節點比右子樹節點值小,然後左右子樹都是二叉搜尋樹;複雜度O(logN),但是我以為二叉搜尋樹一定是完全二叉樹,以為不會出現最壞情況;實際上如果二叉搜尋樹是有可能退化成單枝樹的,這時查詢複雜度會程式設計O(N)。

演算法

  • 快排的實現?
    這個沒啥問題。

  • 追問:快排會出現效能退化的情況嗎?什麼情況下會發生?
    會,如果快排每次選的基準數都是最小或者最大的數,這時候時間複雜度會退化成O(n2)。

  • 追問:如果數組裡有很多重複的元素,快排的效能會變差嗎?
    以前沒考慮過這個問題,想了一下應該是會變差,每次遞迴劃分區間如果非常不平衡就會導致快排效能下降。

  • 追問:那麼面對這種情況,應該用哪種排序演算法比較合適?
    回答了堆排序、桶排序,查了一下發現可以用雜湊表來記錄重複數字的個數,就是所謂的雜湊排序。

  • 追問:怎麼針對含有大量重複元素的情況優化快排?
    這個問題從來沒考慮過,想了一下沒想出來,放棄了。查了一下發現在演算法研究裡針對快排有過專門應對這種情況的改進,就是所謂的雙路快排,用i和j分別指向小於基準數的下一個數和大於基準數的前一個數,i向後遍歷直到遇到比基準數大的元素,j向前遍歷直到遇到比基準數小的元素,這時如果i < j就直接交換i和j指向的元素,然後繼續之前的操作。這樣做好處在於兩邊遇到等於基準數的元素不會處理,這樣兩個區間內包含的等於基準數的元素個數也大致差不多,避免了出現左右區間長度不平衡的情況。在雙路快排的基礎上又可以優化出三路快排,就是在劃分區間的時候分成3個區間,把等於基準數的元素儲存到中間的區間,這樣遞迴到下一層的時候減少了對重複元素的比較。

計算機網路

  • 講一下TCP三次握手
    說了一下流程

  • 為什麼不能是兩次握手?
    這個問題有點沒答到點子上,我說的是伺服器沒有收到客戶端對建立連線的確認訊號時,會重複傳送建立連線的請求,應該是兩次握手時,如果出現訊息滯留,客戶端可能會多次傳送連線建立請求,而沒有第三次握手伺服器無法確定客戶端是不是收到了自己傳送的建立連線的確認訊號,就會每收到一個連線建立請求就主動建立一個連線,造成資源浪費。

  • TCP連線中的半連線狀態是什麼?
    說混了,把半連線說成半關閉了,以為是四次揮手裡面的客戶端收到伺服器對FIN請求迴應的ACK之後的狀態,其實半連線是三次揮手中,伺服器響應客戶端發起連線請求後的狀態,這時如果客戶端不進行第三次握手,伺服器就一直處於半連線狀態,這樣會導致伺服器的記憶體被浪費。
    還有一個半開啟狀態,是TCP連線中有一方突然異常關閉之後,另一方不知情的狀態。

  • TCP泛洪攻擊是什麼?
    這個之前看過,大致講出來了,就是攻擊方向伺服器傳送大量的SYN連線請求但是又不進行第三次握手,這樣伺服器要不停地傳送ACK確認,使伺服器資源被耗盡無法服務其他使用者。

  • 怎麼應對泛洪攻擊?
    這個就憑印象說了一下設定一個時間來丟棄不進行第三次握手的連線(其實本身就有這個設定,叫SYN timeout,應該是縮短它),或者標記一下發送過很多建立連線請求最終又不建立起連線的IP地址把其遮蔽(過濾思路),但是這個方法其實不太能應對偽裝地址或者分散式攻擊,目前的解決方法還是用到防火牆,有這麼幾種思路(參考了部落格: https://www.cnblogs.com/wangp19/p/12099815.html):

    1. 防火牆收到客戶端的SYN包時,直接轉發給伺服器:防火牆收到伺服器的SYN/ACK包後,一方面將SYN/ACK包轉發給客戶端,另一方面以客戶端的名義給伺服器回送一個ACK包.完成TCP的三次握手,讓伺服器端由半連線狀態進入連線狀態。伺服器能承受連線狀態的能力比承受半連線狀態的能力強得多,所以可以降低攻擊的影響.
    2. 設定防火牆的SYN請求超時引數,讓它遠小於伺服器的超時期限.防火牆負責轉發客戶端發往伺服器的SYN包,伺服器發往客戶端的SYN/ACK包、以及客戶端發往伺服器的ACK包。這樣,如果客戶端在防火牆計時器到期時還沒傳送ACK包,防火牆則往伺服器傳送RST包,以使伺服器從佇列中刪去該半連線。由於防火牆的超時引數遠小於伺服器的超時期限,這樣能有效防止泛洪攻擊.
    3. SYN中繼防火牆在收到客戶端的SYN包後.並不向伺服器轉發而是記錄該狀態資訊後主動給客戶端回送SYN/ACK包,如果收到客戶端的ACK包。表明是正常訪問,由防火牆向伺服器傳送SYN包並完成三次握手。這樣由防火牆作為代理來實現客戶端和伺服器端的連線,可以完全過濾不可用連線發往伺服器。
  • TCP和UDP的區別
    應該都答上來了

  • TCP為什麼可靠?或者是TCP用什麼機制來實現可靠性的?
    沒有細緻思考過這個問題,憑直覺講了一下和建立連線、超時重傳、流量控制以及擁塞控制有關。查了一下,應該從亂序重排、應答確認、報文重傳和流量控制四種機制來講,比較有條理。

  • 提到了流量控制,TCP的流量控制是怎麼實現的?
    我記得是靠滑動視窗來實現的,接收方會給傳送方發的確認報文裡提供自己的緩衝window值,傳送方會根據這個值來改變滑動視窗的大小。

  • UDP的應用場景
    從UDP的特點出發,UDP不是可靠的,同時沒有流量控制和擁塞控制,所以很直觀的應用場景就是對準確性要求不高而對速度要求高的場景,像是線上視訊這一類的(網路語音,直播等等).

  • HTTP和HTTPS的區別?
    忘了其實...印象裡HTTPS是基於HTTP的安全協議,好像是和加密演算法有關,但是我只把加密演算法說成了RSA,其實準確得說應該是基於TLS/SSL協議.不過TLS/SSL的功能實現依賴的加密演算法確實包含RSA演算法,總共有三類:雜湊演算法、對稱加密和非對稱加密,RSA屬於非對稱加密演算法而且是很常用的演算法.

  • RSA是對稱加密演算法還是非對稱加密演算法?
    當時忘了,是非對稱的,就是說加密和解密可以使用不同的金鑰.

作業系統

  • 程序間通訊有哪些方式?
    不會,很尷尬,作業系統真是軟肋。面試官後面也沒有問其他作業系統相關的問題了。

程式碼題

做完查了一下,是LeetCode1004原題,把題目貼過來:
給定一個二進位制陣列 nums 和一個整數 k ,如果可以翻轉最多k 個 0 ,則返回 陣列中連續 1 的最大個數 。

示例 1:

輸入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
輸出:6
解釋:[1,1,1,0,0,1,1,1,1,1,1]
粗體數字從 0 翻轉到 1,最長的子陣列長度為 6。

示例 2:

輸入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
輸出:10
解釋:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗體數字從 0 翻轉到 1,最長的子陣列長度為 10。

提示:
1 <= nums.length <= 20000
0 <= k <= nums.length

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/max-consecutive-ones-iii
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

我一開始的思路是先計算出陣列的字首和陣列,這樣能在O(1)的時間裡算出任意子陣列內0和1的個數,如果0的個數小於k就可以將子陣列的長度與當前計算得到的最長值比較,但是這樣要遍歷所有的子陣列,時間複雜度是O(n2)。這題裡面陣列的長度可以達到20000,顯然這個思路不太對,後來面試官提醒了一下用滑動視窗,很快就反應過來了,維護滑動視窗的左右邊界和視窗內0的值,右邊界向右移動,遇到1不處理繼續右移,遇到0判斷現在視窗內0的值有沒有超過k,沒有的話也不處理繼續移動右邊界,如果超過k,更新一次最大子陣列長度,開始右移左邊界直到遇到0,直到右邊界到達陣列結尾,結束迴圈後再比較一下視窗長度和最大子陣列長度。程式碼如下:

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n = nums.size();
        int left = 0, right = 0, cnt = 0;
        int ans = 0;
        while(right < n){
            if(nums[right] == 1){
                ++right;
            }
            else{
                ++cnt;
                if(cnt <= k){
                    ++right;
                }
                else{
                    ans = max(ans, right - left);
                    while(left < right && nums[left] == 1){
                        ++left;
                    }
                    ++left;
                    --cnt;
                    ++right;
                }
            }
        }
        ans = max(ans, right - left);
        return ans;
    }
};

總結

感覺位元組的這場面試會針對一個問題問得特別深入,一直到我回答不上來為止,還是對一些問題的思考有點不夠深入了,需要加深理解.