騰訊研發類筆試面試試題及答案(C++方向)
題很多,先上題後上答案,便於大家思考
問題點:
1、C和C++的特點與區別?
2、C++的多型
3、虛擬函式實現
4、C和C++記憶體分配問題
5、協程
6、CGI的瞭解
7、程式間通訊方式和執行緒間通訊方式
8、TCP握手與釋放
9、http和https的區別?
10、虛擬記憶體的概念與介紹
11、單連結串列的反轉演算法
12、紅黑樹以及其查詢複雜度
13、KPM字串匹配
14、TCP超時等待、重傳以及流量控制
15、資料庫引擎
16、資料庫索引
1、C和C++的特點與區別?
答:(1)C語言特點:
1.作為一種程式導向的結構化語言,易於除錯和維護;
2.表現能力和處理能力極強,可以直接訪問記憶體的實體地址;
3.C語言實現了對硬體的程式設計操作,也適合於應用軟體的開發;
4.C語言還具有效率高,可移植性強等特點。
(2)C++語言特點:
1.在C語言的基礎上進行擴充和完善,使C++相容了C語言的程式導向特點,又成為了一種物件導向的程式設計語言;
2.可以使用抽象資料型別進行基於物件的程式設計;
3.可以使用多繼承、多型進行物件導向的程式設計;
4.可以擔負起以模版為特徵的泛型化程式設計。
C++與C語言的本質差別:在於C++是物件導向的,而C語言是程式導向的。或者說C++是在C語言的基礎上增加了物件導向程式設
計的新內容,是對C語言的一次更重要的改革,使得C++成為軟體開發的重要工具。
2、C++的多型
答:C++的多型性用一句話概括:在基類的函式前加上virtual關鍵字,在派生類中重寫該函式,執行時將會根據物件的實際型別來
呼叫相應的函式。如果物件型別是派生類,就呼叫派生類的函式;如果物件型別是基類,就呼叫基類的函式。
1):用virtual關鍵字申明的函式叫做虛擬函式,虛擬函式肯定是類的成員函式;
2):存在虛擬函式的類都有一個一維的虛擬函式表叫做虛表,類的物件有一個指向虛表開始的虛指標。虛表是和類對應的,虛表指標是
和物件對應的;
3):多型性是一個介面多種實現,是物件導向的核心,分為類的多型性和函式的多型性。;
4):多型用虛擬函式來實現,結合動態繫結.;
5):純虛擬函式是虛擬函式再加上 = 0;
6):抽象類是指包括至少一個純虛擬函式的類;
純虛擬函式:virtual void fun()=0;即抽象類,必須在子類實現這個函式,即先有名稱,沒有內容,在派生類實現內容。
3、虛擬函式實現
答:簡單地說,每一個含有虛擬函式(無論是其本身的,還是繼承而來的)的類都至少有一個與之對應的虛擬函式表,其中存放著該類
所有的虛擬函式對應的函式指標。例:
其中:
B的虛擬函式表中存放著B::foo和B::bar兩個函式指標。
D的虛擬函式表中存放的既有繼承自B的虛擬函式B::foo,又有重寫(override)了基類虛擬函式B::bar的D::bar,還有新增的虛擬函式D::quz。
虛擬函式表構造過程:
從編譯器的角度來說,B的虛擬函式表很好構造,D的虛擬函式表構造過程相對複雜。下面給出了構造D的虛擬函式表的一種方式(僅供參考):
虛擬函式呼叫過程
以下面的程式為例:
4、C和C++記憶體分配問題
答:(1)C語言程式設計中的記憶體基本構成
C的記憶體基本上分為4部分:靜態儲存區、堆區、棧區以及常量區。他們的功能不同,對他們使用方式也就不同。
1.棧 ——由編譯器自動分配釋放;
2.堆 ——一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由OS回收;
3.全域性區(靜態區)——全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域,未初始化的全域性變數
和未初始化的靜態變數在相鄰的另一塊區域(C++中已經不再這樣劃分),程式結束釋放;
4.另外還有一個專門放常量的地方,程式結束釋放;
(a)函式體中定義的變數通常是在棧上;
(b)用malloc, calloc, realloc等分配記憶體的函式分配得到的就是在堆上;
(c)在所有函式體外定義的是全域性量;
(d)加了static修飾符後不管在哪裡都存放在全域性區(靜態區);
(e)在所有函式體外定義的static變數表示在該檔案中有效,不能extern到別的檔案用;
(f)在函式體內定義的static表示只在該函式體內有效;
(g)另外,函式中的"adgfdf"這樣的字串存放在常量區。
(2)C++程式設計中的記憶體基本構造
在C++中記憶體分成5個區,分別是堆、棧、全域性/靜態儲存區、常量儲存區和程式碼區;
1、棧,就是那些由編譯器在需要的時候分配,在不需要的時候自動清楚的變數的儲存區,裡面的變數通常是區域性變數、函式引數等。
2、堆,就是那些由new分配的記憶體塊,他們的釋放編譯器不去管,由我們的應用程式去控制,一般一個new就要對應一個delete。如
果程式設計師沒有釋放掉,那麼在程式結束後,作業系統會自動回收。
3、全域性/靜態儲存區,全域性變數和靜態變數被分配到同一塊記憶體中,在以前的C語言中,全域性變數又分為初始化的和未初始化的,在
C++裡面沒有這個區分了,他們共同佔用同一塊記憶體區。
4、常量儲存區,這是一塊比較特殊的儲存區,他們裡面存放的是常量,不允許修改(當然,你要通過非正當手段也可以修改)。
5、程式碼區 (.text段),存放程式碼(如函式),不允許修改(類似常量儲存區),但可以執行(不同於常量儲存區)。
記憶體模型組成部分:自由儲存區,動態區、靜態區;
根據c/c++物件生命週期不同,c/c++的記憶體模型有三種不同的記憶體區域,即:自由儲存區,動態區、靜態區。
自由儲存區:區域性非靜態變數的儲存區域,即平常所說的棧;
動態區: 用new ,malloc分配的記憶體,即平常所說的堆;
靜態區:全域性變數,靜態變數,字串常量存在的位置;
注:程式碼雖然佔記憶體,但不屬於c/c++記憶體模型的一部分;
一個正在執行著的C編譯程式佔用的記憶體分為5個部分:程式碼區、初始化資料區、未初始化資料區、堆區 和棧區;
(1)程式碼區(text segment):程式碼區指令根據程式設計流程依次執行,對於順序指令,則只會執行一次(每個程式),如果反覆,則需要使用跳轉指令,如果進行遞迴,則需要藉助棧來實現。注意:程式碼區的指令中包括操作碼和要操作的物件(或物件地址引用)。如果是立即數(即具體的數值,如5),將直接包含在程式碼中;
(2)全域性初始化資料區/靜態資料區(Data Segment):只初始化一次。
(3)未初始化資料區(BSS):在執行時改變其值。
(4)棧區(stack):由編譯器自動分配釋放,存放函式的引數值、區域性變數的值等,其操作方式類似於資料結構中的棧。
(5)堆區(heap):用於動態記憶體分配。
為什麼分成這麼多個區域?
主要基於以下考慮:
#程式碼是根據流程依次執行的,一般只需要訪問一次,而資料一般都需要訪問多次,因此單獨開闢空間以方便訪問和節約空間。
#未初始化資料區在執行時放入棧區中,生命週期短。
#全域性資料和靜態資料有可能在整個程式執行過程中都需要訪問,因此單獨儲存管理。
#堆區由使用者自由分配,以便管理。
還有騰訊,阿里,京東等一線大廠面試題及答案整理,需要集錦的朋友可以+qun720209036免費獲取。
5、協程
答:定義:協程是一種使用者態的輕量級執行緒。
協程擁有自己的暫存器上下文和棧。協程排程切換時,將暫存器上下文和棧儲存到其他地方,在切回來的時候,恢復先前儲存的暫存器上下文和棧。因此:協程能保留上一次呼叫時的狀態(即所有區域性狀態的一個特定組合),每次過程重入時,就相當於進入上一次呼叫的狀態,換種說法:進入上一次離開時所處邏輯流的位置;
執行緒是搶佔式,而協程是協作式;
協程的優點:
跨平臺
跨體系架構
無需執行緒上下文切換的開銷
無需原子操作鎖定及同步的開銷
方便切換控制流,簡化程式設計模型
高併發+高擴充套件性+低成本:一個CPU支援上萬的協程都不是問題。所以很適合用於高併發處理。
協程的缺點:
無法利用多核資源:協程的本質是個單執行緒,它不能同時將 單個CPU 的多個核用上,協程需要和程式配合才能執行在多CPU;
進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程式:這一點和事件驅動一樣,可以使用非同步IO操作來解決。
6、CGI的瞭解
答:CGI:通用閘道器介面(Common Gateway Interface)是一個Web伺服器主機提供資訊服務的標準介面。通過CGI介面,Web服務
器就能夠獲取客戶端提交的資訊,轉交給伺服器端的CGI程式進行處理,最後返回結果給客戶端。
CGI通訊系統的組成是兩部分:一部分是html頁面,就是在使用者端瀏覽器上顯示的頁面。另一部分則是執行在伺服器上的Cgi程式。
7、程式間通訊方式和執行緒間通訊方式
答:(1)程式間通訊方式:
# 管道( pipe ):管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程式間使用。程式的親緣關係通常是指父子程式關係。
# 訊號量( semophore ) : 訊號量是一個計數器,可以用來控制多個程式對共享資源的訪問。它常作為一種鎖機制,防止某程式正在訪問共享資源時,其他程式也訪問該資源。因此,主要作為程式間以及同一程式內不同執行緒之間的同步手段。
# 訊息佇列( message queue ) : 訊息佇列是由訊息的連結串列,存放在核心中並由訊息佇列識別符號標識。訊息佇列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。
# 共享記憶體( shared memory ) :共享記憶體就是對映一段能被其他程式所訪問的記憶體,這段共享記憶體由一個程式建立,但多個程式都可以訪問。共享記憶體是最快的 IPC 方式,它是針對其他程式間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,如訊號兩,配合使用,來實現程式間的同步和通訊。
# 套接字( socket ) : 套解口也是一種程式間通訊機制,與其他通訊機制不同的是,它可用於不同及其間的程式通訊。
(2)執行緒間通訊方式:
#全域性變數;
#Messages訊息機制;
#CEvent物件(MFC中的一種執行緒通訊物件,通過其觸發狀態的改變實現同步與通訊)。
8、TCP握手與釋放
答:(1)握手
#第一次握手:主機A傳送握手訊號syn=1和seq=x(隨機產生的序列號)的資料包到伺服器,主機B由SYN=1知道,A要求建立聯機;
#第二次握手:主機B收到請求後要確認聯機資訊,向A傳送syn=1,ack=x(x是主機A的Seq)+1,以及隨機產生的確認端序列號
seq=y的包;
#第三次握手:主機A收到後檢查ack是否正確(ack=x+1),即第一次傳送的seq+1,若正確,主機A會再傳送ack=y+1,以及隨機序
列號seq=z,主機B收到後確認ack值則連線建立成功;
#完成三次握手,主機A與主機B開始傳送資料。
注:上述步驟中,第二和第三次確認包中都還包含一個標誌位未予以說明,該標誌位為1表示正常應答;
具體可見圖片:
為什麼需要“三次握手”?
“三次握手”的目的是“為了防止已失效的連線請求報文段突然又傳送到了服務端,因而產生錯誤”。具體例如:client發出的第一個連線請求報文段並沒有丟失,而是在某個網路結點長時間的滯留了,以致延誤到連線釋放以後的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連線請求報文段後,就誤認為是client再次發出的一個新的連線請求。於是就向client發出確認報文段,同意建立連線。假設不採用“三次握手”,那麼只要server發出確認,新的連線就建立了。由於現在client並沒有發出建立連線的請求,因此不會理睬server的確認,也不會向server傳送資料。但server卻以為新的運輸連線已經建立,並一直等待client發來資料。這樣,server的很多資源就白白浪費掉了。採用“三次握手”的辦法可以防止上述現象發生。例如剛才那種情況,client不會向server的確認發出確認。server由於收不到確認,就知道client並沒有要求建立連線。主要目的防止server端一直等待,浪費資源。
(2)揮手
由於TCP連線是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的資料傳送任務後就能傳送一個FIN來終止這個方向的連線。收到一個 FIN只意味著這一方向上沒有資料流動,一個TCP連線在收到一個FIN後仍能傳送資料。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
(1) TCP客戶端傳送一個FIN,用來關閉客戶到伺服器的資料傳送(報文段4);
(2) 伺服器收到這個FIN,發回一個ACK,確認序號為收到的序號加1(報文段5)。和SYN一樣,一個FIN將佔用一個序號;
(3) 伺服器關閉客戶端的連線後,再傳送一個FIN給客戶端(報文段6);
(4) 客戶段收到服務端的FIN後,發回ACK報文確認,並將確認序號設定為收到序號加1(報文段7);
注意:TCP連線的任何一方都可以發起揮手操作,上述步驟只是兩種之一;
具體過程見圖:
為什麼是“四次揮手”?
因為當收到對方的FIN報文通知時,它僅僅表示對方沒有資料傳送給你了;但未必你所有的資料都全部傳送給對方了,所以你可能還需要傳送一些資料給對方,再傳送FIN報文給對方來表示你同意現在可以關閉連線了,故這裡的ACK報文和FIN報文多數情況下都是分開傳送的,也就造成了4次揮手。
握手,揮手過程中各狀態介紹:
(1)3次握手過程狀態:
#LISTEN: 這個也是非常容易理解的一個狀態,表示伺服器端的某個SOCKET處於監聽狀態,可以接受連線了。
#SYN_SENT: 當客戶端SOCKET執行CONNECT連線時,它首先傳送SYN報文,因此也隨即它會進入到了SYN_SENT狀態,並等待服務端的傳送三次握手中的第2個報文。SYN_SENT狀態表示客戶端已傳送SYN報文。(傳送端)
#SYN_RCVD: 這個狀態與SYN_SENT遙想呼應這個狀態表示接受到了SYN報文,在正常情況下,這個狀態是伺服器端的SOCKET在建立TCP連線時的三次握手會話過程中的一箇中間狀態,很短暫,基本上用netstat你是很難看到這種狀態的,除非你特意寫了一個客戶端測試程式,故意將三次TCP握手過程中最後一個ACK報文不予傳送。因此這種狀態時,當收到客戶端的ACK報文後,它會進入到ESTABLISHED狀態。(伺服器端)
#ESTABLISHED:這個容易理解了,表示連線已經建立了。
(2)4次揮手過程狀態:
#FIN_WAIT_1: 這個狀態要好好解釋一下,其實FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是:FIN_WAIT_1狀態實際上是當SOCKET在ESTABLISHED狀態時,它想主動關閉連線,向對方傳送了FIN報文,此時該SOCKET即進入到FIN_WAIT_1狀態。而當對方回應ACK報文後,則進入到FIN_WAIT_2狀態,當然在實際的正常情況下,無論對方何種情況下,都應該馬上回應ACK報文,所以FIN_WAIT_1狀態一般是比較難見到的,而FIN_WAIT_2狀態還有時常常可以用netstat看到。(主動方)
#FIN_WAIT_2:上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2狀態下的SOCKET,表示半連線,也即有一方要求close連線,但另外還告訴對方,我暫時還有點資料需要傳送給你(ACK資訊),稍後再關閉連線。(主動方)
#TIME_WAIT: 表示收到了對方的FIN報文,併傳送出了ACK報文,就等2MSL後即可回到CLOSED可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。(主動方)
#CLOSING(比較少見): 這種狀態比較特殊,實際情況中應該是很少見,屬於一種比較罕見的例外狀態。正常情況下,當你傳送FIN報文後,按理來說是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示你傳送FIN報文後,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼情況下會出現此種情況呢?其實細想一下,也不難得出結論:那就是如果雙方几乎在同時close一個SOCKET的話,那麼就出現了雙方同時傳送FIN報文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET連線。
#CLOSE_WAIT: 這種狀態的含義其實是表示在等待關閉。怎麼理解呢?當對方close一個SOCKET後傳送FIN報文給自己,你係統毫無疑問地會回應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正需要考慮的事情是察看你是否還有資料傳送給對方,如果沒有的話,那麼你也就可以close這個SOCKET,傳送FIN報文給對方,也即關閉連線。所以你在CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉連線。(被動方)
#LAST_ACK: 這個狀態還是比較容易好理解的,它是被動關閉一方在傳送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也即可以進入到CLOSED可用狀態了。(被動方)
9、http和https的區別?
答:HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,要比http協議安全。
HTTPS(Secure Hypertext Transfer Protocol)安全超文字傳輸協議,與http主要區別在於:
#http是超文字傳輸協議,資訊是明文傳輸,https 則是具有安全性的ssl加密傳輸協議;
#http和https使用的是完全不同的連線方式用的埠也不一樣,前者是80,後者是443;
下面具體介紹一下HTTP和HTTPS協議:
首先說明一下:HTTP和HTTPS協議是應用層協議;
上圖充分表明:HTTP是應用層協議,並且HTTPS是在HTTP協議基礎上新增SSL等加密策略後的協議;
TLS/SSL中使用了非對稱加密,對稱加密以及HASH演算法。
(1)Http協議
1)HTTP協議和TCP協議之間的區別聯絡
①TPC/IP協議是傳輸層協議,主要解決資料如何在網路中傳輸,而HTTP是應用層協議,主要解決如何包裝資料;
②HTTP的預設埠號是80,TCP/IP協議通訊程式設計時埠號需要自己指定(例如socket程式設計);
③HTTP協議是在TCP/IP協議基礎上實現的,即HTTP資料包是經過TCP/IP協議實現傳輸的;
④HTTP是無狀態的短連線協議,TCP是有狀態的長連線協議;
HTTP是在有狀態長連線TCP/IP協議的基礎上實現的,為什麼卻是無狀態短連線協議?
答:因為HTTP協議每次請求結束就會自動關閉連線,這樣就變成了短連線;
短連線又導致了該次請求相關資訊的丟失,也就造成了HTTP協議對於前期事務處理沒有記憶能力,故為無狀態協議。
2)HTTP協議其完整的工作過程可分為四步:
①連線:首先客戶機與伺服器需要建立連線(由TCP/IP握手連線實現)。只要單擊某個超級連結,HTTP的工作開始;
②請求:建立連線後,客戶機傳送一個請求給伺服器,請求方式的格式為:統一資源識別符號(URL)、協議版本號,後邊是MIME資訊包括請求修飾符、客戶機資訊和可能的內容;
③應答:伺服器接到請求後,給予相應的響應資訊,其格式為一個狀態行,包括資訊的協議版本號、一個成功或錯誤的程式碼,後邊是MIME資訊包括伺服器資訊、實體資訊和可能的內容。客戶端接收伺服器所返回的資訊通過瀏覽器顯示在使用者的螢幕上;
④關閉:當應答結束後,瀏覽器和伺服器關閉連線,以保證其他瀏覽器可以與伺服器進行連線。
更完整的過程可能如下:
域名解析 --> 發起TCP的3次握手 --> 建立TCP連線後發起http請求 --> 伺服器響應http請求,瀏覽器得到html程式碼 --> 瀏覽器解析html程式碼,並請求html程式碼中的資源(如js、css、圖片等) --> 瀏覽器對頁面進行渲染呈現給使用者。
如果在以上過程中的某一步出現錯誤,那麼產生錯誤的資訊將返回到客戶端,有螢幕輸出。對於使用者來說,這些過程是由HTTP自己完成的,使用者只要用滑鼠點選,等待資訊顯示就可以了。
(2)Https協議
HTTPS握手過程包括五步:
1)瀏覽器請求連線;
2)伺服器返回證照:證照裡面包含了網站地址,加密公鑰,以及證照的頒發機構等資訊。
3)瀏覽器收到證照後作以下工作:
a) 驗證證照的合法性;
b) 生成隨機(對稱)密碼,取出證照中提供的公鑰對隨機密碼加密;
c) 將之前生成的加密隨機密碼等資訊傳送給網站;
4)伺服器收到訊息後作以下的操作:
a) 使用自己的私鑰解密瀏覽器用公鑰加密後的訊息,並驗證HASH是否與瀏覽器發來的一致;
b) 使用加密的隨機對稱密碼加密一段訊息,傳送給瀏覽器;
5)瀏覽器解密並計算握手訊息的HASH:如果與服務端發來的HASH一致,此時握手過程結束,之後所有的通訊資料將由之前瀏覽
器生成的隨機密碼並利用對稱加密演算法進行加密。
注意:伺服器有兩個金鑰,一個公鑰、一個私鑰,只有私鑰才可以解密公鑰加密的訊息;
如圖:
或者如下圖:
HTTPS協議、SSL、和數字證照的關係介紹:
概述:對於HTTPS協議,所有的訊息都是經過SSL協議方式加密,而支援加密的檔案正是數字證照;
(1)SSL
SSL常用的加密演算法:對稱密碼演算法、非對稱密碼演算法、雜湊演算法;
SSL的加密過程:需要注意的是非對稱加解密演算法的效率要比對稱加解密要低的多。所以SSL在握手過程中使用非對稱密碼演算法來
協商金鑰,實際使用對稱加解密的方法對http內容加密傳輸;
(2)數字證照
數字證照是用於在INTERNET上標識個人或者機構身份的一種技術手段,它通過由一些公認的權威機構所認證,從而可以保證其
安全地被應用在各種場合。證照裡面包含了網站地址,加密公鑰,以及證照的頒發機構等資訊。
10、虛擬記憶體的概念與介紹
答:虛擬記憶體中,允許將一個作業分多次調入記憶體,需要時就調入,不需要的就先放在外存。因此,虛擬記憶體的實需要建立在離散
分配的記憶體管理方式的基礎上。虛擬記憶體的實現有以下三種方式:
#請求分頁儲存管理
#請求分段儲存管理
#請求段頁式儲存管理
虛擬記憶體的意義:
一,虛擬記憶體可以使得實體記憶體更加高效。虛擬記憶體使用置換方式,需要的頁就置換進來,不需要的置換出去,使得記憶體中只儲存了需要的頁,提高了利用率,也避免了不必要的寫入與擦除;
二,使用虛擬地址可以使記憶體的管理更加便捷。在程式編譯的時候就會生成虛擬地址,該虛擬地址並不是對應一個實體地址,使得也就極大地減少了地址被佔用的衝突,減少管理難度;
三,為了安全性的考慮。在使用虛擬地址的時候,暴露給程式設計師永遠都是虛擬地址,而具體的實體地址在哪裡,這個只有系統才瞭解。這樣就提
高了系統的封裝性。
11、單連結串列的反轉演算法
答:思想:建立3個指標,分別指向上一個節點、當前節點、下一個節點,遍歷整個連結串列的同時,將正在訪問的節點指向上一個節點,當遍歷結束後,就同時完成了連結串列的反轉。
實現程式碼:
ListNode* ReverseList(ListNode* pHead) {
ListNode *p,*q,*r;
if(pHead==NULL || pHead->next==NULL){
return pHead;
}else{
p=pHead;
q=p->next;
pHead->next=NULL;
while(q!=NULL){
r=q->next;
q->next=p;
p=q;
q=r;
}
return p;
}
}
12、紅黑樹以及其查詢複雜度
答:(1)紅黑樹來源於二叉搜尋樹,其在關聯容器如map中應用廣泛,主要優勢在於其查詢、刪除、插入時間複雜度小,但其也有缺點,就是容易偏向一邊而變成一個連結串列。
紅黑樹是一種二叉查詢樹,但在每個結點上增加一個儲存位表示結點的顏色,可以是Red或Black。也就是說,紅黑樹是在二叉
查詢樹基礎上進一步實現的;
紅黑樹的五個性質:
性質1. 節點是紅色或黑色;
性質2. 根節點是黑色;
性質3 每個葉節點(指樹的末端的NIL指標節點或者空節點)是黑色的;
性質4 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點);
性質5. 從任一節點到其每個尾端NIL節點或者NULL節點的所有路徑都包含相同數目的黑色節點。
(注:上述第3、5點性質中所說的NIL或者NULL結點,並不包含資料,只充當樹的路徑結束的標誌,即此葉結點非常見的葉子結點)。
因為一棵由n個結點隨機構造的二叉查詢樹的高度為lgn,所以順理成章,二叉查詢樹的一般操作的執行時間為O(lgn)。但二叉查
找樹若退化成了一棵具有n個結點的線性鏈後,則這些操作最壞情況執行時間為O(n);
紅黑樹雖然本質上是一棵二叉查詢樹,但它在二叉查詢樹的基礎上增加以上五個性質使得紅黑樹相對平衡,從而保證了
紅黑樹的查詢、插入、刪除的時間複雜度最壞為O(log n)。
(2)左旋右旋
紅黑樹插入或刪除後,一般就會改變紅黑樹的特性,要恢復紅黑樹上述5個性質,一般都要那就要做2方面的工作:
1、部分結點顏色,重新著色
2、調整部分指標的指向,即左旋、右旋。
左選右旋如圖所示:
左旋,如圖所示(左->右),以x->y之間的鏈為“支軸”進行,使y成為該新子樹的根,x成為y的左孩子,而y的左孩子則成為x的右孩
子。演算法很簡單,旋轉後各個結點從左往右,仍然都是從小到大。
左旋程式碼實現,分三步:
(1) 開始變化,y的左孩子成為x的右孩子;
(2) y成為x的父結點;
(3) x成為y的左孩子;
右旋類似,不再累述;
13、KPM字串匹配
(1)KMP匹配演算法程式碼實現:
int KmpSearch(char* s, char* p)
{
int i = 0;
int j = 0;
int sLen = strlen(s);
int pLen = strlen(p);
while (i < sLen && j < pLen)
{
//①如果j = -1,或者當前字元匹配成功(即S[i] == P[j]),都令i++,j++
if (j == -1 || s[i] == p[j])
{
i++;
j++;
}
else
{
//②如果j != -1,且當前字元匹配失敗(即S[i] != P[j]),則令 i 不變,j = next[j]
//next[j]即為j所對應的next值
j = next[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
(2)next陣列求取
上述(1)中最重要的就是:一旦不匹配,模式串不是向後移動一位,而是根據前面匹配資訊移動多位。而這個多位獲得就是根據next陣列,下面有next陣列的求取方式:
Next陣列是根據模式串的字首字尾獲取的,如下:
①尋找字首字尾最長公共元素長度
舉個例子,如果給定的模式串為“abab”,那麼它的各個子串的字首字尾的公共元素的最大長度如下表格所示:
比如對於字串aba來說,它有長度為1的相同字首字尾a;而對於字串abab來說,它有長度為2的相同字首字尾ab(相同字首字尾的長度為k + 1,k + 1 = 2)。
②求next陣列
next 陣列考慮的是除當前字元外的最長相同字首字尾,所以通過第①步驟求得各個字首字尾的公共元素的最大長度後,只要稍作變形即可:將第①步驟中求得的陣列整體右移一位,然後第一個元素賦為-1即可(注意:字串下標需要從0開始),如下表格所示:
比如對於aba來說,第3個字元a之前的字串ab中有長度為0的相同字首字尾,所以第3個字元a對應的next值為0;而對於abab來說,第4個字元b之前的字串aba中有長度為1的相同字首字尾a,所以第4個字元b對應的next值為1(相同字首字尾的長度為k,k = 1)。
KMP的next 陣列相當於告訴我們:當模式串中的某個字元跟文字串中的某個字元匹配失配時,模式串下一步應該跳到哪個位置(具體:保持測試串的下標i不變,使得匹配串的下標j=next[j])。
字首字尾長度求取以及next陣列獲取:
如果給定的模式串是:“ABCDABD”,從左至右遍歷整個模式串,其各個子串的字首字尾分別如下表格所示:
也就是說,原模式串子串對應的各個字首字尾的公共元素的最大長度表為:
0 0 0 0 1 2 0;
故對應的next陣列為:-1 0 0 0 0 1 2;
(注意:這裡的字串下標是從0開始的,若從1開始,next陣列所有元素都對應要加1。)
求取next的實現程式碼:
string T; //T為模式串
cin>>T;
int len=T.size();
queue<int> MaxLen;
vector<int> next;
MaxLen.push(0); //第一個元素都設為0
for(int i=1;i<len;i++)
{
int k=1,maxLen=0;
while(k<=i)
{
if(T.substr(0,k)==T.substr(i-k+1,k))
{
maxLen=k;
}
k++;
}
MaxLen.push(maxLen);
}
cout<<endl;
next.push_back(-1); //第一個元素都設為-1
while(MaxLen.size()>1)
{
int temp=MaxLen.front();
next.push_back(temp);
MaxLen.pop();
cout<<temp<<' ';
}
14、TCP超時等待、重傳以及流量控制
答:TCP等待時間需要設定,超過了就認為丟包,需要重傳;
為了防止擁塞情況,一般會採用流量控制,其實現手段是用滑動視窗限制客戶端傳送分組數量;
15、資料庫引擎
答:資料庫引擎是用於儲存、處理和保護資料的核心服務。利用資料庫引擎可控制訪問許可權並快速處理事務,從而滿足企業內大多
數需要處理大量資料的應用程式的要求。
簡言之,資料庫引擎就是一段用於支撐所有資料庫操作的核心程式,就如名稱一樣,是一個車的引擎功能;
常見的資料庫引擎有:
(1)Microsoft JET (Joint Engineering Technologe) 用於Access和VB的內嵌資料庫功能的核心元素;
(2)ODBC(Open DataBase Connectivity,開放資料庫互連)是由Microsoft定義的一種資料庫訪問標準,它提供一種標準的資料
庫訪問方法以訪問不同平臺的資料庫。一個ODBC應用程式既可以訪問在本地PC機上的資料庫,也可以訪問多種異構平臺上的資料
庫,例如SQL Server、Oracle或者DB2;
(3)OLE DB是Microsoft開發的最新資料庫訪問介面,Microsoft將其定義為ODBC接班人;
(4)MYSQL支援三個引擎:ISAM、MYISAM和HEAP。另外兩種型別INNODB和BERKLEY(BDB)也常常可以使用;
①ISAM執行讀取操作的速度很快,而且不佔用大量的記憶體和儲存資源。ISAM的兩個主要不足之處在於,它不 支援事務處理,也不能夠容錯;
②MyISAM是MySQL的ISAM擴充套件格式和預設的資料庫引擎MYISAM。除了提供ISAM裡所沒有的索引和欄位管理的大量功能,
MyISAM還使用一種表格鎖定的機制,來優化多個併發的讀寫操作,其代價是你需要經常執行OPTIMIZE TABLE命令,來恢復被更新
機制所浪費的空間;
③HEAP允許只駐留在記憶體裡的臨時表格。駐留在記憶體裡讓HEAP要比ISAM和MYISAM都快,但是它所管理的資料是不穩定的,
而且如果在關機之前沒有進行儲存,那麼所有的資料都會丟失。
16、資料庫索引
答:定義:資料庫索引是對資料庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問資料庫表中的特定資訊;
舉例:employee 表的人員編號列(id)就是資料庫索引,select * from employee where id=10000即可查詢編號10000的人員資訊。如果沒有索引,必須遍歷整個表直到id=10000;
資料庫索引作用:
一,大大加快 資料的檢索速度,這也是建立索引的最主要的原因;
二,保證資料庫表中每一行資料的唯一性;
三,可以加速表和表之間的連線,特別是在實現資料的參考完整性方面特別有意義;
四,在使用分組和排序子句進行資料檢索時,同樣可以顯著減少查詢中分組和排序的時間;
五,通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的效能。
資料庫索引缺陷:
一,表的增刪改查、建立索引和維護索引要耗費時間;
二,索引需要佔物理空間;
資料庫索引的兩個特徵:索引有兩個特徵,即唯一性索引和複合索引;
①唯一 性索引保證在索引列中的全部資料是唯一的,不會包含冗餘資料;
②複合索引就是一個索引建立在兩個列或者多個列上,搜尋時需要兩個或者多個索引列作為一個關鍵值;
資料庫索引好比是一本書前面的目錄,索引分為聚簇索引和非聚簇索引兩類:
1)聚簇索引是按照資料存放的物理位置為順序的,其多個連續行的訪問速度更快;
2)非聚簇索引是按照資料存放的邏輯位置為順序的,其單行訪問速度更快;
區域性性原理與磁碟預讀
區域性性原理:當一個資料被用到時,其附近的資料也通常會馬上被使用。程式執行期間所需要的資料通常比較集中;
磁碟預讀:正是由於區域性性原理以及資料儲存磁碟的讀寫速度慢的原因,每次對資料庫進行讀取都不是按需讀取,而是讀取多
於需求資料區域內的資料到記憶體,用於後續使用,提高寫讀取資料速度;
注:磁碟預讀一般都是每次讀取邏輯上的一頁,或物理上的一塊,不管實際需求是多少;
資料庫索引的實現通常使用B樹及其變種B+樹,下面進行B-/+Tree結構的資料庫索引的效能分析:
(1)B樹索引結構:
資料庫系統的設計者巧妙利用了磁碟預讀原理,將B樹的一個節點的大小設為等於一個頁,這樣每個節點只需要一次I/O就可以
完全載入。為了達到這個目的,在實際實現B-Tree還需要使用如下技巧:
——每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也儲存在一個頁;
B-Tree中一次檢索最多需要h-1次I/O(磁碟IO不包括根節點,因為根節點常駐記憶體),漸進複雜度為O(h)=O(logdN)。一
般實際應用中,出度d是非常大的數字,通常超過100,因此h非常小(通常不超過3)。
而紅黑樹這種結構,h明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用區域性性,所以紅黑樹的I/O漸進
複雜度也為O(h),效率明顯比B-Tree差很多。
所以,B樹結構的資料庫索引,在元素查詢上效率很高;
(2)B+樹的索引結構:
B+樹則適當犧牲檢索的時間複雜度(都必須檢索到葉子結點),但改善了節點插入和刪除的時間複雜度(類似用連結串列改善陣列的效
果),所以B+樹屬於一種折中選擇。
相關文章
- 騰訊往屆筆試面試題整理大全筆試面試題
- Java研發方向如何準備BAT技術面試答案JavaBAT面試
- 騰訊社招筆面試題面試題
- 【面試】社招中級前端筆試面試題總結-答案及擴充前端筆試面試題
- 騰訊技術崗位筆試&面試題(一)筆試面試題
- 騰訊技術崗位筆試&面試題(二)筆試面試題
- 資料探勘面試筆試題(附答案)面試筆試
- 高階Java開發面試解答,Java開發面試題及答案Java面試題
- 2017 年軟體實施工程師筆試面試題及答案工程師筆試面試題
- Python開發面試題及部分答案分享!Python面試題
- 騰訊筆試題集筆試
- 30 道 Dubbo 面試題及答案面試題
- Java初中級面試題及答案Java面試題
- Java高階面試題及答案Java面試題
- 常見AI面試題及答案AI面試題
- Selenium 高頻面試題及答案面試題
- 碼農面試智力題及答案面試
- SQL崗位30個面試題,SQL面試問題及答案SQL面試題
- Linux面試題,淺析常見Linux命令面試題及答案Linux面試題
- 鵝廠視訊部筆試及面試問題整理筆試面試
- Java併發程式設計40道面試題及答案——面試穩了Java程式設計面試題
- 最全前端開發面試問題及答案整理前端面試
- HTML最新面試題(筆試面試題)HTML面試題筆試
- 2018年最新Java面試題及答案Java面試題
- 阿里Jvm必問面試題及答案阿里JVM面試題
- 面試必備,Linux面試題和答案!Linux面試題
- 雲端計算面試題及答案,AWS雲端計算面試題面試題
- Web前端經典面試試題及答案(參考連結)Web前端面試
- 技術乾貨:關於效能測試面試題及答案面試題
- 面試寶典:15道MyBatis 常見面試題彙總及答案MyBatis面試題
- 2019 Python最新面試題及答案16道題Python面試題
- Python 筆試 面試題Python筆試面試題
- 技術乾貨:RabbitMQ面試題及答案MQ面試題
- 技術乾貨:ActiveMQ面試題及答案MQ面試題
- 技術乾貨:Hadoop面試題及答案Hadoop面試題
- ASP net core面試題彙總及答案面試題
- 阿里金服最全java面試題及答案阿里Java面試題
- 24個Jvm面試題總結及答案JVM面試題