程式設計師面試題(C++ 實現) - Day1
文章目錄
說明
- 以下題目均來自於牛客網
- 以下程式碼用 C++11 編寫
- 以下程式碼均已編譯通過(Compile by MINGW)
- 以下程式碼均有測試案例(Main function)
- 以下程式碼均已進行優化或部分優化(Optimize)
- 以下程式碼均有註釋(Comment)
- 部分題目附有解析(Analysis)
- 如有錯誤或侵權,請聯絡博主
題目一覽
- 二維陣列中的查詢
- 替換空格
- 從尾到頭列印連結串列
- 重建二叉樹
- 用兩個棧實現佇列
1. 二維陣列中的查詢
問題描述
在一個二維陣列中(每個一維陣列的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。
解析
先與每行的第一個數字作比較,一旦大於等於該數字,再與該行的在最後一個數字比較,如果小於該數字,則選定該行,然後在該行依次進行比較查詢;否則不存在該整數
實現
/*
在一個二維陣列中(每個一維陣列的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。
請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。
*/
#include <iostream>
#include <vector>
using namespace std;
bool Find(int target, const vector<vector<int> > &array) {
if (array.empty())
return false;
if (target < array[0][0])
return false;
int _length = array.size();
for (int i = 0; i < _length; i++) {
if (array[i].empty())
continue;
else if(target >= array[i][0]) { // 選定該行
if (target <= array[i][array[i].size() - 1]) { // 是否小於該行的最後一個數字
for (int j = array[i].size() - 1; j >= 0; j--) {
if (target == array[i][j])
return 1;
else if (target > array[i][j])
break;
}
} else {
continue;
}
} else
return false;
}
return false;
}
int main(int argc, char const *argv[]) {
// Test
vector<vector<int> > vec;
for(int i = 0; i < 10; i++) {
vector<int> vecson;
for(int j = 0; j < 7; j++) {
vecson.push_back(i + j);
cout << i + j << " ";
}
vec.push_back(vecson);
cout << endl;
}
int target = 13;
cout << "Is the target exist in array? " <<
((Find(target, vec) == false) ? "False" : "True") << endl;
return 0;
}
輸出結果
6 7 8 9 10 11 12
7 8 9 10 11 12 13
8 9 10 11 12 13 14
9 10 11 12 13 14 15
Is the target exist in array? True
2. 替換空格
問題描述
請實現一個函式,將一個字串中的每個空格替換成“%20”。例如,當字串為We Are Happy,則經過替換之後的字串為We%20Are%20Happy。
解析
暫且沒有太好的演算法,目前只是通過指標來實現
更多請參考我的另一篇部落格:C/C++檔案 / 字串 操作大全
實現
/*
請實現一個函式,將一個字串中的每個空格替換成“%20”。
例如,當字串為We Are Happy.
則經過替換之後的字串為We%20Are%20Happy。
*/
#include <iostream>
#include <stdio.h>
using namespace std;
void replaceSpace(char *str, int length) {
if(str == NULL)
return ;
int CountOfBlanks = 0;
int Originallength = 0;
for(int i = 0; str[i] != '\0'; i++) {
Originallength++;
if(str[i] == ' ')
++CountOfBlanks;
}
int len = Originallength + 2 * CountOfBlanks; //因為 %20 比 空格 多兩個字元
if(len + 1 > length)
return ;
char *pStr1 = str + Originallength; //複製結束符‘\0’
char *pStr2 = str + len;
while(pStr1 < pStr2) {
if(*pStr1 == ' ') {
*pStr2-- = '0';
*pStr2-- = '2';
*pStr2-- = '%';
} else {
*pStr2-- = *pStr1;
}
--pStr1;
}
}
int main(int argc, char const *argv[]) {
//Test
char p[] = "AA BB CC ";
printf("替換前:%s\n", p);
replaceSpace(p, 50);
printf("替換後:%s\n", p);
return 0;
}
輸出結果
替換前:AA BB CC
替換後:AA%20BB%20CC%20
3. 從尾到頭列印連結串列
問題描述
輸入一個連結串列,按連結串列值從尾到頭的順序返回一個ArrayList。
解析
使用棧的 LIFO (Last in First out)性質來實現
如果有對連結串列的基本操作不熟悉的,請參考我的另一篇博文:
更多請參考我的另一篇部落格:連結串列大全
實現
/*
輸入一個連結串列,按連結串列值從尾到頭的順序返回一個ArrayList。
*/
#include <vector>
#include <stack>
#include <iostream>
#include <stdlib.h>
using namespace std;
// 定義節點
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
// 建立連結串列
struct ListNode *createList() {
struct ListNode *head = new ListNode(0);
struct ListNode *ret = head;
for(int i = 1; i < 10; i++) {
struct ListNode *p = new ListNode(i);
ret->next = p;
ret = ret->next;
}
return head;
}
// 刪除連結串列
void deleteList(struct ListNode *list) {
struct ListNode *p = list;
while(p != NULL) {
list = p->next;
free(p);
p = list;
}
free(p);
}
// 列印連結串列
void printList(ListNode *head) {
while(head != NULL) {
cout << head->val << " ";
head = head->next;
}
cout << endl;
}
// 從尾部列印連結串列
vector<int> printListFromTailToHead(struct ListNode *const head) {
vector <int> result;
stack<int> arr;
struct ListNode *p = head;
while(p != NULL) {
arr.push(p->val);
p = p->next;
}
int len = arr.size();
for(int i = 0; i < len; i++) {
result.push_back(arr.top());
arr.pop();
}
return result;
}
int main(int argc, char const *argv[]) {
//Test
struct ListNode *head = createList();
cout << "原始列表:\n";
printList(head);
cout << "逆序列印列表:\n";
vector<int> v(printListFromTailToHead(head));
for(int i = 0; i < v.size(); i++)
cout << v[i] << " ";
deleteList(head);
return 0;
}
輸出結果
原始列表:
0 1 2 3 4 5 6 7 8 9
逆序列印列表:
9 8 7 6 5 4 3 2 1 0
4. 重建二叉樹
問題描述
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。
假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。
例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
解析
主要利以下內容:
- 根節點肯定是前序遍歷的第一個數
- 分治思想
分治演算法參考部落格:五大常用演算法之一:分治演算法
如果有對二叉樹的基本操作不熟悉的,請參考我的另一篇博文:
更多請參考我的另一篇部落格:樹結構大全
實現
/*
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。
假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。
例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
*/
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 重建二叉樹
struct TreeNode *reConstructBinaryTree(vector<int> pre, vector<int> in) {
int inlen = in.size();
if(inlen == 0)
return NULL;
vector<int> left_pre, right_pre, left_in, right_in;
//建立根節點,根節點肯定是前序遍歷的第一個數
TreeNode *head = new TreeNode(pre[0]);
//找到中序遍歷根節點所在位置,存放於變數gen中
int gen = 0;
for(int i = 0; i < inlen; i++) {
if (in[i] == pre[0]) {
gen = i;
break;
}
}
//對於中序遍歷,根節點左邊的節點位於二叉樹的左邊,根節點右邊的節點位於二叉樹的右邊
//利用上述這點,對二叉樹節點進行歸併
for(int i = 0; i < gen; i++) {
left_in.push_back(in[i]);
left_pre.push_back(pre[i + 1]); //前序第一個為根節點
}
for(int i = gen + 1; i < inlen; i++) {
right_in.push_back(in[i]);
right_pre.push_back(pre[i]);
}
//和shell排序的思想類似,取出前序和中序遍歷根節點左邊和右邊的子樹
//遞迴,再對其進行上述所有步驟,即再區分子樹的左、右子子數,直到葉節點
head->left = reConstructBinaryTree(left_pre, left_in);
head->right = reConstructBinaryTree(right_pre, right_in);
return head;
}
// 列印二叉樹
vector<vector<int> > Print(TreeNode *pRoot) {
vector<vector<int> > Value;
if (pRoot == NULL)
return Value;
queue<TreeNode * > queNode;
queNode.push(pRoot);
vector<int> subValue; //輔助陣列,儲存當前層所有的結點值
int nextLevels = 0; //用來統計下一層的結點數
int currentLevels = 1; //用來標記當前層剩餘的沒有列印的結點數
while (!queNode.empty()) {
TreeNode *pNode = queNode.front();
queNode.pop();
subValue.push_back(pNode->val);
if (pNode->left != NULL) {
queNode.push(pNode->left);
++nextLevels;
}
if (pNode->right != NULL) {
queNode.push(pNode->right);
++nextLevels;
}
--currentLevels;
if (currentLevels == 0) { //如果當前層結點已全部列印完畢
Value.push_back(subValue);
subValue.clear(); //清空,開始存下一層結點
currentLevels = nextLevels; //下一層要列印的結點數
nextLevels = 0; //置0,開始統計下一層結點數
}
}
return Value;
}
void plr(TreeNode *pRoot) {
if(pRoot != NULL) {
cout << pRoot->val << endl;
if(pRoot->left != NULL)
cout << "left " << pRoot->left->val << endl;
if(pRoot->right != NULL)
cout << "right " << pRoot->right->val << endl;
}
}
int main(int argc, char const *argv[]) {
// Test
// 建立 vec_pre vec_in
int arr_pre[8] = {1, 2, 4, 7, 3, 5, 6, 8};
vector<int> vec_pre(arr_pre, arr_pre + 8);
int arr_in[8] = {4, 7, 2, 1, 5, 3, 8, 6};
vector<int> vec_in(arr_in, arr_in + 8);
struct TreeNode *head = reConstructBinaryTree(vec_pre, vec_in);
vector<vector<int> > list(Print(head));
for(int i = 0; i < list.size(); i++) {
for(int j = 0; j < list[i].size(); j++) {
cout << list[i][j] << " ";
}
cout << endl;
}
return 0;
}
輸出結果
1
2 3
4 5 6
7 8
5. 用兩個棧實現佇列
問題描述
用兩個棧來實現一個佇列,完成佇列的Push和Pop操作。 佇列中的元素為int型別。
解析
利用棧的 LIFO性質(Last in First out)
實現
注:這個版本有很多細節沒有注意起來,參考第二個完美版本,但是比較複雜
/*
用兩個棧來實現一個佇列,完成佇列的Push和Pop操作。 佇列中的元素為int型別。
*/
#include <iostream>
#include <stack>
using namespace std;
class Queue {
public:
void push(int node) {
stack1.push(node);
}
int pop() {
int res;
if (stack2.size() > 0) {
res = stack2.top()