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):用於動態內存分配。
為什么分成這么多個區域?
主要基於以下考慮:
#代碼是根據流程依次執行的,一般只需要訪問一次,而數據一般都需要訪問多次,因此單獨開辟空間以方便訪問和節約空間。
#未初始化數據區在運行時放入棧區中,生命周期短。
#全局數據和靜態數據有可能在整個程序執行過程中都需要訪問,因此單獨存儲管理。
#堆區由用戶自由分配,以便管理。
更多內容見:http://www.cnblogs.com/Stultz-Lee/p/6751522.html
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握手與釋放
讀懂TCP狀態轉移過程,對理解網絡編程頗有幫助,本文將對TCP狀態轉移過程進行介紹,但各狀態(總共11個)含義不在本文介紹的范圍,請參考文末的書目列表。
TCP狀態轉換圖(state transition diagram)

1. 建立連接(three-way hand shake)
- 主動打開(passive open):服務器必須准備好接受外來的連接,通常通過socket、bind和listen完成。
- 被動打開(active open):客戶端通過connect發起主動打開。
插句話,該文介紹的性能分析工具(sar -n TCP,ETCP 1)命令可以對主動打開和被動打開的數目進行統計,在一定程度上可以反應服務器的繁忙程度。
下圖給出客戶端與服務器的三次握手過程:

- 服務器准備好接受外來連接,通常通過socket、bind和listen完成。(服務器:CLOSED->LISTEN)
- 客戶端通過connect連接服務器,客戶端TCP將發送一個SYN包,告訴服務器客戶端將在待建立連接發送數據的初始序列號。(客戶端:CLOSED->SYN_SENT)
- 服務器端必須ACK客戶端SYN,同時發送一個SYN,告訴客戶端,服務器將在待建立連接發送數據的初始序列號。(服務器:SYN_RCVD)
- 客戶端必須ACK服務器SYN。(客戶端:SYN_SENT->ESTABLISHED)
- 服務器接收到客戶端ACK。(服務器:SYN_RECV->ESTABLISHED)
下圖給出三次握手對應的狀態轉移圖:

2. 建立連接(同時打開simultaneous open)
參考《TCP/IP詳解》卷一第18章18.8節。這種情況發生在兩端幾乎同時發送SYN並且這兩個SYN在網絡中交錯的情形。這種情況可能發生,但是非常罕見。
例如,主機A的應用程序使用本地端口7777,並與主機B的端口8888執行主動打開。主機B的應用程序使用本地端口8888,並與主機A的端口7777執行主動打開。
下圖給出同時打開過程:

下圖給出同時打開的狀態轉移圖:

說明:從目前個人經驗來看,這種場景沒有遇到過,但這是完整理解TCP狀態轉移必須的一部分。但《TCP/IP詳解》卷一第18章18.8節中支出,許多伯克利版的TCP實現都不能正確地支持打開。
3. 建立連接失敗(1)
考慮場景:客戶端尚未接收到服務器ACK+SYN,異常退出(崩潰退出)。這時,當客戶端接收到服務器的ACK+SYN時,客戶端回復RST(reset)。此時服務器處於SYN_RCVD狀態,當接收到客戶端RST時,則從SYN_RCVD轉移到LISTEN狀態。
具體過程:
- 服務器准備好接受外來連接,通常通過socket、bind和listen完成。(服務器:CLOSED->LISTEN)
- 客戶端通過connect連接服務器,客戶端TCP將發送一個SYN包,告訴服務器,客戶端將在待建立連接發送數據的初始序列號。然后客戶端異常退出。(客戶端:CLOSED->SYN_SENT,然后突然crash,則退出SYN_SENT)
- 服務器端ACK客戶端SYN,同時發送一個SYN,告訴客戶端,服務器將在待建立連接發送數據的初始序列號。(服務器:SYN_RCVD)
- 客戶端找不到服務器ACK+SYN對應的SYN_SENT狀態的socket,則響應RST。(服務器:SYN_RCVD->LISTEN)
下圖給出建立連接失敗的狀態轉移圖:

4. 建立連接失敗(2)
考慮場景1:服務器的進程異常退出,客戶端不知道。那么客戶端發送SYN后,服務器端響應RST,則客戶端建立連接失敗。
考慮場景2:服務器機器關閉,導致服務器IP不可達。那么客戶端發送SYN后,超時重發,超過重試次數,最終TIMEOUT,則客戶端建立連接失敗。
具體過程:
- 假設服務器進程退出。(服務器:LISTEN->CLOSED)
- 客戶端通過connect連接服務器,客戶端TCP將發送一個SYN包,告訴服務器,客戶將在待建立連接發送數據的初始序列號。(客戶端:CLOSED->SYN_SENT)
- 服務器端收到客戶端SYN,響應RST(服務器:CLOSED)
- 客戶收到RST。(客戶端:SYN_SENT->CLOSED)
下圖給出建立連接失敗的狀態轉移圖:

5. 斷開連接(四次揮手)
- 主動關閉(active close):某個應用程序首先調用close,發送一個FIN包。
- 被動關閉(passive close):接收到FIN的對端執行被動關閉。
下圖給出客戶端與服務器的四次揮手過程:

- 某個應用程序首先調用close,該端發送一個FIN包,表示數據發送完畢,該應用程序再無更多數據發送給對端。(例如HTTP服務器發送Reponse數據給client后,再無多余數據發送,則Server可以執行主動關閉)(主動端:ESTABLISHED->FIN_WAIT_1)
- 接收到FIN的對端執行被動關閉。首先ACK這個收到的FIN包。該FIN包的接收也作為一個文件結束符(EOF)傳遞給應用程序(放在已排隊等候該應用進程接收的任何其他數據之后),因為FIN包意味着接收端應用進程在相應的連接上再無額外數據可接收。(被動端:ESTABLISHED->CLOSE_WAIT,主動端:FIN_WAIT_1->FIN_WAIT_2)
- 一段時間后,接收到這個EOF的應用進程將調用close關閉socket。這導致它的TCP也發送一個FIN包。(被動端:CLOSE_WAIT->LAST_WAIT)
- 接收這個最終FIN的執行主動關閉的那一端ACK這個FIN。(被動端:LAST_WAIT->CLOSED,主動端:FIN_WAIT_2->TIME_WAIT(2MSL之后,TIME_WAIT->CLOSED))
下圖給出四次揮手的狀態轉移圖:

6. 斷開連接(同時關閉simultaneous close)
參考《TCP/IP詳解》卷一第18章18.9節。我們在前面討論了一方(通常但不總是客戶方)發送第一個FIN執行主動關閉。雙方都執行主動關閉也是可能的,TCP協議也允許這樣的同時關閉(simultaneous close)。
下圖給出同時關閉過程:

下圖給出同時關閉的狀態轉移圖:

7. 斷開連接(在FIN_WAIT_1狀態中,接收FIN+ACK)
考慮場景:被動關閉端收到FIN包后,直接發送FIN+ACK,則主動關閉方則從FIN_WAIT_1跳過FIN_WAIT_2,直接進入TIME_WAIT。
給出具體流程:
- 某個應用程序首先調用close,該端發送一個FIN包,表示數據發送完畢,該應用程序再無更多數據發送給對端。(例如HTTP服務器發送Reponse數據給client后,再無多余數據發送,則Server可以執行主動關閉)(主動端:ESTABLISHED->FIN_WAIT_1)
- 接受到FIN的對端執行被動關閉。收到FIN包之后,被動端調用close關閉socket,則FIN+ACK同時發給主動端。(被動端:ESTABLISHED->CLOSE_WAIT->LAST_ACK,主動端:FIN_WAIT_1->TIME_WAIT)
- 接收這個最終FIN的執行主動關閉的那一端ACK這個FIN。(被動端:LAST_WAIT->CLOSED,主動端:FIN_WAIT_1->TIME_WAIT(2MSL之后,TIME_WAIT->CLOSED))
下圖給出同時關閉的狀態轉移圖:

參考:
- 中文版《UNIX網絡編程》第一卷第2章
- 中文版《TCP/IP詳解》卷一第18章
錯誤說明:
- 中文版《UNIX網絡編程》第一卷(第三版)(第一次印刷版)圖2.4有個錯誤,服務器從LISTEN轉移到SYN_RCVD時,條件應該是“接收:SYN;發送:SYN,ACK”,而不是“接收:RST;發送:SYN,ACK”。這一點可以從英文原本的配圖看到。(注:2015年8月第二次印刷已經修訂這個錯誤)
- 我也認為,書中的TCP狀態轉移圖中,SYN_RCVD轉移到LISTEN的條件應該是服務器狀態,而非客戶端狀態。

關於作者:
限於本人對某些技術點的理解程度(雖然也查了很多資料),可能某些地方表達的不太准確,敬請指教,聯系方式為pengfeicui@yeah.net
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+樹屬於一種折中選擇。
