劍指offer題目記錄


1.如下為類型CMyString的聲明,請為該類型添加賦值運算符函數。

1 class CMyString 
2 {
3 public:
4     CMyString(char* pData = NULL);
5     CMyString(const CMyString& str);
6     ~CMyString(void);
7 private:
8     char* m_pData;
9 };
View Code

 

2.設計一個類,我們只能生成該類的一個實例。

 

3.在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否函數該整數。

 

4.請實現一個函數,把字符串中的每個空格替換成"%20"。例如輸入"We are happy",則輸出"We%20are%20happy"。

 

4_1.有兩個排序的數組A1和A2,內存在A1的末尾有足夠多的空余空間容納A2。請實現一個函數,把A2中的所有數字插入到A1中並且所有的數字是排序的。

 

5.輸入一個鏈表的頭結點,從尾到頭反過來打印出每個節點的值。

 

6.輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果都不含重復的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建出圖2.6所示的二叉樹並輸出它的頭結點。

 

7.用兩個棧實現一個隊列。隊列的聲明如下,請實現它的兩個函數appendTail和deleteHead,分別完成在隊列尾部插入節點和隊列頭部刪除節點的功能。

 

7_1.用兩個隊列實現一個棧。

 

8.把一個數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如數組{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該數組的最小值為1。

 

9.寫一個函數,輸入n,求斐波那契數列的第n項。

 

9_1.一只青蛙一次可以跳上1級台階,也可以跳上2級。求該青蛙跳上一個n級台階總共有多少種跳法。

 

9_2.青蛙跳台階問題中,如果把條件改成:一只青蛙一次可以跳上1級台階,也可以跳上2級...它也可以跳上n級,此時青蛙跳上一個n級台階總共有多少種跳法?

 

9_3.我們可以用2*1的小矩形橫着活着豎着去覆蓋更大的矩形。請問8個2*1的小矩形無重疊地覆蓋一個2*8的大巨星,總共有多少種方法?

 

10.請實現一個函數,輸入一個整數,輸出該二進制表示中1的個數。例如把9表示成二進制是1001,有2位是1。因此如果輸入9,該函數輸出2。

 

10_1.用一條語句判斷一個整數是不是2的整數次方。一個整數如果是2的整數次方,那么它的二進制表示中有且只有一位是1,而其他所有位都是0。根據前面的分析,把這個整數減去1之后再和它自己做與運算,這個整數中唯一的1就會變成0。

 

10_2.輸入兩個整數m和n,計算需要改變m的二進制表示中的多少位才能得到n。比如10的二進制表示為1010,13的二進制表示為1101,需要改變1010中的3位才能得到1101 。 我們可以分為兩步解決這個問題:第一步求這兩個數的異或,第二步統計異或結果中1的位數。

 

10_3.把一個整數減去1之后再和原來的整數做位與運算,得到的結果相當於是把整數的二進制表示中的最右邊一個1變成0 。 很多二進制的問題都可以用這個思路解決。

 

sum up

基礎知識,准備這方面的知識需要從編程語言、數據結構和算法3方面做准備。通常采用概念題、代碼分析和編程題3種常見題型考察對某一編程語言的掌握程度。

數據結構一直是面試考察的重點,數組和字符串是兩種最基本的數據結構。鏈表應該是面試題中使用頻率最高的一種數據結構。如果面試官想加大面試難度,他很有可能會選用樹(尤其是二叉樹)相關的面試題。由於棧與遞歸調用密切相關,隊列在圖(包括樹)的寬度優先遍歷中需要用到,因此應聘者也需要掌握這兩種數據結構。

算法也是面試考察的重點,查找(特別是二分查找)和排序(特別是快速排序和歸並排序)是面試中最經常考察的算法,一定要熟悉掌握。另外還需掌握分析時間復雜度的方法,理解即使是同一思路,基於循環和遞歸的不同實現他們的時間復雜度可能大不相同。

位運算是針對二進制數字的運算規律,熟悉掌握二進制的與、或、異或運算以及左移、右移操作,就能解決與位運算相關的問題。


 

 

11.實現函數double Power(double base,int exponent),求base的exponent次方。不得使用庫函數,同時不需要考慮大數問題。

(提示由於計算機表示小數float和double型都有誤差,所以不能直接利用等號(==)判斷兩個小數是否相等。如果兩個小數的絕對值很小,例如小於0.0000001,我們可以認為其相等)

 

12.輸入數字n,按順序打印出從1最大的n位十進制數。比如輸入3,則打印出1、2、3一直到最大的3位數即999 。

 

12_1.前面的代碼中,我們都是用一個char型字符表示十進制數字的一位。8個bit的char型字符最多能表示256個字符,而十進制數字只有0-9的10個數字。因此用char型字符串來表示十進制的數字並沒有充分利用內存,有一些浪費。有沒有更高效的方式來表示大數。

 

12_2.定義一個函數,在該函數中可以實現任意兩個整數的加法。由於沒有限定輸入兩個數的大小范圍,我們也要把它當做大數問題來處理。在前面的代碼的第一個思路中,實現了在字符串表示的數字上加1的功能,我們可以參考這個思路實現兩個數字相加功能,另外還有一個需要注意的問題:如果輸入的數字中有負數,我們應該怎么處理?(如果面試題關於n位的整數並且沒有限定n的取值范圍,或者是輸入任意大小的整數,那么這個題目很有可能是需要考慮大數問題的,字符串是一個簡單,有效的表示大數的方法)

 

13.給定單向鏈表的頭指針和一個結點指針,定義一個函數在O(1)時間刪除該節點。鏈表的節點與函數的定義如下:

1 struct ListNode
2 {
3     int m_nValue;
4     ListNode* m_pNext;
5 };
6 void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted);
View Code

(提示:刪除單向鏈表結點的兩種方法

(a)刪除結點i之前,先從鏈表的頭結點開始遍歷到i前面的一個節點h,把h的m_pNext指向i的下一個節點j,再刪除結點i , 這也是最經常用的一個方法,自己熟悉。

(b)把節點j的內容復制覆蓋節點i,接下來再把節點i的m_pNext指向j的下一個節點之后刪除結點j。這種方法不用遍歷鏈表上結點i前面的節點,只需要有指向i結點的指針即可完成操作。

 

14.輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有奇數位於數組的前半部分,所有偶數位於數組的后半部分。

(考慮可擴展性的解決方案,將判斷奇偶的部分利用函數指針抽象出來達到解耦的效果)

 

15.輸入一個鏈表,輸出該鏈表中倒數第K個結點。為了符合大多數人的習慣,本題從1開始計數,即鏈表的尾結點是倒數第1個結點。例如一個鏈表有6個結點,從頭結點開始它們的值依次是1,2,3,4,5,6。這個鏈表的倒數第3個結點是值為4的結點。(注意代碼魯棒性,考慮輸入空指針,鏈表結點總數少於k,輸入的k參數為0)

 

15_1.求鏈表的中間結點。如果鏈表中結點總數為奇數,返回中間結點;如果結點總數為偶數,返回中間兩個結點的任意一個。(通過一次遍歷解決這個問題)

 

15_2.判斷一個單項鏈表是否形成了環形結構。(提示:速度不同的鏈表指針遍歷,類似於操場跑步,當我們用一個指針遍歷鏈表不能解決問題的時候,可以嘗試利用兩個指針來遍歷鏈表,可以讓其中一個指針遍歷的速度快一些,比如一次在鏈表上走兩步,或者讓它先在鏈表上走若干步

 

16.定義一個函數,輸入一個鏈表的頭結點,反轉該鏈表並輸出反轉后鏈表的頭結點。

 

16_1.遞歸實現同樣的反轉鏈表的功能。

 

17.輸入兩個遞增排序的鏈表,合並這兩個鏈表並使新鏈表中結點仍然是按照遞增排序的。例如輸入1->3->5->7和2->4->6->8,則合並之后的升序鏈表應該是1->2->3->4->5->6->7->8 。

 

18.輸入兩顆二叉樹A和B,判斷B是不是A的子結構。二叉樹結點的定義如下:

 

1 struct BinaryTreeNode
2 {
3     int m_nValue;
4     BinaryTreeNode* m_pLeft;
5     BinaryTreeNode* m_pRight;
6 };
View Code

 

 

sum up

 

從規范性、完整性和魯棒性3個方面介紹了如何在面試時寫出高質量的代碼。

規范性:書寫清晰,布局清晰,命名合理

完整性:完成基本功能,考慮邊界條件,做好錯誤處理

魯棒性:采取防御式編程,處理無效的輸入


 

19.請完成一個函數,輸入一個二叉樹,該函數輸出它的鏡像。(遞歸和循環應該分別怎樣實現?)

 

20.輸入一個矩陣,按照從外向里以順時針的順序依次打印出每一個數字。例如:如果輸入如下矩陣:

1    2    3    4

5    6    7    8

9    10   11   12

13   14   15   16

則依次打印出數字1、2、3、4、8、12、16、15、14、13、9、5、6、7、11、10 。

 

21.定義棧的數據結構,請在該類型中實現一個能夠得到棧的最小元素的min函數。在該棧中,調用min、push以及pop的時間復雜度都是O(1)。

 

22.輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1、2、3、4、5是某棧的壓棧序列,序列4、5、3、2、1是該壓棧序列對應的一個彈出序列,但4、3、5、1、2就不可能是該壓棧序列的彈出序列。

 

23.從上往下打印出二叉樹的每個結點,同一層的結點按照從左到右的順序打印。

 

23_1.如何廣度優先遍歷一個有向圖?這同樣也可以基於隊列實現。樹是圖的一種特殊退化形式,從上到下按層遍歷二叉樹,從本質上來講就是廣度優先遍歷二叉樹。

 

23_2.不管是廣度優先遍歷一個有向圖還是一棵樹,都要用到隊列。第一步我們把起始結點(對樹而言是跟結點)放入隊列中,接下來每一次從隊列的頭部取出一個結點,遍歷這個結點之后把從它能達到的結點(對樹而言是子結點)都一次放入隊列。我們重復這個遍歷過程,直到隊列中的結點全部被遍歷為止。

 

24.輸入一個整數數組,判斷該數組是不是某二叉搜索樹的后序遍歷的結果。如果是則返回true,否則返回false。假設輸入的數組的任意兩個數字都互不相同。

 

24_1.輸入一個整數數組,判斷該數組是不是某二叉搜索樹的前序遍歷的結果。這和前面的問題的后序遍歷很類似,只是前序遍歷的序列中,第一個數字是根結點的值。

 

24_1.如果面試題是要求處理一顆二叉樹的遍歷序列,我們可以先找到二叉樹的根結點,再基於根結點把整棵樹的遍歷序列拆分成左子樹對應的子序列和右子樹對應的子序列,接下來再遞歸地處理這兩個子序列。

 

25.輸入一顆二叉樹和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。

 

26.請實現函數ComplexListNode* Clone(ComplexListNode* pHead),復制一個復雜鏈表。在復雜鏈表中,每個結點除了有一個m_pNext指針指向下一個結點外,還有一個m_pSibling指向鏈表中的任意結點或者NULL。結點C++的定義如下:

 

1 struct ComplexListNode
2 {
3     int m_nValue;
4     ComplexListNode* m_pNext;
5     COmplexListNode* m_pSibling;
6 };
View Code

 

 

27.輸入一顆二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建人和新的結點,只能調整樹中結點指針的指向。

 

28.輸入一個字符串,打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符串a、b、c所能排列出來的所有字符串abc、acb、bac、bca、cab和cba。

 

28_1.如果不是求字符的所有排列,而是求字符的所有組合,應該怎么辦?還是輸入三個字符a、b、c,則它們的組合有a、b、c、ab、ac、bc、abc。當交換字符串中兩個字符時,雖然能得到兩個不同的排列,但卻是同一個組合。比如ab和ba是不同的排列,但只算一個組合。

 

28_2.當輸入一個含有8個數字的數組,判斷有沒有可能把這8個數字分別放到正方體的8個頂點上,使得正方體上三組相對的面上的4個頂點的和都相等。

 

28_3.在8*8的國際象棋上擺放8個皇后,使其不能相互攻擊,及任意兩個皇后不得處於同一行,同一列或者同意對角線上,請問總共有多少種符合條件的擺法。

 

28_4.如果面試題是按照一定要求擺放若干數字,我們可以先求出這些數字的所有排列,然后再一一判斷每個排列是不是滿足題目給定的要求。

 

sum up

面試的時候難免會遇到難題,畫圖、舉例子和分解者三種辦法能夠幫助解決復雜的問題。

圖形能夠使抽象的問題形象化,當涉及鏈表、二叉樹等數據結構時,如果在紙上畫幾張草圖,題目中隱藏的規律就有可能變得很直觀。

一兩個例子能使得抽象的問題具體化。

復雜問題分解成若干個小問題,是解決很多復雜問題的有效方法。如果我們遇到的問題很大,嘗試先把大問題分解成小問題,然后遞歸的解決這些小問題。分治法、動態規划等方法都是基於這種思路。


 

 

29.數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度為9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。

 

30.輸入n個整數,找出其中最小的k個數。例如輸入4、5、1、6、2、7、3、8這8個數字,則最小的4個數字是1、2、3、4 。

 

31.輸入一個整型數組,數組里有正數,也有負數。數組中一個或連續的多個整數組成一個子數組。求所有子數組的和的最大值。要求時間復雜度為O(n)。

 

32.輸入一個整數n,求從1到n這n個整數的十進制表示中1出現的次數。例如輸入12,從1到12這些整數中包含1的數字有1,10,11和12,1一共出現5次。

 

33.輸入一個正整數數組,把數組里所有數字拼接起來排成一個數,打印能拼接出所有數字中最小的一個。例如輸入數組{3,32,321},則打印出這3個數字能排成的最小數字321323。

 

34.我們把只包含因子2,3和5的數稱作丑數。求按從小到大的順序的第1500個丑數。例如6、8都是丑數,但14不是,因為它包含因子7.習慣上我們把1當做第一個丑數。

 

35.在字符串中找出第一個只出現一次的字符。如輸入"abaccdeff",則輸出'b'。

 

35_1.在前面的例子中,我們之所以可以把哈希表的大小設為256,是因為字符(char)是8個bit的類型,總共只有256個字符。但實際上字符不只是256個,比如中文就有幾千個漢字。如果題目要求考慮漢字,前面的算法是不是有問題?如果有,可以怎么解決。

 

35_2.定義一個函數,輸入兩個字符串,從第一個字符串中刪除在第二個字符串中出現過的所有字符。例如第一個字符串"we are students",第二個字符串是"aeiou",結果應該是"w r stdnts"。

 

35_3.定義一個函數,刪除字符串中所有重復出現的字符。例如輸入"google",則輸出結果應該是"gole"。

 

35_4.請完成一個函數,判斷輸入的兩個字符串是否是Anagram。

 

35_5.如果需要判斷多個字符是不是在某個字符串里出現過或者統計多個字符在某個字符串中出現的次數,我們可以考慮基於數組創建一個簡單的哈希表。這樣可以用很小的空間消耗換來時間效率的提升。

 

36.在數組中的兩個數字如果前面一個數字大於后面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。

 

37.輸入兩個單向鏈表,找出它們的第一個公共結點。

 

sum up

降低時間復雜度的第一個方法是改用更加高效的算法。

降低時間復雜度的第二個方法是用空間換取時間,另外我們可以創建一個緩存保存中間的計算結果,從而避免重復計算。遞歸中會出現很多重復計算,所以這種保存已經計算的結果成為“記賬法”。

空間和時間應該進行權衡,嵌入式系統的內存有限,所以不一定非要花費空閑去換取時間。

 


 

 

38.統計一個數字在排序數組中出現的次數。例如輸入排序數組{1,2,3,3,3,3,4,5}和數字3,由於3在這個數組中出現了4次,因此輸出4。

 

39.輸入一顆二叉樹的根節點,求該樹的深度。從根節點到葉節點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度為樹的深度。

 

39_1.輸入一顆二叉樹的根結點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意結點的左右子樹的深度相差不超過1,那么它就是一顆平衡二叉樹。

 

40.一個整型數組里除了兩個數字之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。要求時間復雜度是O(n),空間復雜度O(1)。

 

41.輸入一個遞增排序的數組和一個數字s,在數組中查找兩個數,使得它們的和正好是s。如果有多對數字的和等於s,輸出任意一對即可。

 

41_1.輸入一個正數s,打印出所有和為s的連續正數序列(至少含兩個數)。例如輸入15,由於1+2+3+4+5=4+5+6=7+8=15,所以結果打印出3個連續序列1~5,4~6和7~8。

 

42.輸入一個英文句子,翻轉句子中單詞的順序,但單詞內字符的順序不變。為簡單起見,標點符號和普通字母一樣處理。例如輸入字符串"I am student",則輸出"student. a am I"。

 

42_1.字符串的左旋轉操作是把字符串前面若干個字符轉移到字符串的尾部。請定義一個函數實現字符串左旋轉操作的功能。比如輸入字符串"abcdefg"和數字2,函數將返回"cdefgab"。

 

43.把n個骰子仍在地上,所有骰子朝上一面的點數之和為s,輸入n,打印出s的所有可能的值出現的概率。

 

44.從撲克牌中隨機抽5張牌,判斷是不是一個順子,即這5張牌是不是連續的。2~10為數字本身,A為1,J為11,Q為12,K為13,而大、小王可以看成任意數字。

 

45.0~n-1這n個數字排列成一個圓圈,從數字0開始每次從這個圓圈中刪除第m個數字。求出這個圓圈里剩下的最后一個數字。

 

46.求1+2+...+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句。

 

47.寫一個函數,求兩個整數之和,要求函數體內部的使用+、-、*、\四則與水暖符號。

 

48.用C++設計一個不能被繼承的類。

 

sum up

溝通能力、學習能力。善於提問的人有較好的溝通和學習能力。

知識遷移能力能幫助我們輕松解決很多問題,舉一反三的能力,平時要有一定的積累,每完成一道題目之后都要總結解題方法。

抽象建模能力,選取適當的數據結構表述模型,分析模型中的內在規律確定計算方法。

發散思維能力,跳出常規思路的束縛,從不同的角度去嘗試新的方法。

 

 


 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM