備戰秋招——C++知識點

 

1.字符串的末尾'\0'也算一個字符,一個字節。c++

2.使用庫函數strcpy(a,b)進行拷貝b->a操做,strcpy會從源地址一直日後拷貝,直到遇到'\0'爲止。因此拷貝的長度是不定的。若是一直沒有遇到'\0'致使越界訪問非法內存,程序就崩了。git

3.strlen的結果未統計’\0’所佔用的1個字節。  程序員

4.寫出完整的strcpy函數算法

char * strcpy( char *strDest, const char *strSrc ) 
{
 assert( (strDest != NULL) && (strSrc != NULL) );
 char *address = strDest; 
 while( (*strDest++ = * strSrc++) != ‘\0’ ); 
 return address;
}

 

5.malloc和free要對應使用,防止內存泄漏編程

6.printf(str) 改成 printf("%s",str),不然可以使用格式化 字符串攻擊。printf會把str當成一個格式化字符串(formatstring),printf在其中尋找特殊的格式字符好比"%d"。若是碰到格式字符,一個變量的參數值就從堆棧中取出。很明顯,攻擊者至少能夠經過打印出堆棧中的這些值來偷看程序的內存。設計模式

7.malloc以後應該對內存分配是否成功進行判斷。free(p)以後應該把p=NULL,不然p會變成野指針。api

8.浮點型變量並不精確,因此不可將float變量用「==」或「!=」與數字比較,應該設法轉化成「>=」或「<=」形式。數組

9.數組名的本質以下:   
(1)數組名指代一種數據結構,這種數據結構就是數組;   安全

char str[10];
cout << sizeof(str) << endl;

 

 

輸出結果爲10,str指代數據結構char[10]。   
(2)數組名能夠轉換爲指向其指代實體的指針,並且是一個指針常量,不能做自增、自減等操做,不能被修改;   
char str[10];    
str++; //編譯出錯,提示str不是左值  數據結構

(3) 數組名做爲函數形參時,淪爲普通指針。  
Windows NT 32位平臺下,指針的長度(佔用內存的大小)爲4字節,故sizeof( str ) 、sizeof ( p ) 都爲4。  

10.static關鍵字至少有下列n個做用: 

  
(1)函數體內static變量的做用範圍爲該函數體,不一樣於auto變量,該變量的內存只被分配一次,所以這個靜態變量的值在下次調用該函數時仍維持上次的值;   
(2)在模塊內的static全局變量能夠被模塊內所用函數訪問,但不能被模塊外其它函數訪問;   
(3)在模塊內的static函數只可被這一模塊內的其它函數調用,這個函數的使用範圍被限制在聲明它的模塊內;   
(4)在類中的static成員變量屬於整個類所擁有,對類的全部對象只有一份拷貝;   
(5)在類中的static成員函數屬於整個類所擁有,這個函數不接收this指針,於是只能訪問類的static成員變量。    

11.const關鍵字至少有下列n個做用:   

(1)欲阻止一個變量被改變,可使用const關鍵字。在定義該const變量時,一般須要對它進行初始化,由於之後就沒有機會再去改變它了;   const int* p ;p指向的變量的值不能變。int* const p; p指向的東西不能變
(2)對指針來講,能夠指定指針自己爲const,也能夠指定指針所指的數據爲const,或兩者同時指定爲const;   
(3)在一個函數聲明中,const能夠修飾形參,代表它是一個輸入參數,在函數內部不能改變其值;   
(4)對於類的成員函數,若指定其爲const類型,則代表其是一個常函數,不能修改類的 成員變量;   
(5)對於類的成員函數,有時候必須指定其 返回值爲const類型,以使得其返回值不爲「左值」。例如:   
const classA operator*(const classA& a1,const classA& a2);   
operator*的返回結果必須是一個const對象。若是不是,這樣的變態代碼也不會編譯出錯:   
classA a, b, c;   
(a * b) = c; // 對a*b的結果賦值   
操做(a * b) = c顯然不符合編程者的初衷,也沒有任何意義。   
warning:不要再頭文件中聲明static的全局函數,不要在cpp內聲明非static的全局函數,若是你要在多個cpp中複用該函數,就把它的聲明提到頭文件裏去,不然cpp內部聲明需加上static修飾;
 
C++和C的區別
設計思想上:

C++是面向對象的語言,而C是面向過程的結構化編程語言

語法上:

C++具備重載、繼承和多態三種特性

C++相比C,增長多許多類型安全的功能,好比強制類型轉換、

C++支持範式編程,好比模板類、函數模板等

 

C/C++ 中指針和引用的區別?

1.指針有本身的一塊空間,而引用只是一個別名;

2.使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小;

3.指針能夠被初始化爲NULL,而引用必須被初始化且必須是一個已有對象 的引用;

4.做爲參數傳遞時,指針須要被解引用才能夠對對象進行操做,而直接對引 用的修改都會改變引用所指向的對象;

5.能夠有const指針,可是沒有const引用;

6.指針在使用中能夠指向其它對象,可是引用只能是一個對象的引用,不能 被改變;

7.指針能夠有多級指針(**p),而引用至於一級;

8.指針和引用使用++運算符的意義不同;

9.若是返回動態內存分配的對象或者內存,必須使用指針,引用可能引發內存泄露。

 
 

請你說一下你理解的c++中的smart pointer四個智能指針: shared_ptr,unique_ptr,weak_ptr,auto_ptr

·智能指針也是一個類,當退出這個類對象的做用域時會自動調用析構函數,不只會釋放這個類的成員變量,指針的內存空間,還會釋放這個指針指向的對象的內存空間,因此不用擔憂這個指針指向的對象忘記釋放從而致使的內存泄漏。 

1. auto_ptr(c++98的方案,cpp11已經拋棄)

採用全部權模式。

 

auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.」));
auto_ptr<string> p2;
p2 = p1; //auto_ptr不會報錯.

此時不會報錯,p2剝奪了p1的全部權,可是當程序運行時訪問p1將會報錯。因此auto_ptr的缺點是:存在潛在的內存崩潰問題!

 

2. unique_ptr(替換auto_ptr)

unique_ptr實現獨佔式擁有或嚴格擁有概念,保證同一時間內只有一個智能指針能夠指向該對象。它對於避免資源泄露(例如「以new建立對象後由於發生異常而忘記調用delete」)特別有用。

採用全部權模式,仍是上面那個例子

unique_ptr<string> p3 (new string ("auto"));   //#4
unique_ptr<string> p4;                       //#5
p4 = p3;//此時會報錯!!

若是確實想執行相似的操做,要安全的重用這種指針,可給它賦新值。C++有一個標準庫函數std::move(),讓你可以將一個unique_ptr賦給另外一個。例如:

unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;

 

 

3. shared_ptr

shared_ptr實現共享式擁有概念。多個智能指針能夠指向相同對象,該對象和其相關資源會在「最後一個引用被銷燬」時候釋放。從名字share就能夠看出了資源能夠被多個指針共享,它使用計數機制來代表資源被幾個指針共享。能夠經過成員函數use_count()來查看資源的全部者個數。除了能夠經過new來構造,還能夠經過傳入auto_ptr, unique_ptr,weak_ptr來構造。當咱們調用release()時,當前指針會釋放資源全部權,計數減一。當計數等於0時,資源會被釋放。

shared_ptr 是爲了解決 auto_ptr 在對象全部權上的侷限性(auto_ptr 是獨佔的), 在使用引用計數的機制上提供了能夠共享全部權的智能指針。

成員函數:

use_count 返回引用計數的個數

unique 返回是不是獨佔全部權( use_count 爲 1)

swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)

reset 放棄內部對象的全部權或擁有對象的變動, 會引發原有對象的引用計數的減小

get 返回內部對象(指針), 因爲已經重載了()方法, 所以和直接使用對象是同樣的.如 shared_ptr<int> sp(new int(1)); sp 與 sp.get()是等價的

 

4. weak_ptr

weak_ptr 是一種不控制對象生命週期的智能指針, 它指向一個 shared_ptr 管理的對象. 進行該對象的內存管理的是那個強引用的 shared_ptr. weak_ptr只是提供了對管理對象的一個訪問手段。weak_ptr 設計的目的是爲配合 shared_ptr 而引入的一種智能指針來協助 shared_ptr 工做, 它只能夠從一個 shared_ptr 或另外一個 weak_ptr 對象構造, 它的構造和析構不會引發引用記數的增長或減小。weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,若是說兩個shared_ptr相互引用,那麼這兩個指針的引用計數永遠不可能降低爲0,資源永遠不會釋放。它是對對象的一種弱引用,不會增長對象的引用計數,和shared_ptr之間能夠相互轉化,shared_ptr能夠直接賦值給它,它能夠經過調用lock函數來得到shared_ptr。

注意的是咱們不能經過weak_ptr直接訪問對象的方法,好比B對象中有一個方法print(),咱們不能這樣訪問,pa->pb_->print(); 英文pb_是一個weak_ptr,應該先把它轉化爲shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();

 

 

請回答一下數組和指針的區別

 
指針和數組的主要區別以下:

 

指針

數組

保存數據的地址

保存數據

間接訪問數據,首先得到指針的內容,而後將其做爲地址,從該地址中提取數據

直接訪問數據,

一般用於動態的數據結構

一般用於固定數目且數據類型相同的元素

經過Malloc分配內存,free釋放內存

隱式的分配和刪除

一般指向匿名數據,操做匿名函數

自身即爲數據名

 
 
 
 

C++虛函數和虛函數表原理

 虛表是一個指針數組,其元素是虛函數的指針,每一個元素對應一個虛函數的函數指針。須要指出的是,普通的函數即非虛函數,其調用並不須要通過虛表,因此虛表的元素並不包括普通函數的函數指針。 
 
 

虛表是屬於類的,而不是屬於某個具體的對象,一個類只須要一個虛表便可。同一個類的全部對象都使用同一個虛表。 
爲了指定對象的虛表,對象內部包含一個虛表的指針,來指向本身所使用的虛表。爲了讓每一個包含虛表的類的對象都擁有一個虛表指針,編譯器在類中添加了一個指針,*__vptr,用來指向虛表。這樣,當類的對象在建立時便擁有了這個指針,且這個指針的值會自動被設置爲指向類的虛表。

 

多繼承狀況下,派生類中有多個虛函數表,虛函數的排列方式和繼承的順序一致。派生類重寫函數將會覆蓋全部虛函數表的同名內容,派生類自定義新的虛函數將會在第一個類的虛函數表的後面進行擴充。

 
 

 爲何析構函數必須是虛函數?爲何C++默認的析構函數不是虛函數

將可能會被繼承的父類的析構函數設置爲虛函數,能夠保證當咱們new一個子類,而後使用基類指針指向該子類對象,釋放基類指針時能夠釋放掉子類的空間,防止內存泄漏。

類析構順序:1)派生類自己的析構函數;2)對象成員析構函數;3)基類析構函數。

C++默認的析構函數不是虛函數是由於虛函數須要額外的虛函數表和虛表指針,佔用額外的內存。而對於不會被繼承的類來講,其析構函數若是是虛函數,就會浪費內存。所以C++默認的析構函數不是虛函數,而是隻有當須要看成父類時,設置爲虛函數。

 
 

請你來講一下函數指針

一、定義

函數指針是指向函數的指針變量。

函數指針自己首先是一個指針變量,該指針變量指向一個具體的函數。這正如用指針變量可指向整型變量、字符型、數組同樣,這裏是指向函數。

C在編譯時,每個函數都有一個入口地址,該入口地址就是函數指針所指向的地址。有了指向函數的指針變量後,可用該指針變量調用函數,就如同用指針變量可引用其餘類型變量同樣,在這些概念上是大致一致的。

二、用途:

調用函數和作函數的參數,好比回調函數。

三、示例:

char * fun(char * p)  {…}       // 函數fun

char * (*pf)(char * p);             // 函數指針pf

pf = fun;                        // 函數指針pf指向函數fun

pf(p);                        // 經過函數指針pf調用函數fun

 

 

請你來講一下fork函數

Fork:建立一個和當前進程映像同樣的進程能夠經過fork( )系統調用:
#include <sys/types.h>

#include <unistd.h>

pid_t fork(void);

 

成功調用fork( )會建立一個新的進程,它幾乎與調用fork( )的進程如出一轍,這兩個進程都會繼續運行。在子進程中,成功的fork( )調用會返回0。在父進程中fork( )返回子進程的pid。若是出現錯誤,fork( )返回一個負值。

 

map和set有什麼區別,分別又是怎麼實現的?

紅黑樹佔坑
 
 

請你來介紹一下STL的allocaotr

STL的分配器用於封裝STL容器在內存管理上的底層細節。在C++中,其內存配置和釋放以下:

new運算分兩個階段:(1)調用::operator new配置內存;(2)調用對象構造函數構造對象內容

delete運算分兩個階段:(1)調用對象希構函數;(2)掉員工::operator delete釋放內存

爲了精密分工,STL allocator將兩個階段操做區分開來:內存配置有alloc::allocate()負責,內存釋放由alloc::deallocate()負責;對象構造由::construct()負責,對象析構由::destroy()負責。

同時爲了提高內存管理的效率,減小申請小內存形成的內存碎片問題,SGI STL採用了兩級配置器,當分配的空間大小超過128B時,會使用第一級空間配置器;當分配的空間大小小於128B時,將使用第二級空間配置器。第一級空間配置器直接使用malloc()、realloc()、free()函數進行內存空間的分配和釋放,而第二級空間配置器採用了內存池技術,經過空閒鏈表來管理內存。

 
 

C++中類成員的訪問權限

C++經過 public、protected、private 三個關鍵字來控制成員變量和成員函數的訪問權限,它們分別表示公有的、受保護的、私有的,被稱爲成員訪問限定符。在類的內部(定義類的代碼內部),不管成員被聲明爲 public、protected 仍是 private,都是能夠互相訪問的,沒有訪問權限的限制。在類的外部(定義類的代碼以外),只能經過對象訪問成員,而且經過對象只能訪問 public 屬性的成員,不能訪問 private、protected 屬性的成員
 
 

C++中struct和class的區別

在C++中,能夠用struct和class定義類,均可以繼承。區別在於:structural的默認繼承權限和默認訪問權限是public,而class的默認繼承權限和默認訪問權限是private。

另外,class還能夠定義模板類形參,好比template <class T, int i>。

 
 

C++中繼承權限的區別

 
公有繼承時基類中各成員屬性保持不變,基類中private成員被隱藏。派生類的成員只能訪問基類中的public/protected成員,而不能訪問private成員;派生類的對象只能訪問基類中的public成員。
私有繼承時基類中各成員屬性均變爲private,而且基類中private成員被隱藏。派生類的成員也只能訪問基類中的public/protected成員,而不能訪問private成員;派生類的對象不能訪問基類中的任何的成員。 
保護繼承時基類中各成員屬性均變爲protected,而且基類中private成員被隱藏。派生類的成員只能訪問基類中的public/protected成員,而不能訪問private成員;派生類的對象不能訪問基類中的任何的成員。
 
//公有繼承                      對象訪問    成員訪問
public    -->  public              Y         Y
protected -->  protected           N         Y
private   -->  private             N         N
 
//保護繼承                      對象訪問    成員訪問
public    -->  protected           N         Y
protected -->  protected           N         Y
private   -->  protected           N         N
 
//私有繼承                      對象訪問    成員訪問
public    -->  private             N         Y
protected -->  private             N         Y
private   -->  private             N         N

 

 

 C++源文件從文本到可執行文件經歷的過程?

 對於C++源文件,從文本到可執行文件通常須要四個過程:

預處理階段:對源代碼文件中文件包含關係(頭文件)、預編譯語句(宏定義)進行分析和替換,生成預編譯文件。

編譯階段:將通過預處理後的預編譯文件轉換成特定彙編代碼,生成彙編文件

彙編階段:將編譯階段生成的彙編文件轉化成機器碼,生成可重定位目標文件

連接階段:將多個目標文件及所須要的庫鏈接成最終的可執行目標文件

 

 

include頭文件的順序以及雙引號」」和尖括號<>的區別?

Include頭文件的順序:對於include的頭文件來講,若是在文件a.h中聲明一個在文件b.h中定義的變量,而不引用b.h。那麼要在a.c文件中引用b.h文件,而且要先引用b.h,後引用a.h,不然彙報變量類型未聲明錯誤。

雙引號和尖括號的區別:編譯器預處理階段查找頭文件的路徑不同。

對於使用雙引號包含的頭文件,查找頭文件路徑的順序爲:

當前頭文件目錄

編譯器設置的頭文件路徑(編譯器可以使用-I顯式指定搜索路徑)

系統變量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的頭文件路徑

對於使用尖括號包含的頭文件,查找頭文件的路徑順序爲:

編譯器設置的頭文件路徑(編譯器可以使用-I顯式指定搜索路徑)

系統變量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的頭文件路徑

 

malloc的原理,另外brk系統調用和mmap系統調用的做用分別是什麼?

Malloc函數用於動態分配內存。爲了減小內存碎片和系統調用的開銷,malloc其採用內存池的方式,先申請大塊內存做爲堆區,而後將堆區分爲多個內存塊,以塊做爲內存管理的基本單位。當用戶申請內存時,直接從堆區分配一塊合適的空閒塊。Malloc採用隱式鏈表結構將堆區分紅連續的、大小不一的塊,包含已分配塊和未分配塊;同時malloc採用顯示鏈表結構來管理全部的空閒塊,即便用一個雙向鏈表將空閒塊鏈接起來,每個空閒塊記錄了一個連續的、未分配的地址。

當進行內存分配時,Malloc會經過隱式鏈表遍歷全部的空閒塊,選擇知足要求的塊進行分配;當進行內存合併時,malloc採用邊界標記法,根據每一個塊的先後塊是否已經分配來決定是否進行塊合併。

Malloc在申請內存時,通常會經過brk或者mmap系統調用進行申請。其中當申請內存小於128K時,會使用系統函數brk在堆區中分配;而當申請內存大於128K時,會使用系統函數mmap在映射區分配。

 

C++的內存管理是怎樣的?

在C++中,虛擬內存分爲代碼段、數據段、BSS段、堆區、文件映射區以及棧區六部分。

代碼段:包括只讀存儲區和文本區,其中只讀存儲區存儲字符串常量,文本區存儲程序的機器代碼。

數據段:存儲程序中已初始化的全局變量和靜態變量

bss 段:存儲未初始化的全局變量和靜態變量(局部+全局),以及全部被初始化爲0的全局變量和靜態變量。

堆區:調用new/malloc函數時在堆區動態分配內存,同時須要調用delete/free來手動釋放申請的內存。

映射區:存儲動態連接庫以及調用mmap函數進行的文件映射

棧:使用棧空間存儲函數的返回地址、參數、局部變量、返回值

 

 

C++/C的內存分配 

 
 
 

 何時會發生段錯誤

 段錯誤一般發生在訪問非法內存地址的時候,具體來講分爲如下幾種狀況:

使用野指針

試圖修改字符串常量的內容

 

 

什麼是memory leak,也就是內存泄漏

內存泄漏(memory leak)是指因爲疏忽或錯誤形成了程序未能釋放掉再也不使用的內存的狀況。內存泄漏並不是指內存在物理上的消失,而是應用程序分配某段內存後,因爲設計錯誤,失去了對該段內存的控制,於是形成了內存的浪費。

內存泄漏的分類:

1. 堆內存泄漏 (Heap leak)。對內存指的是程序運行中根據須要分配經過malloc,realloc new等從堆中分配的一塊內存,再是完成後必須經過調用對應的 free或者delete 刪掉。若是程序的設計的錯誤致使這部份內存沒有被釋放,那麼此後這塊內存將不會被使用,就會產生Heap Leak.

2. 系統資源泄露(Resource Leak)。主要指程序使用系統分配的資源好比 Bitmap,handle ,SOCKET等沒有使用相應的函數釋放掉,致使系統資源的浪費,嚴重可致使系統效能下降,系統運行不穩定。

3. 沒有將基類的析構函數定義爲虛函數。當基類指針指向子類對象時,若是基類的析構函數不是virtual,那麼子類的析構函數將不會被調用,子類的資源沒有正確是釋放,所以形成內存泄露。

 

 

new和malloc的區別

一、new分配內存按照 數據類型進行分配,malloc分配內存按照指定的 大小分配;

二、new返回的是指定對象的指針,而malloc返回的是void*,所以malloc的返回值通常都須要進行類型轉化。

三、new不只分配一段內存,並且會調用構造函數,malloc不會。

四、new分配的內存要用delete銷燬,malloc要用free來銷燬;delete銷燬的時候會調用對象的析構函數,而free則不會。

五、new是一個操做符能夠重載,malloc是一個庫函數。

六、malloc分配的內存不夠的時候,能夠用realloc擴容。擴容的原理?new沒用這樣操做。

七、new若是分配失敗了會拋出bad_malloc的異常,而malloc失敗了會返回NULL。

八、申請數組時: new[]一次分配全部內存,屢次調用構造函數,搭配使用delete[],delete[]屢次調用析構函數,銷燬數組中的每一個對象。而malloc則只能sizeof(int) * n。

 

共享內存相關api

Linux容許不一樣進程訪問同一個邏輯內存,提供了一組API,頭文件在sys/shm.h中。

1)新建共享內存shmget

int shmget(key_t key,size_t size,int shmflg);

key:共享內存鍵值,能夠理解爲共享內存的惟一性標記。

size:共享內存大小

shmflag:建立進程和其餘進程的讀寫權限標識。

返回值:相應的共享內存標識符,失敗返回-1

2)鏈接共享內存到當前進程的地址空間shmat

void *shmat(int shm_id,const void *shm_addr,int shmflg);

shm_id:共享內存標識符

shm_addr:指定共享內存鏈接到當前進程的地址,一般爲0,表示由系統來選擇。

shmflg:標誌位

返回值:指向共享內存第一個字節的指針,失敗返回-1

3)當前進程分離共享內存shmdt

int shmdt(const void *shmaddr);

4)控制共享內存shmctl

和信號量的semctl函數相似,控制共享內存

int shmctl(int shm_id,int command,struct shmid_ds *buf);

shm_id:共享內存標識符

command: 有三個值

IPC_STAT:獲取共享內存的狀態,把共享內存的shmid_ds結構複製到buf中。

IPC_SET:設置共享內存的狀態,把buf複製到共享內存的shmid_ds結構。

IPC_RMID:刪除共享內存

buf:共享內存管理結構體。

 
 

請本身設計一下如何採用單線程的方式處理高併發

在單線程模型中,能夠採用I/O複用來提升單線程處理多個請求的能力,而後再採用事件驅動模型,基於異步回調來處理事件來
 
 
請你詳細介紹一下C++11中的可變參數模板、右值引用和lambda這幾個新特性。
 
 

 C++重載,重寫與虛函數:

重載:一個類裏有多個同名函數,可是它們的參數列表不一樣,就構成了重載
覆蓋:① 父類裏的函數是虛函數 ②子類中有這個函數的同名參數列表也相同的函數就構成了重寫。
隱藏:當子類有父類的同名函數時,會隱藏父類的函數,不能構成重載。
 
 

紅黑樹和AVL樹的定義,特色,以及兩者區別

平衡二叉樹(AVL樹):

平衡二叉樹又稱爲AVL樹,是一種特殊的二叉排序樹。其左右子樹都是平衡二叉樹,且左右子樹高度之差的絕對值不超過1。一句話表述爲:以樹中全部結點爲根的樹的左右子樹高度之差的絕對值不超過1。將二叉樹上結點的左子樹深度減去右子樹深度的值稱爲平衡因子BF,那麼平衡二叉樹上的全部結點的平衡因子只多是-一、0和1。只要二叉樹上有一個結點的平衡因子的絕對值大於1,則該二叉樹就是不平衡的。

 

紅黑樹:

紅黑樹是一種二叉查找樹,但在每一個節點增長一個存儲位表示節點的顏色,能夠是紅或黑(非紅即黑)。經過對任何一條從根到葉子的路徑上各個節點着色的方式的限制,紅黑樹確保沒有一條路徑會比其它路徑長出兩倍,所以,紅黑樹是一種弱平衡二叉樹,相對於要求嚴格的AVL樹來講,它的旋轉次數少,因此對於搜索,插入,刪除操做較多的狀況下,一般使用紅黑樹。

性質:

1. 每一個節點非紅即黑

2. 根節點是黑的;

3. 每一個葉節點(葉節點即樹尾端NULL指針或NULL節點)都是黑的;

4. 若是一個節點是紅色的,則它的子節點必須是黑色的。

5. 對於任意節點而言,其到葉子點樹NULL指針的每條路徑都包含相同數目的黑節點;

 

區別:

AVL 樹是高度平衡的,頻繁的插入和刪除,會引發頻繁的rebalance,致使效率降低;紅黑樹不是高度平衡的,算是一種折中,插入最多兩次旋轉,刪除最多三次旋轉。

 

紅黑樹較AVL樹的優勢:

AVL 樹是高度平衡的,頻繁的插入和刪除,會引發頻繁的rebalance,致使效率降低;紅黑樹不是高度平衡的,算是一種折中,插入最多兩次旋轉,刪除最多三次旋轉。

因此紅黑樹在查找,插入刪除的性能都是O(logn),且性能穩定,因此STL裏面不少結構包括map底層實現都是使用的紅黑樹。

 
 

map和unordered_map的底層實現

map底層是基於紅黑樹實現的,所以map內部元素排列是有序的。而unordered_map底層則是基於哈希表實現的,所以其元素的排列順序是雜亂無序的。
 
 

須要無序容器,快速查找刪除,不擔憂略高的內存時用unordered_map;有序容器穩定查找刪除效率,內存很在乎時候用map。

對於map,其底層是基於紅黑樹實現的,優勢以下:

1)有序性,這是map結構最大的優勢,其元素的有序性在不少應用中都會簡化不少的操做

2)map的查找、刪除、增長等一系列操做時間複雜度穩定,都爲logn

缺點以下:

1)查找、刪除、增長等操做平均時間複雜度較慢,與n相關

對於unordered_map來講,其底層是一個哈希表,優勢以下:

查找、刪除、添加的速度快,時間複雜度爲常數級O(c)

缺點以下:

由於unordered_map內部基於哈希表,以(key,value)對的形式存儲,所以空間佔用率高

Unordered_map的查找、刪除、添加的時間複雜度不穩定,平均爲O(c),取決於哈希函數。極端狀況下可能爲O(n)

 

 

棧和堆的區別,以及爲何棧要快 

 堆和棧的區別:

堆是由低地址向高地址擴展;棧是由高地址向低地址擴展

 

堆中的內存須要手動申請和手動釋放;棧中內存是由OS自動申請和自動釋放,存放着參數、局部變量等內存

 

堆中頻繁調用malloc和free,會產生內存碎片,下降程序效率;而棧因爲其先進後出的特性,不會產生內存碎片

 

堆的分配效率較低,而棧的分配效率較高

 

棧的效率高的緣由:

 

棧是操做系統提供的數據結構,計算機底層對棧提供了一系列支持:分配專門的寄存器存儲棧的地址,壓棧和入棧有專門的指令執行;而堆是由C/C++函數庫提供的,機制複雜,須要一些列分配內存、合併內存和釋放內存的算法,所以效率較低。

 

 

 堆和棧的區別

1)申請方式:

棧由系統自動分配和管理,堆由程序員手動分配和管理。

2)效率:

棧由系統分配,速度快,不會有內存碎片。

堆由程序員分配,速度較慢,可能因爲操做不當產生內存碎片。

3)擴展方向

棧從高地址向低地址進行擴展,堆由低地址向高地址進行擴展。

4)程序局部變量是使用的棧空間,new/malloc動態申請的內存是堆空間,函數調用時會進行形參和返回值的壓棧出棧,也是用的棧空間。

 
 

 Array&List, 數組和鏈表的區別

數組的優勢:

1. 隨機訪問性強

2. 查找速度快

數組的缺點:

1. 插入和刪除效率低

2. 可能浪費內存

3. 內存空間要求高,必須有足夠的連續內存空間。

4. 數組大小固定,不能動態拓展

 

鏈表的優勢:

1. 插入刪除速度快

2. 內存利用率高,不會浪費內存

3. 大小沒有固定,拓展很靈活。

鏈表的缺點:

不能隨機查找,必須從第一個開始遍歷,查找效率低

 

 

快排代碼

 
 

 第K大

 
 

 各類排序

 
 

解決hash衝突的方法

當哈希表關鍵字集合很大時,關鍵字值不一樣的元素可能會映象到哈希表的同一地址上,這樣的現象稱爲哈希衝突。目前經常使用的解決哈希衝突的方法以下:

開放定址法: 當發生地址衝突時,按照某種方法繼續探測哈希表中的其餘存儲單元,直到找到空位置爲止。

再哈希法:當發生哈希衝突時使用另外一個哈希函數計算地址值,直到衝突再也不發生。這種方法不易產生彙集,可是增長計算時間,同時須要準備許多哈希函數。

鏈地址法:將全部哈希值相同的Key經過鏈表存儲。key按順序插入到鏈表中

創建公共溢出區:採用一個溢出表存儲產生衝突的關鍵字。若是公共溢出區還產生衝突,再採用處理衝突方法處理。

 
 

最長公共子串 and 最長公共連續子序列

最長公共子串長度就爲max{c[i][j]}了

 

 

 

最長迴文子串 

 
 

反轉鏈表 

 
 

設計模式

 
 

git中Merge和rebase區別

 
 
 
 
 

 實現一個LRU cache

 
 
 
 
 
 
 

 動態連接 靜態連接

相關文章
相關標籤/搜索