一、 C/C++程序基礎
面試例題1——分析代碼寫輸出(一般賦值語句的概念和方法)。
面試例題2——分析代碼寫運行結果(C++域操作符)。
面試例題3——分析代碼寫輸出(i++和++i的區別)。
面試例題4——i++和++i哪個效率更高。++i可以返回對象引用,i++必須返回對象的值。在內建數據類型的情況下,效率沒有區別。在自定義數據類型的情況下,++i效率較高。
面試例題5——選擇編程風格良好的條件比較語句。
面試例題6——分析代碼寫結果(有符號變量與無符號變量值的轉換,負數最高位為1)。
面試例題7——將數a、b的值進行交換,并且不使用任何中間變量(局部變量、加減運算、異或運算)。
面試例題8——C和C++有什么不同(面向過程與面向對象)。
面試例題9——C++是面向對象化的而C是面向過程化的?
面試例題10——為什么標準頭文件都有類似一下所示的結構?(預處理結構,如ifndef、define、extern “C”、endif等)。
面試例題11——頭文件引用中< >(工程或標準頭文件)和“ ”(用戶提供)的區別。
面試例題12——C++中main函數執行完后還執行其他語句(exit()、atexit()函數)。
二、 預處理、const、static與sizeof
面試例題1——分析代碼寫結果(預處理的使用):#ifdef、#else、#endif。
面試例題2——用#define實現宏,并求最大值和最小值。
類如#define MAX(x, y) (((x)>(y)) ? (x): (y))
面試例題3——分析代碼寫結果(宏定義的使用)。
面試例題4——分析代碼寫結果(宏參數的連接)。
面試例題5——用宏定義得到一個字的高位和低位字節。
#define WORD_LO(xxx) ((byte)((word)(xxx)&255))
#define WORD_HI(xxx) ((byte)((word)(xxx)>>8))
面試例題6——用宏定義得到一個數組所含的元素個數。
#define ARR_SIZE(a) (sizeof((a))/sizeof((a[0])))
面試例題7——找錯(const的使用)。
面試例題8——請說明const與#define的各自特點和區別。
面試例題9——C++中const的作用:定義常量、修飾函數形式參數(如const &參數,提高效率)、修飾函數的返回值(返回值不能被直接修改)、修飾類的成員函數(函數定義體)。
面試例題10——static的作用:在函數體靜態變量維持其值不變;在模塊內靜態變量不能被模塊外其他函數訪問;在模塊內靜態函數只能被這一模塊內的其他函數調用。
面試例題11——static全局變量與普通全局變量有什么區別(前者只初始化一次,防止在其他單元中被引用)。static局部變量和普通局部變量有什么區別(前者只初始化一次,下一次依據上一次結果值)。static函數與普通函數有什么區別(前者在內存中只有一份,后者在每個被調用中維持一份復制品)。
面試例題12——分析代碼寫結果(C++類的靜態成員):調用時用“::”域操作符。
面試例題13——填空(使用sizeof計算普通變量所占空間)。
面試例題14——分析代碼寫結果(使用sizeof計算類對象所占空間大小)。填充字節(滿足最寬的基本類型成員的大小整除)。
面試例題15——分析代碼寫結果(使用sizeof計算含有虛函數的類對象的空間大小)。隱含的虛表指針成員(8字節)。
面試例題16——分析代碼寫結果(使用sizeof計算虛擬繼承的類對象的空間大小)。編譯器為虛擬繼承的子類安插一個指向父類的指針,大小為4。
面試例題17——sizeof與strlen的區別(前者空間大小,后者計算字符串長度)。
面試例題18——sizeof用途(與存儲分配和I/O系統的例程進行通信;查看某個類型的對象在內存中所占的單元字節;動態分配對象時,可以使系統知道要分配多少內存;便于一些類型的擴充;建議在涉及操作數字節大小時用sizeof來代替常量計算;如果操作數是函數中的數組形參或函數類型的形參,sizeof給出其指針的大小)。
面試例題19——找錯(使用strlen()函數代替sizeof計算sizeof計算字符串長度)。
面試例題20——分析代碼寫結果(使用sizeof計算聯合體的大小)。
面試例題21——選擇題(#pragma pack的作用:設置對齊方式)。
面試例題22——為什么要引入內聯函數(用它代替C中表達式形式的宏定義,解決程序中函數調用的效率問題)。
面試例題23——為什么inline能很好地取代表達式形式的預定義(沒有了調用的開銷,效率很高;仍舊是一個真正的函數;可以作為某個類的成員函數)。
面試例題24——內聯函數使用的場合(設有或者保護成員的讀寫)。
面試例題25——為什么不把所有的函數都定義成內聯函數(內聯是以代碼膨脹(復制)為代價,僅僅省去了函數調用的開銷,從而提高函數的執行效率)。
面試例題26——內聯函數與宏有什么區別(前者在編譯時展開,宏在預編譯時展開;在編譯的時候內聯函數可以直接被嵌入到目標代碼中,而宏只是一個簡單的文本替換;前者可以完成諸如類檢測、語句是否正確等編譯功能,宏就不具有這樣的功能;宏不是函數,前者是函數;宏在定義時要小心處理宏參數,否則容易產生二義性,而前者不會出現二義性。
三、 引用和指針
面試例題1——分析代碼寫結果(一般變量引用:&)。
面試例題2——分析代碼寫結果(指針變量引用:&)。
面試例題3——分析代碼找錯誤(變量引用)。
面試例題4——交換兩個字符串(參數引用)。
面試例題5——程序差錯(參數引用)。
面試例題6——程序差錯(參數引用:常量引用類型在函數體內其值不能被修改;非常量引用不能作為常量的引用)。
面試例題7——指針與引用的區別(初始化要求不同,引用要求創建同時必須初始化,指針可以在定義后的任何地方重新賦值;可修改性不同,引用一旦被初始化,它就不能被另一個對象引用,而指針可以;不存在NULL引用;測試時,使用引用之前不需要測試它的合法性,而指針可以指向NULL,故必須經常進行測試;應用上,如果指向一個對象就不會改變指向,那么應該使用引用。)。
面試例題8——為什么引用比指針安全(不存在NULL引用)。
面試例題9——復雜指針的聲明(首先從最里面的圓括號看起,然后往右看,再往左看)。
面試例題10——分析代碼寫結果(用指針賦值)。
面試例題11——分析代碼寫結果(指針加減操作)。
面試例題12——分析代碼寫結果(指針比較)。
面試例題13——分析代碼找錯誤(內存訪問違規)。
面試例題14——分析代碼找錯誤(指針的隱式轉換)。
面試例題15——指針常量和常量指針的區別(前者是指針形式的常量,不可改變地址的指針,但可以對它指向的內容修改;后者是常量形式的指針,即常量指針是指向常量的一個指針,所指向的地址的內容不能改變。)。
面試例題16——同15。
面試例題17——同15。
面試例題18——this指針的正確敘述(類的非靜態成員函數才有this指針)。
面試例題19——同18。
面試例題20——指針數組和數組指針(前者表示數組的元素是指針形式,后者表示指向數組的指針。)。
面試例題21——同20。
面試例題22——函數指針和指針函數(指針函數是指帶指針的函數,即本質是一個函數,并且返回的是某一類型的指針。函數指針是指向函數的指針變量。)。
面試例題23——數組指針和函數指針的定義。
面試例題24——各種指針的定義(函數指針、函數返回指針、const指針、指向const的指針、指向const的const指針)。
面試例題25——代碼改錯(函數指針的使用)。
面試例題26——分析代碼寫結果(函數指針的使用)。
面試例題27——typedef用于函數指針定義(使用typedef自定義數據類型)。
面試例題28——“野指針”是什么(不是NULL指針,是指向“垃圾”內存的指針。成因:指針變量沒有初始化;指針p被free或者delete之后,沒有置為NULL。)。
面試例題29——分析代碼差錯(“野指針”的危害:程序崩潰)。
面試例題30——malloc/free和new/delete(前者是庫函數而不是運算符,不能滿足動態對象的要求,不能夠把執行構造函數和析構函數的任務強加給它)。
面試例題31——程序改錯(“野指針”,即指針的初始化)。
面試例題32——各種內存分配和釋放的函數的聯系和區別:malloc、calloc、realloc、free等。調用格式如下:
(類型*) malloc (size); 長度為size的內存。
(類型*) calloc(n, size); 長度為size的n塊內存。
(類型*) realloc(*p, size); 將p的長度增大到size。
free(void *p); 釋放。
面試例題33——程序找錯(動態內存的傳遞)。
面試例題34——程序分析(動態內存的傳遞:在C中采用指向指針的指針解決動態內存不能傳遞的問題;在C++中采用傳遞指針的引用解決;也可使用函數返回值來傳遞動態內存。)。
面試例題35——比較分析兩個代碼段的輸出(動態內存的傳遞)。
面試例題36——程序差錯(“野指針”用于變量值的互換)。
面試例題37——內存的分配方式(靜態存儲區(如全局變量)、棧(效率很高,但是分配的村內存容量有限)、堆(亦稱動態內存分配))。
面試例題38——句柄(用來標識項目的,這些項目包括:模塊module;任務task;實例instance;文件file;內存塊block of memory;菜單menu;控制control;字體font;資源resource(包括圖標icon、光標cursor、字符串string等);GDI對象GDI object(包括位圖bitmap、畫刷brush、元文件metafile、調色板palette、畫筆pen、區域region以及設備描述表device context)。程序執行順序:句柄地址(穩定)→記載對象在內存中的地址→對象在內存中的地址(不穩定)→實際對象。)。
面試例題39——指針和句柄的區別(句柄所指可以是一個復雜的結構,并且可以與系統有關;指針也可以指向一個復雜的結構,但是通常是由用戶定義的,所以必要的工作要由用戶完成,特別刪除部分的工作。)。
四、 字符串
面試例題1——使用庫函數將數字轉換為字符串:itoa()為整型;ltoa()為長整型;ulota()為無符號長整型;gcvt()為浮點數,取四舍五入;ecvt()為雙精度浮點型,結果中不包含十進制小數點;fcvt()指定位數為轉換精度,其余同ecvt()。
面試例題2——不適用庫函數將數字轉換為字符串:ASCII碼。
面試例題3——使用庫函數將字符串轉換為數字:atof雙精度浮點型、atoi整型、atol長整型、strtod雙精度浮點數、strtol長整型、strtoul無符號長整型。后面三者不同于前面的區別在于:它們報告不能轉換的所有剩余字符。
面試例題4——不使用庫函數將字符串轉換為數字:ASCII碼。
面試例題5——編程實現strcpy函數。
面試例題6——編程實現memcpy函數。
面試例題7——strcpy和memcpy的區別:復制的內容不同,前者只能復制字符串,后者可以復制任意內容,例如字符數組、整型、結構體、類等;復制的方法不同,前者不需要指定長度,遇到字符串結束符“\0”便結束,后者需要給定復制長度的參數;用途不同。
面試例題8——該錯(數組越界)。
面試例題9——分析程序(數組越界)。
面試例題10——分析程序(打印操作可能產生數組越界)。
面試例題11——編程實現字符串的長度檢測(strlen函數)。
面試例題12——編程實現字符串中子串的查找(strstr函數)。
面試例題13——編程實現字符串中各單詞的翻轉(查找空格和字符串的結束符號)。
面試例題14——編程判斷字符串是否為回文(先判斷長度,再比較)。
面試例題15——編程實現strcmp庫函數(判斷字符串相等)。
面試例題16——編程查找兩個字符串的最大公共子串。
面試例題17——不能使用printf,將十進制數以二進制和十六進制的形式輸出(用字符串表示十進制數)。
面試例題18——在字符串中,插入字符統計的個數。
面試例題19——字符串編碼例題。
面試例題20——反轉字符串,但其指定的子串不反轉(編寫函數對輸入字符串和子串進行反轉,然后在輸入字符串的反轉中查找子串的反轉)。
面試例題21——編寫字符串反轉函數strrev(法一:遍歷字符串,將第一個字符和最后一個字符交換,再往中間循環;法二:通過數組下標的方式訪問字符串,也可以用指針直接操作;法三:異或運算符;法四:+和-運算符。)。
面試例題22——編程實現任意長度的兩個正整數相加。
面試例題23——編程實現字符串循環右移。
面試例題24——從字符串的指定位置開始,刪除其指定長度字符。
面試例題25——字符串的排序及交換。
面試例題26——編程實現刪除字符串中所有指定的字符。
面試例題27——分析代碼(使用strcat函數連接字符串)。
面試例題28——編程實現庫函數strcat。
面試例題29——計算含有漢字的字符串的長度。
面試例題30——找出01字符串中0和1連續出現的最大次數。
面試例題31——實現字符串的替換。
五、 位運算與嵌入式編程
面試例題1——分析代碼寫結果(位制轉換:使用printf輸出不同類型變量)。
面試例題2——分析代碼寫出結果(位運算:左移<<(相當于乘法)和右移>>(相當于除法))。
面試例題3——設置或清除特定的位(使用“&”和“|”位操作符)。
面試例題4——計算一字節里有多少位被置1。
面試例題5——位運算改錯(位運算符和邏輯運算符)。
面試例題6——運用位運算交換a、b兩個數。
面試例題7——列舉并解釋C++中的四種運算符轉化,說明它們的不同點(const_cast、dynamic_cast、reinterpret_cast和static_cast)。
面試例題8——用#define聲明一個常數,用以表明一年中有多少秒。如:
#define SECONDS_PER_YEAR(60*60*24*365)UL
面試例題9——用C語言寫死循環(while(1){}或for(;;){}或loop:…goto loop;)。
面試例題10——如果訪問特定位置的內存。
面試例題11——對中斷服務代碼的評論(_interrupt)。
面試例題12——分析代碼寫結果(整數的自動轉換)。
面試例題13——關鍵字static的作用。
面試例題14——關鍵字volatile的作用:定義為volatile的變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值。幾個例子:并行設備的硬件寄存器(如狀態寄存器);一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables);多線程應用中被幾個任務共享的變量。
面試例題15——判斷處理器使用Big_endian還是Little_endian模式存儲數據(若是前者,返回0;若是后者,返回1。)。
面試例題16——評價代碼片斷(處理器字長)。
六、 C++面向對象
面試例題1——面向對象技術的基本概率(類(抽象、繼承、封裝、重載、多態)、對象、消息)。
面試例題2——判斷題(類的基本概念)。
面試例題3——選C相比,C++的改進(面向對象)。
面試例題4——class和struct的區別(在C中,struct只是作為一種復雜數據類型定義,不能用于面向對象編程;C++中對于成員訪問權限及繼承方式,class中默認的是private,而struct中則是public。class還可以用于表示模板類型,struct則不行。)。
面試例題5——改錯(C++類對象的聲明,與構造函數相匹配)。
面試例題6——分析代碼寫結果(C++類成員的訪問)。
面試例題7——找錯(類成員的初始化)。
面試例題8-——分析代碼寫結果(靜態成員變量的使用:是該類類型的全局變量。靜態成員只是一個,由該類類型的所有對象共享訪問)。
面試例題9——與全局對象相比,使用靜態數據成員有什么優勢(靜態數據成員沒有進入程序的全局名字空間,因此不存在程序中其他全局名字沖突的問題。使用靜態成員可以隱藏信息,因此靜態成員可以是private成員,而全局對象不能。)。
面試例題10——有哪幾種情況只能初始化列表(initialization list),而不能用賦值(assignment):const和reference類型成員變量只能被初始化而不能被賦值操作;類的構造函數需要調用其基類的構造函數。
面試例題11——代碼改錯(靜態成員的使用)。
面試例題12——選擇題(對靜態數據成員的正確描述:不受private控制符作用,可以直接用類名調用。)。
面試例題13——main函數執行以前,會執行什么代碼(全局對象的構造函數)。
面試例題14——C++中的空類,默認情況下會產生哪些類成員函數:默認構造函數和復制構造函數,它們被用于類的對象的構造過程;析構函數,它被用于類的對象的析構過程;賦值函數,它被用于同類的對象間的賦值過程;取值運算,當對類的對象進行取地址(&)時,此函數被調用。
面試例題15——構造函數和析構函數是否可以被重載(前者可以,后者不可以)。
面試例題16——分析代碼(重載構造函數的調用)。
面試例題17——分析代碼(構造函數的使用)。
面試例題18——構造函數explicit與普通構造函數的區別(前者只能被顯式調用,后者能夠被隱式調用)。
面試例題19——分析代碼(explicit構造函數的使用)。
面試例題20——C++中虛析構函數的作用(實現多態。只有當一個類被用來作為基類的時候,才會把析構函數寫成虛函數。)。
面試例題21——分析代碼寫結果(析構函數的執行順序:與構造函數相反)。
面試例題22——拷貝構造函數(復制構造函數)何時調用:一個對象以值傳遞的方式傳入函數體;一個對象以值傳遞的方式從函數返回;一個對象需要通過另外一個對象進行初始化。淺拷貝是讓新舊兩個對象指向同一個外部的內容,而深拷貝是指為新對象制作了外部對象的獨立復制。
面試例題23——什么時候編譯器會生成默認的拷貝構造函數:用戶沒有定義拷貝構造函數而在代碼中使用到了拷貝構造函數時,編譯器就會生成默認的拷貝構造函數。但如果用戶定義了拷貝構造函數,編譯器就不會生成默認的copy constructor。
面試例題24——寫一個繼承類的復制函數的原則是:使用基類的拷貝構造函數。
面試例題25——拷貝構造函數與賦值函數的區別:前者是一個對象初始化一塊內存區域,這塊內存就是新對象的內存區,后者是對于一個已經被初始化的對象來進行賦值操作。前者大多數情況下是復制,后者則是引用對象。實現不一樣,前者首先是一個構造函數,它調用的時候是通過參數的對象初始化一個對象,而后者則是把一個新的對象賦值給一個原有的對象。
面試例題26——編寫類string的構造函數、析構函數和賦值函數。
面試例題27——分析代碼寫結果(C++類各成員函數關系:構造、析構和賦值)。
面試例題28——分析代碼寫結果(C++類的臨時對象)。
面試例題29——分析代碼寫輸出(拷貝構造函數和析構函數)。
面試例題30——分析代碼寫結果(C++靜態成員和臨時對象)。
面試例題31——什么是臨時對象?在什么情況下產生?(僅使用一小段時間的變量稱為臨時變量,應避免它的使用,因為會影響程序的效率。)。
面試例題32——什么是函數重載(用來描述同名函數具有相同或者相似功能,但數據類型或者是參數不同的函數管理操作)。C++支持而C不支持。
面試例題33——函數重載的正確聲明。
面試例題34——重載與覆寫的區別(重載overriding是指子類改寫父類的方法;覆寫overloading是指同一個函數的不同版本之間參數不同。)重載特征:方法名必須相同;參數列表必須不相同;返回值類型可以不相同。覆寫特征:只有虛方法和抽象方法才能夠被覆寫;具有相同的函數名;具有相同的參數列表;具有相同的返回值類型。
面試例題35——重載“=”和“+”運算符。形式:type operator @ (arglist)
面試例題36——各類運算符重載函數的編寫(“<”、“>”、“==”、“!=”、“+=-----”以及輸入輸出運算符)。
面試例題37——分析代碼寫輸出(new操作符重載的使用)。
七、 C++繼承與多態
面試例題1——C++類繼承的三種關系(public、private和protected)。
面試例題2——同1。
面試例題3——同1。
面試例題4——私有繼承作用:編譯器一般不會將派生類對象轉換成基類對象;從私有基類繼承而來的成員成為派生類的私有成員。
面試例題5——私有繼承和組合的相同點和不同點:它們都可以表示“hasa”(即有一個)關系;但私有繼承中派生類訪問基類的protected成員,并且可以重寫基類的虛擬函數(甚至當基類是抽象類時),組合不具有這些功能。選擇它們的原則為盡可能使用組合,萬不得已時使用私有繼承。
面試例題6——多態:編譯時的多態性(重載)、運行時的多態性(虛成員)。
面試例題7——虛函數如何實現:如果一個類中含有虛函數,則系統會為這個類分配一個指針成員指向一張虛函數表(vtbl),表中每一項指向一個虛函數的地址,實現上就是一個函數指針的數組。虛函數表既有繼承性又有多態性。,每個派生類的虛函數表繼承了它各個基類的虛函數表。在類對象的內存分布中,首先是虛函數表指針,然后才是對象數據。
面試例題8——分析代碼寫輸出(構造函數調用虛函數:虛函數不會向下匹配到派生類,而是直接執行基類的函數。)。
面試例題9--——分析代碼寫輸出(虛函數的作用)。
面試例題10——分析代碼寫結果(虛函數)。
面試例題11——選擇題(虛函數相關)。
面試例題12——多重繼承的優點在于對象可以調用多個基類中的接口;缺點是如果派生類所繼承的多個基類有相同的基類,而派生類對象需要調用這個祖先類的接口方法,容易產生二義性(解決方法:加上全局符確定調用復制;使用虛擬繼承。)。
面試例題13——分析代碼找錯(多重繼承中的二義性)。
面試例題14——多重繼承二義性的消除(虛擬繼承)。
面試例題15——分析代碼寫結果(多重繼承和虛擬繼承)。
面試例題16——為什么要引入抽象基類和純虛函數:為了方便使用多態特性;在很多情況下,基類本身生成對象時不合情理的。純虛函數在基類中沒有定義,必須在子類中加以實現。如果基類含有一個或多個純虛函數,那么它就屬于抽象基類,不能被實例化。
面試例題17——虛函數與純虛函數的區別:(1)類里聲明虛函數的作用是為了能讓這個函數在它的子類里面被覆蓋,這樣編譯器就可以使用后期綁定來達到多態了。純虛函數只是一個接口,是個函數的聲明而已,它要留到子類中去實現。(2)虛函數在子類里面也可以不重載,但純虛函數必須在子類中去實現。(3)虛函數的類用于“實作繼承”,也就是說繼承接口的同時也繼承了父類的實現。純虛函數的類用于“界面繼承”,即純虛函數關注的是接口的統一性,實現由子類完成。
面試例題18——程序找錯(抽象類不能實例化)。
面試例題19——應用題(用面向對象的方法進行設計)。
面試例題20——什么是COM(組件對象模型Component Object Model):通過定義二進制標準解決一下問題,即DLLsf是針對C接口寫的,它們只能被C或理解C調用規范的語言使用,由編程語言實現共享代碼,而不是由動態鏈接庫本身實現。簡單地說,它定義了一種二進制標準,使得任何編程語言都能存取它所編寫的模塊。
面試例題21——COM組件是一種商業標準,其商業品牌被稱為ActiveX。組件的特點有:組件與開發工具語言無關;通過接口有效保證了組件的復用性;組件運行效率高,便于使用和管理。
面試例題22——如何理解COM對象和接口:一個對象實現一個接口,一個接口定義了接口的成員函數、調用方法、返回類型、它們的參數類型和參數數量,以及這些函數要干什么。接口的實現就是程序員在一個接口定義上提供的執行相關動作的代碼。但是,在COM模型中,對象本身對于客戶來說是不可見的,客戶請求服務時,只能通過接口實現,每個接口都由一個128位的全局標識符(GUID,Globally unique Identifier)來標識。與接口類似,每個對象也用一個GUID來標識,稱為CLSID(class ID)。繼承在COM里并不意味著代碼的重用。如果一個接口由另一個接口繼承的話,它就包含了另一個接口定義的所有方法。管理實現COM對象的IUnknown::QueryInterface方法的3個主要規則:對象必須要有一個標識符;對象實例的接口集合必須是靜態的;在對象中從任何其他的接口查詢此接口都應該成功。
面試例題23——ActiveX是微軟提出的一套基于COM的構件技術標準,實際上是對象嵌入與鏈接(OLE)的新版本。DCOM(Distribute COM)是基于分布式環境下的COM,它實現了COM對象與遠程計算機上的另一個對象之間的相互交互。
面試例題24——DLL HELL:是指DLL(動態鏈接庫)版本沖突的問題。
補充:虛函數表——主要是一個類的虛函數的地址,這張表解決了繼承、覆蓋的問題,其內容真實反映了實際函數。對于一個單繼承的類,如果它有虛擬函數,則只有一張虛函數表。對于多重繼承的類,它可能有多張虛函數表。
八、 數據結構
面試例題1——編程實現單鏈表的建立(head頭節點,可作為指針使用)。
面試例題2——編程實現單鏈表的測長(head指針,如head->next表示先前走一步)。
面試例題3——編程實現單鏈表的打印(head指針,同上)。
面試例題4——編程實現單鏈表的節點查找(head->next)。
面試例題5——編程實現單鏈表的節點插入(鏈表首部、中間和尾端三種情況)。
面試例題6——編程實現單鏈表的節點刪除。
面試例題7——編程實現單鏈表的逆轉。
面試例題8——尋找單鏈表的中間元素(一遍掃描)。
面試例題9——單鏈表的正向排序。
面試例題10——判斷單鏈表是否存在環形鏈表問題:假設兩個指針為p1和p2,每循環一次p1->next,p2->next->next,知道p2=NULL或者p2->next=NULL或者p1==p2時循環結束。如果相等則說明存在環。
面試例題11——有序單鏈表的合并(非遞歸方法:把較短的插入到較長的即可;遞歸方法:比較兩個鏈表的第一個元素,把結果鏈表的頭節點指向元素小的那個鏈表的第一個節點,再對剩下的遞歸調用此過程。)。
面試例題12——約瑟夫問題:編號為1~N的N個人按順時候圍坐一圈,沒人持有一個正整數,開始任選一個作為報數的上限值M,從第一個人按順時針方向自1開始順序報數,報到M時停止報數。報M的人出列,將他的正整數作為新的M值,從下一個人開始重新報數,如此下去,直至所有的人都出列為止,試編程實現。(循環鏈表)
面試例題13——建立以個雙向鏈表(節點數據、前驅指針、后繼指針)。
面試例題14——雙向鏈表的測長(head->right)。
面試例題15——雙向鏈表的打印(head->right,head->data)。
面試例題16——雙向鏈表的節點查找(right指針遍歷,直至找到數據為data的節點)。
面試例題17——雙向鏈表的節點插入(插入位置在中間時,需要把節點的原后繼節點的前驅指針指向新插入的節點,在鏈表末尾插入則不需要。)。
面試例題18——雙向鏈表的節點刪除(刪除頭結點、中間節點和末節點)。
面試例題19——實現有序雙向循環鏈表的插入操作。
面試例題20——刪除兩個雙向循環鏈表的相同節點。
面試例題21——編程實現隊列的入隊、出隊、測長、打印。隊列是可在對頭front刪除而在隊尾rear插入的數據結構,先進先出(FIFO)。
面試例題22——棧在棧頂top插入、刪除,另一端稱為棧底bottom。后進先出(LIFO)。隊列和棧的區別:操作的名稱、可操作的方向、操作的方法都不相同。
面試例題23——隊列和棧的使用。
面試例題24——同22。
面試例題25——使用隊列實現棧。
面試例題26——棧的使用,同22。
面試例題27——用C++實現一個二叉排序樹,完成創建節點、插入節點、刪除節點、查找節點等功能。非遞歸方法插入節點:創建節點;查找新建節點的插入位置,把新節點插入到目標節點的正確位置。如果當前節點的數據值小于要插入的data,則應該在的左子樹插入;反之則應該在它的右子樹插入。
面試例題28——使用遞歸與非遞歸方法實現中序遍歷:左根右。非遞歸:先將根節點入棧,遍歷左子樹;遍歷完左子樹返回時,根節點出棧,并打印節點數據;再中序遍歷右子樹。
面試例題29——使用遞歸與非遞歸方法實現先序遍歷:根左右。非遞歸:打印根節點數據;把根節點的right(右子節點)入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應為right,出棧,遍歷以該指針為根的子樹。
面試例題30——使用遞歸與非遞歸方法實現后序遍歷:左右根。后序遍歷要求在遍歷完左右子樹后,再訪問根節點,需要判斷根節點的左右子樹是否均遍歷過。
面試例題31——編寫層次遍歷二叉樹的算法:一層一層地進行遍歷,使用隊列。
面試例題32——編寫判斷給定二叉樹是否為二叉排序樹的程序:中序遍歷。
九、 排序
1、插入排序:每次將一個待排序的記錄,按其關鍵字大小插入到前面已經排好序的子數組中的適當位置,直到全部記錄插入完成為止。分為直接插入排序(穩定)和希爾(Shell)排序(不穩定)。
面試例題1——編程實現直接插入排序:把數組分為兩個區,即前面的有序區和后面的無序區,再依次把無序區的元素插入到有序區即可。編程思想:在當前有序區R[1,…,i-1]中查找R[i]的正確位置k(1<=k<=i-1);將R[k,…,i-1]中的記錄均后移一位,騰出k位置上的空間插入R[i]。
面試例題2——編程實現直接Shell排序:先將要排序的一組數按某個增量d分成若干組,對每組中全部元素進行排序,然后用一個較小的增量對它進行再次分組,并對新組進行排序。當增量減到1時,整個要排序的數被分為一組,排序完成。分組插入方法。
2、交換排序:兩兩比較待排序記錄的關鍵字,發現兩個記錄的次序相反時即進行交換,直到沒有反序為止。其主要排序方法有冒泡排序和快速排序。
面試例題3——編程實現直接冒泡排序:第一次掃描最小的記錄被放在最前位置;第二次是次小的記錄;依次類推,直到排序完成。
面試例題4——編程實現快速排序:通常稱為分治法(Divide-and-Conquer Method),將原問題分解為若干個規模更小但結構與原問題相似的子問題,遞歸地解答這些子問題,然后將這些子問題的解組合為原問題的解。基本思想——分解、求解、組合。
3、選擇排序:每一次從待排序的記錄中選出關鍵字最小的記錄,順序放在已排好序的子文件的最后,直到全部記錄排序完畢。常用的選擇排序方法有直接選擇排序和堆排序。
面試例題5——直接選擇排序:第一次排序把最小的元素與第一個元素交換;第二次次小與第二個交換;以此類推,直到排序完成。
面試例題6——編程實現堆排序:有兩種堆,即小根堆(所有子節點都大于其父節點)和大根堆(所有子節點都小于父節點)。堆實質上是滿足如下性質的完全二叉樹:樹中任意非葉節點的關鍵字均不大于(或不小于)其左右子節點(若存在)的關鍵字。
4、歸并排序:利用“歸并”技術來進行排序。歸并是指將若干個已排序的子文件合并成一個有序的文件。兩種實現方法:自底向上、自頂向下(分解、求解、組合)。
面試例題7——歸并排序算法的實現(使用自頂向下的方法)。
5、基數排序:是箱排序的改進和推廣。箱排序也稱桶排序(Bucket Sort),其基本思想是:設置若干個箱子,依次掃描待排序的記錄R[0],R[1],…,R[n-1],把關鍵字等于k的記錄全都裝入到第k個箱子里(分配),然后按序號依次將各非空的箱子首尾連接起來(收集)。有兩種排序方法:最高位優先MSD(Most Significant Digit first)和最低位優先(Least Significant Digit first)。最典型的是LSD排序方法,其基本思想是:從低位到高位依次對數據進行箱排序。
面試例題8——使用基數排序對整數進行排序。
6、各種排序方法比較。
按平均時間分類:
時間復雜度
算法
O(n^2)
簡單排序:直接插入、直接選擇和冒泡排序
O(nlogn)
快速、堆和歸并排序
O(n1+&)
&是介于0和1之間的常數,例如希爾排序
O(n)
桶、箱和基數排序
O(nlog2n)
歸并排序、快速排序的最理想情況
比較結果:簡單排序中直接插入最佳,快速排序最快,當文件為正序時,直接插入和冒泡最好。
選擇排序方法的影響因素:待排序的記錄數目n;記錄的大小(規模);關鍵字的結構及其初始狀態;對穩定性的要求;語言工具的條件;存儲結構;時間和空間復雜度。
面試例題9——各種排序算法速度的性能比較。
面試例題10——各排序算法的時間復雜度比較。