360面試

面試準備:

1 常用排序算法及複雜度

這裏寫圖片描述

2 快速排序的優點

時間複雜度爲o(nlogn),空間複雜度反映在遞歸造成的棧空間的使用,空間複雜度爲o(logn) - o(n)
優點: 時間複雜度最優(冒泡排序相比);時間複雜度相同情況下,空間複雜度最優(與歸併排序相比)
缺點: 算法不穩定,其不穩定性發生在對應元素與基準元素髮生交換時

3 STL中vector和list的底層實現

vector是動態數組,list是雙向鏈表,deque是雙端隊列
vector分配連續內存,在隨機訪問時最方便,但是在中間插入或刪除時麻煩
list內存空間不連續,在插入刪除時最方便,但是隨機訪問很麻煩
deque內存空間不連續,更像是vector和list的結合,在隨機訪問和插入刪除都比較方便

map是有序無重複的關聯容器,set是集合
map在底層通常用二叉搜索樹來實現

4 棧和隊列的區別

  • 棧的插入和刪除操作都是在一端進行的,而隊列的操作卻是在兩端進行的。
  • 棧是先進後出,隊列是先進先出。
  • 棧只允許在表尾一端進行插入和刪除,隊列只允許在表尾一端進行插入,在表頭一端進行刪除。

5 c++中的多態

借用兩篇博客中的解釋:
解釋1:
 c++的多態性就是通過晚綁定技術來實現的。

 c++的多態性用一句話概括就是:在基類的函數前加上virtual關鍵字,在派生類中重寫該函數,運行時將會根據對象的實際類型來調用相應的函數,如果對象類型是派生類,就調用派生類的函數,如果對象類型是基類,就調用基類的函數。

 虛函數是在基類中定義的,目的是不確定它的派生類的具體行爲,例如:
  定義一個基類:class Animal //動物,它的函數爲breathe()
  再定義一個類class Fish //魚。它的函數也爲breathe()
  再定義一個類class Sheep //羊,它的函數也爲breathe()

將Fish,Sheep定義成Animal的派生類,然而Fish與Sheep的breathe不一樣,一個是在水中通過水來呼吸,一個是直接呼吸,所以基類不能確定該如何定義breathe,所以在基類中只定義了一個virtual breathe,它是一個空的虛函數,具體的函數在子類中分別定義,程序一般運行時,找到類,如果它有基類,再找到它的基類,最後運行的是基類中的函數,這時,它在基類中找到的是virtual標識的函數,它就會再回到子類中找同名函數,派生類也叫子類,基類也叫父類,這就是虛函數的產生,和類的多態性的體現。
這裏的多態性是指類的多態性。

  • 函數的多態性是指一個函數被定義成多個不同參數的函數。當你調用這個函數時,就會調用不同的同名函數。
  • 一般情況下(不涉及虛函數),當我們用一個指針/引用調用一個函數的時候,被調用的函數是取決於這個指針/引用的類型。
  • 當設計到多態性的時候,採用了虛函數和動態綁定,此時的調用就不會在編譯時候確定而是在運行時確定。不在單獨考慮指針/引用的類型而是看指針/引用的對象的類型來判斷函數的調用,根據對象中虛指針指向的虛表中的函數的地址來確定調用哪個函數

解釋2:
C++多態方式:

(1)靜態多態(重載,模板)
是在編譯的時候,就確定調用函數的類型。

(2)動態多態(覆蓋,虛函數實現)
在運行的時候,才確定調用的是哪個函數,動態綁定。運行基類指針指向派生類的對象,並調用派生類的函數。

  • 虛函數實現原理:虛函數表和虛函數指針。
  • 純虛函數: virtual int fun() = 0;

多態基礎介紹:

首先,什麼是多態(Polymorphisn)?按字面的意思就是”多種形狀”。我手頭的書上沒有找到一個多態的理論性的概念的描述。

暫且引用一下 Charlie Calverts的對多態的描述吧——多態性是允許你將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作(摘自”Delphi4 編程技術內幕”)。

簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。多態性在Object Pascal和C++中都是通過虛函數(Virtual Function) 實現的。

好,接着是”虛函數”(或者是”虛方法”)。虛函數就是允許被其子類重新定義的成員函數。而子類重新定義父類虛函數的做法,稱爲”覆蓋”(override),或者稱爲”重寫”。


這裏有一個初學者經常混淆的概念。覆蓋(override)和重載(overload)。

上面說了,覆蓋是指子類重新定義父類的虛函數的做法。而重載,是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。其實,重載的概念並不屬於”面向對象編程”,重載的實現是:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。如,有兩個同名函數:function func(p:integer):integer;和function func(p:string):integer;。那麼編譯器做過修飾後的函數名稱可能是這樣的:int_func、str_func。對於這兩個函數的調用,在編譯器間就已經確定了,是靜態的(記住:是靜態)。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態無關!

真正和多態相關的是 “覆蓋”。當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態(記住:是動態!)的調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。
因此,這樣的函數地址是在運行期綁定的(晚邦定)。結論就是:重載只是一種語言特性,與多態無關,與面向對象也無關!

那麼,多態的作用是什麼呢?我們知道,封裝可以隱藏實現細節,使得代碼模塊化;繼承可以擴展已存在的代碼模塊(類);它們的目的都是爲了——代碼重用。

而多態則是爲了實現另一個目的——接口重用!而且現實往往是,要有效重用代碼很難,而真正最具有價值的重用是接口重用,因爲”接口是公司最有價值的資源。設計接口比用一堆類來實現這個接口更費時間。而且接口需要耗費更昂貴的人力的時間。”


參考:
https://www.cnblogs.com/cxq0017/p/6074247.html
http://www.runoob.com/cplusplus/cpp-polymorphism.html
https://www.cnblogs.com/Allen-rg/p/6927129.html

6 構造和析構

  • 構造函數的作用:只要創建類類型的新對象,都會自動執行構造函數,構造函數保證每個對象的數據成員具有合適的初始值。
  • 構造函數的名字與類的名字相同,並且不能指定返回類型
  • 構造函數可以沒有形參,也可以有多個形參
  • 構造函數可以被重載
  • 類的析構函數是類的一種特殊的成員函數,它會在每次刪除所創建的對象時執行。
    析構函數的名稱與類的名稱是完全相同的,只是在前面加了個波浪號(~)作爲前綴,它不會返回任何值,也不能帶有任何參數。析構函數有助於在跳出程序(比如關閉文件、釋放內存等)前釋放資源。

參考:
http://www.runoob.com/cplusplus/cpp-constructor-destructor.html

面試題

1 求二值圖的最大連通域

面試官要求我輸出最大連通域中聯通像素的個數,其實還是需要把所有連通域求出來,找了幾篇博客,已經講的很清楚了:
https://blog.csdn.net/icvpr/article/details/10259577
https://blog.csdn.net/xjt2015/article/details/51283387
https://www.cnblogs.com/ryuasuka/p/4932239.html
https://www.cnblogs.com/ronny/p/img_aly_01.html
總的來說,就是兩種思路,一種是兩遍掃描:第一遍掃描記錄連通的lable對,第二次掃描把lableImage中的大lable全部改成lable對中最小的lable,在兩次掃描中間,需要計算連通的lable,這裏可以使用圖的深度優先搜索方法;第二種思路就是深度優先搜索的方法,一遍掃描,利用棧,把連通的像素全部找出來,第二種方法更加簡單粗暴一些。
我用python把第二種方法簡單實現了一下:

'''求二值圖連通域'''
def calcConnectedBySeedFill(originImage):
    rows, cols = len(originImage), len(originImage[0])
    stack = []
    lable = 0
    lableImage = np.zeros([rows, cols], dtype=np.int)
    print(originImage)
    print(lableImage)
    for i in range(0, rows):
        for j in range(0, cols):
            if originImage[i][j] == 0 or lableImage[i][j] != 0:
                continue
            else:
                stack.append([i, j])
                lable += 1
                lableImage[i][j] = lable
                while len(stack) > 0:
                    currentIndex = stack.pop()
                    x, y = currentIndex[0], currentIndex[1]
                    if y-1 >= 0 and lableImage[x][y-1] == 0 and originImage[x][y-1] == 255:
                        stack.append([x, y-1])
                        lableImage[x][y-1] = lable
                    if y+1 <= cols-1 and lableImage[x][y+1] == 0 and originImage[x][y+1] == 255:
                        stack.append([x, y+1])
                        lableImage[x][y+1] = lable
                    if x-1 >= 0 and lableImage[x-1][y] == 0 and originImage[x-1][y] == 255:
                        stack.append([x-1, y])
                        lableImage[x-1][y] = lable
                    if x+1 <= rows-1 and lableImage[x+1][y] == 0 and originImage[x+1][y] == 255:
                        stack.append([x+1, y])
                        lableImage[x+1][y] = lable
    print(lableImage)
    return lableImage
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章