1. 程式人生 > >頭條面試總結

頭條面試總結

頭條面試問題整理
  • 自我介紹
  • 專案詳細介紹
  • 演算法題
    • LeetCode上一個題,給定一個二維陣列和目標值,該二維陣列每一行和每一列都是非遞減的,問二維陣列中的等於目標值的座標。
    • 實現一個排序樹,能插入,能刪除,能平衡
    • 輸入一個數組,要求輸出陣列中每個數字後面第一個比他大的數字,沒有比他大的輸出-1,時間複雜度O(n)。輸入:5,1,9,6,7 輸出:9,9,-1,7,-1
    • 二維陣列中,只能向右和向下,找到從左上角到右下角路徑中和最大的值
    • 堆排序
    • 快排思想
    • 兩個棧實現佇列
    • 單鏈表排序
  • 資料庫
    • MySQL的索引原理、資料庫索引型別
      • 索引原理
        • 本質
          通過不斷地縮小想要獲取資料的範圍來 篩選出 最終想要的結果,同時把隨機的事件變成順序的事件。
        • 資料結構角度考慮,為什麼使用B樹而不用AVL樹or紅黑樹?
          • 定義:AVL樹是嚴格的二叉排序樹(保持平衡),紅黑樹是非嚴格的二叉排序樹(不用儲存平衡)
          • 顯然,AVL樹和紅黑樹都比B樹的高度h高,因為B樹的節點度d更大(B樹高度O(logdN))
          • I/O次數越多:一個節點大小佔用OS中的一個邏輯頁,對應了一次I/O,因此樹越高一次查詢次數(即為樹高)越多。
          • 無法運用程式區域性性原理:以為樹太高導致邏輯上很近的節點(父子節點)物理上可能很遠。
          • 因此,B樹的I/O次數明顯低於AVL樹和紅黑樹,故選擇B樹作為索引結構。
        • 計算機組成原理角度考慮,大多數資料庫系統為什麼使用B+樹(對B+樹結構做了優化,添加了葉子節點的順序指標)而不使用B-樹?
          • 利用磁碟預讀原理(根據區域性性原理),DB的設計者將1個節點大小設定為1頁,故1個節點只需1個I/O就能完全載入。
          • B樹一次查詢最多查詢次數=h-1(高度為h,根節點常駐記憶體)
          • 樹高度h=O(logdN)。實際應用中,出讀d是非常大的數字,通常超過100,因此h非常小。
          • B-樹高度>B+樹高度
            因為d(B+)>d(B),d的計算公式為:d=節點大小(1頁,4或8kB)/(keysize+datasize+pointsize)。而B+樹的非葉子節點沒有data域,故datasize值為0,故其d更大。
          • 優化後的B+樹能幹嘛?
            提高區間訪問的效能。
            eg:如果要查詢key為從18到49的所有資料記錄,則從第一個葉子節點開始查詢,當找到key=18後,只需順著結點和指標順序遍歷就可以一次性訪問到所有資料結點,極大提到了區間查詢效率。
        • 補充常識
          目前大部分資料庫系統檔案系統都採用B-樹或者B+樹作為索引結構。
    • 型別
      • 普通索引index :加速查詢
      • 唯一索引
        • 主鍵索引:primary key :加速查詢+約束(不為空且唯一)
        • 唯一索引:unique:加速查詢+約束 (唯一)
      • 聯合索引
        • primary key(id,name):聯合主鍵索引
        • unique(id,name):聯合唯一索引
        • index(id,name):聯合普通索引
      • 全文索引fulltext :用於搜尋很長一篇文章的時候,效果最好。
        db表message_queue_history,包括欄位
        • 建立語法
          alter table message_queue_history add fulltext index idx_message (message);
        • 查詢語法
          select * from message_queue_history where match(message) against(‘10086’);
      • 空間索引spatial :瞭解就好,幾乎不用
    • B+樹
      • 索引欄位要儘量的小
        • 因為IO次數取決於b+樹的高度h ,而索引欄位影響了樹的高度
        • 如何影響?
          假設當前資料表的資料為N,每個磁碟塊的資料項的數量是m,則有h=㏒(m+1)N,當資料量N一定的情況下,m越大,h越小;而 m = 磁碟塊的大小/資料項的大小,磁碟塊的大小也就是一個數據頁的大小,是固定的,如果資料項佔的空間越小,資料項的數量越多,樹的高度越低。
      • 索引的最左匹配特性(即從左往右匹配)
  • 網路通訊
    • 畫出tcp斷開連線的圖
      • 過程
        • Client端收到App的關閉訊號後,傳送FIN給Server端。此後Client處於Fin_Wait1狀態
        • Sever端收到來自Client端的Fin訊號後,傳送Ack訊號給Client端,告訴App可以關閉連線了。此後Server進入Close_Wait狀態
        • Client端收到來自Server端的Ack訊號。此後Client進入Fin_Wait2狀態,等待Server傳送Fin訊號
        • Server端的App準備關閉連線時,傳送Fin訊號給Client端。此後Server進入Last_Ack狀態
        • Client端收到來自Server端的Fin訊號後,傳送Ack訊號給Server端,並等待2msl時長再關閉本地核心連線。此後Client進入Time_Wait狀態
      • 圖[畫圖]
        在這裡插入圖片描述
    • tcp四次揮手 和 狀態 以及 為什麼等待2msl,如果伺服器一直沒收到一直髮斷開連線請求的話,它會一直重置這個時間嗎
      • 套接字狀態
        • Client端依次為:Fin_Wait1、Fin_Wait2、Time_Wait
        • Server端依次為:Close_Wait、Last_Ack
        • 狀態說明
          • Fin_Wait1
            • 連線處於半段開狀態(可以接受、應答資料,當不能傳送資料),並將連線的控制權託管給 Kernel,程式就不再進行處理。
            • 一般情況下,連線處理 FIN_WAIT1 的狀態只是持續很短的一段時間
          • Fin_Wait2
            • 在等待對方的 FIN 資料報
            • 當 TCP 一直保持這個狀態時,對方就有可能永遠都不斷開連線,導致該連線一直保持著。
            • 不屬於任何應用的孤兒連線保持Fin_Wait2狀態的最長時間預設為:60s。超過這個時間,就會被本地直接關閉,不會進入Time_Wait狀態。
            • 處於 Fin_Wait2 狀態的TCP連線,威脅要比 Fin_Wait1 的小,佔用的資源也很小,通常不會有什麼問題
          • Time_Wait
            • Client端發出Ack響應後,核心會設定一個時間長度為 2MSL 的定時器,當定時器在到時間點後,核心就會將該連線關閉。
            • 當連線尚未關閉的時候,又收到了對方傳送過來的 FIN 請求(可能是我們傳送出去的請求對方並未收到),或者收到 ICMP 請求(比如 ACK 資料報,在網路傳輸中出現了錯誤),該連線就會重新發送 ACK 請求,並重置定時器
          • Close_Wait
            • 該連線可能有資料需要傳送,或者一些其他事情要做
            • 當這類連線過多的時候,就會導致網路效能下降,耗盡連線數,無法建立新的連線。
          • Last_Ack
            • 等待對方進行傳送 ACK 資料報
            • 收到了響應的ACK資料報後,連線進入CLOSED 狀態,並釋放相關資源。
            • 如果超時未收到響應,就觸發了TCP的重傳機制。
      • 等待2msl的原因
        保證Server端能夠收到Client端傳送的最後一個Ack訊號。而Server端在收到最後一個Ack訊號後才能關閉連線。
    • 程序間通訊方式 & 執行緒間通訊方式
      • 程序間通訊方式
        • 分類
          管道、訊息佇列、訊號量、共享記憶體、Socket、Streams等。其中Socket、Streams支援不同主機上的兩個程序通訊
        • 分別介紹
          • 管道
            • 無名管道(預設)
              • 它是半雙工的(即資料只能在一個方向上流動),具有固定的讀端和寫端
              • 它只能用於具有親緣關係的程序之間的通訊(也是父子程序或者兄弟程序之間)
              • 它可以看成是一種特殊的檔案,對於它的讀寫也可以使用普通的read、write 等函式。但是它不是普通的檔案,並不屬於其他任何檔案系統,並且只存在於記憶體
              • 通常呼叫 pipe 的程序接著呼叫 fork,這樣就建立了父程序與子程序之間的 IPC 通道
            • 有名管道
              • 名字:FIFO
              • FIFO可以在無關的程序之間交換資料
              • FIFO有路徑名與之相關聯,它以一種特殊裝置檔案形式存在於檔案系統
              • 在資料讀出時,FIFO管道中同時清除資料,並且“先進先出”
          • 訊息佇列
            • 存放在核心
            • 一個訊息佇列由一個識別符號(即佇列ID)來標識
            • 訊息佇列是面向記錄的,其中的訊息具有特定的格式以及特定的優先順序
            • 訊息佇列獨立於傳送與接收程序。程序終止時,訊息佇列及其內容並不會被刪除
            • 訊息佇列可以實現訊息的隨機查詢,訊息不一定要以先進先出的次序讀取,也可以按訊息的型別讀取
          • 訊號量
            • 它是一個計數器。訊號量用於實現程序間的互斥與同步,而不是用於儲存程序間通訊資料
            • 訊號量用於程序間同步,若要在程序間傳遞資料需要結合共享記憶體
            • 訊號量基於作業系統的 PV 操作,程式對訊號量的操作都是原子操作
            • 每次對訊號量的 PV 操作不僅限於對訊號量值加 1 或減 1,而且可以加減任意正整數
            • 支援訊號量組
          • 共享記憶體
            • 最快的一種 IPC,因為程序是直接對記憶體進行存取
            • 因為多個程序可以同時操作,所以需要進行同步
            • 訊號量+共享記憶體通常結合在一起使用,訊號量用來同步對共享記憶體的訪問
          • Socket
            • 參考部落格:https://blog.csdn.net/Hearthougan/article/details/51275765
            • 通訊流程
              • 圖片
                在這裡插入圖片描述
              • 說明
                • 伺服器根據地址型別(ipv4,ipv6)、socket型別、協議建立socket
                • 伺服器為socket繫結ip地址和埠號
                • 伺服器socket監聽埠號請求,隨時準備接收客戶端發來的連線,這時候伺服器的socket並沒有被開啟
                • 客戶端建立socket
                • 客戶端開啟socket,根據伺服器ip地址和埠號試圖連線伺服器socket
                • 伺服器socket接收到客戶端socket請求,被動開啟,開始接收客戶端請求,直到客戶端返回連線資訊。這時候socket進入阻塞狀態,所謂阻塞即accept()方法一直到客戶端返回連線資訊後才返回,開始接收下一個客戶端諒解請求
                • 客戶端連線成功,向伺服器傳送連線狀態資訊
                • 伺服器accept方法返回,連線成功
                • 客戶端向socket寫入資訊
                • 伺服器讀取資訊
                • 客戶端關閉
                • 伺服器端關閉
            • 流程解釋
              • 建立一個套接字—socket()
                socket()函式用於根據指定的地址族、資料型別、協議來分配一個套接字的描述字及其所用的資源
              • 指定本地地址—bind()函式
                將本地地址與一套接字捆綁。當socket()建立套接字 後,它便存在於一個名字空間(地址族)中,bind()函式通過給一個未命名的套接字分配一個本地名字,來為套接字建立本地捆綁(主機地址)
              • 監聽和請求連線—listen()、connect()函式
              • 建立套接字連線—accept()函式
                呼叫accept()函式來接收請求,這樣連線便建立好了,之後就可以網路I/O操作了,即類同於普通檔案的讀寫I/O操作
              • 資料傳輸—send()與recv()
                客戶端and伺服器應用程式都用send函式來向TCP連線的另一端傳送資料
                ps:send函式僅僅是把buf中的資料copy到sockfd的傳送緩衝區的剩餘空間裡,而不是sockfd的傳送緩衝中的資料傳送到連線的另一端,那是協議乾的活
              • 關閉套接字—close()
                關閉套接字s,並釋放分配給該套接字的資源
          • Streams
      • 執行緒間通訊方式
        • 同步:如synchronized關鍵字
        • 輪詢:如CAS機制
        • wait/notify機制
        • 管道通訊(共享記憶體)
    • 網路分層
    • 網路層的功能
      控制子網的執行。如分組傳輸、路由選擇等。
    • 傳輸層協議
      tcp、udp
    • Tcp和Udp區別
      • 是否面向連線
      • 是否可靠
      • 面向位元組流or報文
      • 是否執行多對多
        • Tcp只能一對一建立連線,傳輸資料。
        • Udp允許一對一,一對多,多對一和多對多的互動通訊。
      • 首部開銷大小
        Tcp為20B,Udp為8B。
    • Tcp長連線和短連線
      • 長連結
        • 定義
          指在一個TCP連線上可以連續傳送多個數據包,在TCP連線保持期間,如果沒有資料包傳送,需要雙方發檢測包以維持此連線,一般需要自己做線上維持
        • 過程
          連線→資料傳輸→保持連線(心跳)→資料傳輸→保持連線(心跳)→……→關閉連線(一個TCP連線多個讀寫通訊)
        • 優勢
          • 提高傳輸速度
          • 降低連續建立連線的資源消耗
        • 適用物件
          操作頻繁(讀寫),點對點的通訊,而且連線數不能太多情況
      • 短連線
        • 定義
          短連線是指通訊雙方有資料互動時,就建立一個TCP連線,資料傳送完成後,則斷開此TCP連線。
        • 過程
          連線→資料傳輸→關閉連線
        • 優勢
          管理簡單,存在的連線都是有用的連線,不需要額外的控制手段
        • 適用物件
          WEB網站的http服務一般都用短連結.
          ps:http1.0 -----只支援短連線;
          http1.1 -----keep alive 帶時間,操作次數限制的長連線
    • socket的理解,底層,如何做一個server
  • 作業系統
    • 多程序和多執行緒的區別
    • 為什麼區分核心態和使用者態
      • 使用者態
        程序執行自己的程式碼時
      • 核心態
        發生系統呼叫時,程序執行核心的程式碼
      • 原因
        • CPU的所有指令中,有一些指令是非常危險的,如果錯用,將導致整個系統崩潰
        • CPU將指令分為特權指令和非特權指令.
        • 危險的指令,只允許作業系統及其相關模組使用
        • 普通的應用程式只能使用那些不會造成災難的指令
    • 1億個數,100個執行緒,每個執行緒隨機從1億個數裡面取一個數做處理,要求保證不會有兩個執行緒對同一個數做處理。我腦子裡都是java裡的各種鎖,他說不要考慮java,想想作業系統裡的互斥鎖
    • 程序間的通訊方式,管道通訊如何實現,命名管道呢,共享記憶體呢
    • 如何實現一個程序,說說對殭屍程序的理解,孤兒程序的理解,能手寫一個fork程序嗎
      • 孤兒程序
        一個父程序退出,而它的一個或多個子程序還在執行,那麼那些子程序將成為孤兒程序。孤兒程序將被init程序(程序號為1)所收養,並由init程序對它們完成狀態收集工作。
      • 殭屍程序
        程序終止後進入僵死狀態(zombie),等待告知父程序自己終止,後才能完全消失.但是如果一個程序已經終止了,但是其父程序還沒有獲取其狀態,那麼子程序就稱之為殭屍程序。
      • 殭屍程序的危害
        如果父程序不呼叫wait/waitpid,那麼保留退出子程序的那段資訊就不會釋放+程序號會一直被佔用。因此如果有大量殭屍程序的出現將導致OS不能產生新程序。
      • 殭屍程序的解決辦法
        • 通過訊號機制
          子程序退出時向父程序傳送SIGCHILD訊號,父程序處理SIGCHILD訊號
        • fork兩次
          原理是將子程序成為孤兒程序,從而其的父程序變為init程序,通過init程序可以處理殭屍程序
      • 手動建立fork程序
        • 函式原型
          1)include // 必須引入標頭檔案,使用fork函式的時候,必須包含這個標頭檔案,否則,系統找不到fork函式
          2)pid_t fork(void); //void代表沒有任何形式引數
        • fork函式不需要任何引數,對於返回值有三種情況
          • 對於父程序,fork函式返回新建子程序的pid
          • 對於子程序,fork函式返回 0;
          • 如果出錯, fork 函式返回 -1。
        • 程式碼示例
          在這裡插入圖片描述
  • 設計題
    • 設計火車票訂票系統,場景是對於固定的某一天,輸入起始站和終點站,返回給使用者車次資訊和每個席位的剩餘票數