(下午要去面試有道...還是做一點功課比較好...)
簡答
1. 多態實現機制
多態性可以簡單的概括爲“1個接口,多種方法”,在程序運行的過程中才決定調用的機制,通過父類指針調用子類的函數,可以讓父類指針有多種形態。
編譯器爲每個類的對象提供一個虛表指針,這個指針指向對象所屬類的虛表(存放了虛函數的地址)。在程序運行時,根據對象的類型去初始化vptr,從而讓vptr正確的指向所屬類的虛表,從而在調用虛函數時,就能夠找到正確的函數。
在構造函數中進行虛表的創建和虛表指針的初始化。在構造子類對象時,要先調用父類的構造函數,此時編譯器只“看到了”父類,並不知道後面是否後還有繼承者,它初始化父類對象的虛表指針,該虛表指針指向父類的虛表。當執行子類的構造函數時,子類對象的虛表指針被初始化,指向自身的虛表。
2. 虛函數,純虛函數
爲了方便使用多態特性,我們常常需要在基類中定義虛擬函數。定義他爲虛函數是爲了允許用基類的指針來調用子類的這個函數。它虛就虛在所謂“推遲聯編”或者“動態聯編”上,一個類函數的調用並不是在編譯時刻被確定的,而是在運行時刻被確定的。由於編寫代碼的時候並不能確定被調用的是基類的函數還是哪個派生類的函數,所以被成爲“虛”函數。
純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。同時含有純虛擬函數的類稱爲抽象類,它不能生成對象。
3. 引用和指針的區別
(1) 引用必須被初始化,指針不必。
(2) 引用初始化以後不能被改變,指針可以改變所指的對象。
(3) 不存在指向空值的引用,但是存在指向空值的指針。
4. 重載和重寫的區別
重載:是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。
重寫:是指子類重新定義父類虛函數的方法。(和多態相關)
5. 拷貝構造函數(深拷貝和淺拷貝)
如果一個構造函數的第一個參數是自身類類型的引用,且任何額外參數都有默認值,則此構造函數時拷貝構造函數。
淺拷貝是創建了一個對象用一個現成的對象初始化它的時候只是複製了成員(簡單賦值)而沒有拷貝分配給成員的資源(如給其指針變量成員分配了動態內存); 深拷貝是當一個對象創建時,如果分配了資源,就需要定義自己的拷貝構造函數,使之不但拷貝成員也拷貝分配給它的資源。
6. 內存分配方式及他們的區別
(1) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static 變量。
(2) 在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集。
(3) 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc 或new 申請任意多少的內存,程序員自己負責在何時用free 或delete 釋放內存。動態內存的生存期由程序員決定,使用非常靈活,但問題也最多。
7. C++中類型轉換機制
(http://blog.csdn.net/kesalin/article/details/8119586)
(1) 隱式類型轉換
將某種類型的對象拷貝到另一種不同類型的對象中時就會發生隱式轉型,不推薦。
(2) 顯示類型轉換
cast_name<type>(expression),cast_name可以是以下四種:
Ø static_cast
任何具有明確定義的類型轉化,只要不包含底層的const,都可以使用static_cast。可以將非常量轉化爲常量,反之不可以。不進行安全檢查。
Ø const_cast
可以將常量轉換爲非常量。只能改變常量屬性,不能改變表達式的類型。
Ø dynamic_cast
主要用來在繼承體系中的安全向下轉型,它能安全地將指向基類的指針轉型爲指向子類的指針或引用,並獲知轉型動作成功是否。具有類型檢查功能,比static_cast更安全,但是存在一定的效率損失。
Ø reinterpret_cast
只用於底層代碼,一般我們都用不到它~依賴於機器,爲運算對象的位模式提供較低層次上的重新解釋。
8. new/delete和malloc/free的區別
new/delete是C++運算符,malloc/free是C語言的標準庫函數。對於非內部數據類型的對象而言,光用maloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數(除分配/釋放內存外,還可能有其他更爲詳細的工作)。由於malloc/free是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free。因此C++語言需要一個能完成動態內存分配和初始化工作的運算符new,以及一個能完成清理與釋放內存工作的運算符delete。注意new/delete不是庫函數。
9. 進程和線程的區別
編程
1. 字符串裏對稱子串的最大長度
(騰訊實習生筆試題…當時居然空了……)
輸入一個字符串,輸出該字符串中對稱的子字符串的最大長度。比如輸入字符串“goooogle”, 由於該字符串裏最長的對稱子字符串是“goooog”, 因此輸出 6。代碼如下:
#include<iostream>
#include<string>
usingnamespace std;
intsymLength(string str);
boolisSym(string str);
int main(){
string test;
int maxSymLength;
while(cin>>test){
maxSymLength =symLength(test);
cout << maxSymLength<< endl;
}
return 0;
}
intsymLength(string str)//字符串中最長的對稱子字符串
{
int maxSymLength = 0;
if(str.size()==1) return 0;
for(int i=0; i<str.size()-1; i++)
for(int j=str.size()-1;j>i; j--){
string temp =str.substr(i,j-i+1);
if(isSym(temp))maxSymLength = max(j-i+1,maxSymLength);
}
return maxSymLength;
}
boolisSym(string str)//判斷一個字符串是否對稱
{
for(int i=0;i<str.size()/2;i++){
if(str[i]==str[str.size()-1-i])continue;
else return false;
}
return true;
}
自己寫的這種方法時間複雜度比較高,參考了網上的代碼,可以從o(n^3)降到o(n^2)。
主要思路是如果我們從內向外比較字符,那麼對於aba型的字符串,如果我們判斷了b是對稱的,只需要再左右各移一位就可以判斷下一個字符串是否是對稱的,這樣就能避免重複;原字符串中每一個字符有兩種情況,一種是子串是以單個字符爲中心對稱分佈的,即子串的長度是奇數;另一種情況是子串以兩個字符串爲中心,即子串的長度是偶數。參考代碼(http://www.cnblogs.com/python27/archive/2011/12/18/2291977.html):
#include<iostream>
#include<string>
usingnamespace std;
/*********************************************************************
* 計算字符串最大對稱子串的長度
*********************************************************************/
intMaxSymmetricalSubstringLenth(char* pstring)
{
int maxlength = 1;
char* pchar = pstring + 1;
while(*pchar != '\0')
{
//以該字符串爲中心,子串長度爲奇數
char *pfirst = pchar - 1;
char *psecond = pchar + 1;
while(pfirst > pstring &&psecond < &pstring[strlen(pstring) - 1] && *pfirst == *psecond)
{
pfirst--;
psecond++;
}
int templength = psecond - pfirst + 1;
if(templength > maxlength)
{
maxlength = templength;
}
//字該字符及之後的字符兩個爲中心,子串爲偶數
pfirst = pchar - 1;
psecond = pchar;
while(pfirst > pstring &&psecond < &pstring[strlen(pstring) - 1] && *pfirst == *psecond)
{
pfirst--;
psecond++;
}
templength = psecond - pfirst + 1;
if(templength > maxlength)
{
maxlength = templength;
}
pchar++;
}
return maxlength;
}
int main()
{
cout<<"Please Enter YourString:"<<endl;
char *yourstring = new char[1000];
cin>>yourstring;
cout<<"The Max SymmetricalSubString Length is:"<<endl;
cout<<MaxSymmetricalSubstringLenth(yourstring)<<endl;
delete[] yourstring;
return 0;
}
2. 打印回形矩陣&蛇形矩陣
(同騰訊實習生筆試題…)
輸入一個正整數N,輸出N*N的回形/蛇形矩陣。eg.N=4, 如下所示:
回形矩陣:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
蛇形矩陣:
1 2 6 7
3 5 8 13
4 9 12 14
10 11 15 16
代碼如下:
#include<iostream>
using namespace std;
void roundMatrix(int matrix[],int N);
void snakeMatrix(int matrix[],int N);
void printMatrix(int m[], int M);
int main(){
int N;
cin >> N;
int *round = new int[N*N];
int *snake = new int[N*N];
roundMatrix(round,N);
snakeMatrix(snake,N);
printMatrix(round,N*N);
printMatrix(snake,N*N);
delete[] round;
delete[] snake;
return 0;
}
void roundMatrix(int matrix[],int N)
{
int round = 0;
int num = 1;
while(1)
{
for(int col=round;col<N-round&&num<=N*N;col++) //從左到右
matrix[round*N+col] = num++;
if(num>N*N) break;
for(int row=round+1;row<N-round&&num<=N*N;row++) //從上到下
matrix[row*N+N-1-round] = num++;
if(num>N*N) break;
for(int col=N-2-round;col>=round&&num<=N*N;col--) //從右到左
matrix[(N-1-round)*N+col] = num++;
if(num>N*N) break;
for(int row=N-2-round;row>round&&num<=N*N;row--) //從下到上
matrix[row*N+round] = num++;
if(num>N*N) break;
round++;
}
}
void snakeMatrix(int matrix[], int N)
{
int diag = 0;
int num = 1;
while(1)
{
if(diag<N){
//從左下到右上
if(diag%2==0){
for(int col=0;col<=diag&&num<=N*N;col++)
matrix[(diag-col)*N+col] = num++;
}
//從右上到左下
else{
for(int col=diag;col>=0&&num<=N*N;col--)
matrix[(diag-col)*N+col] = num++;
}
}
else{
int diag1 = 2*(N-1)-diag;
//從左下到右上
if(diag%2==0){
for(int col=N-1-diag1;col<=N-1&&num<=N*N;col++)
matrix[(diag-col)*N+col] = num++;
}
//從右上到左下
else{
for(int col=N-1;col>=N-1-diag1&&num<=N*N;col--)
matrix[(diag-col)*N+col] = num++;
}
}
printMatrix(matrix, N*N);
if(num>N*N) break;
diag++;
}
}
void printMatrix(int m[], int M)
{
int N = sqrt(M*1.0);
for(int i=0;i<M;i++){
if((i+1)%N==0){
cout<<m[i]<<" \n";
}
else cout<<m[i]<<" ";
}
}
3. 二叉樹的遍歷(非遞歸)
(基礎…)
深度優先搜索算法(前序遍歷),可以藉助堆棧的數據結構,由於堆棧是後進先出的順序,由此可以先將右子樹壓棧,然後再對左子樹壓棧,這樣一來,左子樹結點就存在了棧頂上,因此某結點的左子樹能在它的右子樹遍歷之前被遍歷。
參考代碼:
void depthFirstSearch(Tree* root){
stack<Tree *> nodeStack; //使用C++的STL標準模板庫
nodeStack.push(root);
Tree *node;
while(!nodeStack.empty()){
node = nodeStack.top();
printf(format, node->data); //遍歷根結點
nodeStack.pop();
if(node->rchild){
nodeStack.push(node->rchild); //先將右子樹壓棧
}
if(node->lchild){
nodeStack.push(node->lchild); //再將左子樹壓棧
}
}
}
廣度優先算法,藉助隊列數據結構,由於隊列是先進先出的順序,因此可以先將左子樹入隊,然後再將右子樹入隊。這樣一來,左子樹結點就存在隊頭,可以先被訪問到。
參考代碼:
void breadthFirstSearch(Tree* root){
queue<Tree *> nodeQueue; //使用C++的STL標準模板庫
nodeQueue.push(root);
Tree *node;
while(!nodeQueue.empty()){
node = nodeQueue.front();
nodeQueue.pop();
printf(format, node->data);
if(node->lchild){
nodeQueue.push(node->lchild); //先將左子樹入隊
}
if(node->rchild){
nodeQueue.push(node->rchild); //再將右子樹入隊
}
}
}
4. 各種排序算法
可參考http://www.cnblogs.com/kkun/archive/2011/11/23/2260312.html
選擇排序、快速排序、希爾排序、堆排序不是穩定的排序算法,
冒泡排序、插入排序、歸併排序和基數排序是穩定的排序算法。
可以看一下http://blog.csdn.net/jiuyueguang/article/details/12028393
寫下快速排序和歸併排序:
(1) 快速排序
通過掃描一次將要排序的數據分割成獨立的兩部分(通常用首位作爲flag),其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
#include<iostream>
#include<vector>
usingnamespace std;
voidquicksort(vector<int> &v, int left, int right);
int main(){
vector<int> test;
int elem;
while(cin>>elem &&elem!='\n'){
test.push_back(elem);
}
quicksort(test,0,test.size()-1);
for(int i=0;i<test.size();i++)
cout << test[i]<< " ";
return 0;
}
voidquicksort(vector<int> &v, int left, int right){
if(left<right){
int flag = v[left];
int low = left;
int high = right;
while(low<high){
while(low<high&& v[high]>flag){
high--;
}
v[low] = v[high];
while(low < high&& v[low] < flag){
low++;
}
v[high] = v[low];
}
v[low] = flag;
quicksort(v,left,low-1);
quicksort(v,low+1,right);
}
}
(2) 歸併排序
原理,把原始數組分成若干子數組,對每一個子數組進行排序,繼續把子數組與子數組合並,合併後仍然有序,直到全部合併完,形成有序的數組
#include<iostream>
#include<vector>
usingnamespace std;
voidmergesort(int a[], int first, int last, int temp[]);
voidmergearray(int a[], int first, int mid, int last, int temp[]);
int main(){
int test[6] = {2,3,1,6,8,4};
int after[6];
mergesort(test,0,5,after);
for(int i=0;i<6;i++)
cout << after[i]<< " ";
return 0;
}
voidmergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
while (i <= m && j <= n)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i <= m)
temp[k++] = a[i++];
while (j <= n)
temp[k++] = a[j++];
for (i = 0; i < k; ++i)
a[first + i] = temp[i];
}
voidmergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid = (first + last) / 2;
mergesort(a, first, mid,temp); //左邊有序
mergesort(a, mid + 1, last,temp); //右邊有序
mergearray(a, first, mid,last, temp); //再將兩個有序數列合併
}
}
網易實習生面試
因爲軟件園好多地方都在施工,甚至把路都堵住了,頂着大太陽找了好久才找到網易.......對於一個路癡來說簡直是.....地圖+指南針還經常搞不清往哪裏走==心真累。
面試官看起來蠻年輕,典型技術男的長相,先是做了自我介紹,然後針對簡歷簡單聊了幾句....然後就拿出了白紙,做了兩道編程題:
(1)string型數組,將數組裏所有的無序字符串保留下來,如["abc","ab","bca","cba","cd"],則輸出爲["abc","bca","cba"]。
具體思路就是a.對每個字符串進行排序,生成一個新的有序字符串數組;b.對有序字符串數組掃描一次,用map<string,vector<int>>記錄下有序字符串及對應的位置;c.如果記錄字符串位置的vector長度大於1,則保留vector中所有位置的無序字符串。
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
using namespace std;
#define LEN 10
vector<string> findDisorder(string test[], int N);
int main(){
string test[LEN] = {"what", "abc", "hawt","ab","bca","acb","www","htaw","mmm"};
vector<string> result;
result = findDisorder(test,LEN);
for(int i=0;i<result.size();i++)
cout << result[i] << " ";
return 0;
}
vector<string> findDisorder(string test[], int N){
vector<string> sorted;
vector<string> ans;
for(int i=0;i<N;i++){
sorted.push_back(test[i]);
sort(sorted[i].begin(),sorted[i].end());//直接使用STL中的排序函數
}
map<string,vector<int>> locations;//存放所有的無序字符串及其在源字符串中的位置
for(int i=0;i<sorted.size();i++){
if(locations.count(sorted[i]))
locations[sorted[i]].push_back(i);
else{
vector<int> loc;
loc.push_back(i);
locations.insert(pair<string,vector<int>>(sorted[i],loc));
}
}
map< string,vector<int> >::iterator it;
for(it=locations.begin();it!=locations.end();it++){
if(it->second.size()!=1) {
for(int i=0;i<it->second.size();i++)
ans.push_back(test[it->second[i]]);
}
}
return ans;
}
(2)int型數組,將數組裏的元素連接拼成最大的數字,如[12,132,89,15]的輸出是一個string:“891513212”。
具體思路就是使用歸併排序,而比較大小的sort準則需要自己寫。簡單來說下sort準則:[a,b]可以組合成兩個字符串:a+b/b+a,比大小,前者大則a大,後者大則b大。
#include<iostream>
#include<string>
#include<sstream>
using namespace std;
#define LEN 3
string maxNum(int test[], int N);
void mergesort(string a[], int first, int last, string temp[]);
void mergearray(string a[], int first, int mid, int last, string temp[]);
bool maxThan(string a, string b);
int main(){
int test[LEN] = {98, 12, 120};
string result = maxNum(test,LEN);
cout << result << endl;
return 0;
}
string maxNum(int test[], int N)
{
string *ans = new string[N];
string *temp = new string[N];
string sum="";
for(int i=0;i<N;i++){
stringstream os;
os<<test[i];
ans[i] = os.str();
}
mergesort(ans,0,N-1,temp);
for(int i=0;i<N;i++) sum+=ans[i];
delete[] ans;
delete[] temp;
return sum;
}
void mergesort(string a[], int first, int last, string temp[])
{
if (first < last)
{
int mid = (first + last) / 2;
mergesort(a, first, mid, temp); //左邊有序
mergesort(a, mid + 1, last, temp); //右邊有序
mergearray(a, first, mid, last, temp); //再將兩個有序數列合併
}
}
void mergearray(string a[], int first, int mid, int last, string temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
while (i<=m && j <= n)
{
if (maxThan(a[i],a[j]))
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i <= m)
temp[k++] = a[i++];
while (j <= n)
temp[k++] = a[j++];
for (i = 0; i < k; ++i)
a[first + i] = temp[i];
}
bool maxThan(string a, string b){
string str1 = a+b;
string str2 = b+a;
return str1>str2;
}
感覺自己通常可以理清解決問題的思路,但是幾乎只能寫出僞代碼...好多函數記不清...基礎還是太差==另外,我之前投的崗位是研發工程實習生,但是聽面試官的意思是他們主要做有道系統開發,在這方面完全沒有優勢....好吧,等待結果....估計要掛...