1. 程式人生 > >程式設計師面試題(C++ 實現) - Day1

程式設計師面試題(C++ 實現) - Day1

文章目錄

說明


  • 以下題目均來自於牛客網
  • 以下程式碼用 C++11 編寫
  • 以下程式碼均已編譯通過(Compile by MINGW)
  • 以下程式碼均有測試案例(Main function)
  • 以下程式碼均已進行優化或部分優化(Optimize)
  • 以下程式碼均有註釋(Comment)
  • 部分題目附有解析(Analysis)
  • 如有錯誤或侵權,請聯絡博主

題目一覽


  1. 二維陣列中的查詢
  2. 替換空格
  3. 從尾到頭列印連結串列
  4. 重建二叉樹
  5. 用兩個棧實現佇列

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()