1. 程式人生 > >網易 2016 實習研發project師 3道 編程題

網易 2016 實習研發project師 3道 編程題

堆棧 finder rst 起點 wap ise 狀態 swap 由於

1 比較重量

給定兩顆鉆石的編號g1,g2,編號從1開始。同一時候給定關系數組vector,當中元素為一些二元組。第一個元素為一次比較中較重的鉆石的編號,第二個元素為較輕的鉆石的編號。最後給定之前的比較次數n。

請返回這兩顆鉆石的關系,若g1更重返回1,g2更重返回-1,無法推斷返回0。

輸入數據保證合法,不會有矛盾情況出現。

測試例子:
2,3,[[1,2],[2,4],[1,3],[4,3]],4
返回: 1

class Cmp {
public:
    int cmp(int g1, int g2, vector<vector<int
>
>
records, int n) { // write code here } };

思路:有向圖。給定起點和重點。求是否存在路徑

1.1 第1次提交 錯誤 結果超時

題目說沒有違例數據。所以沒有考慮反復數據和矛盾數據,暴力搜

class Cmp {
public:
    int cmp(int g1, int g2, vector<vector<int> > records, int n) {
        // write code here
        // 轉換為鄰接表
        vector<int
>
tempVector; vector< vector<int> > graph; graph.resize(n+1, tempVector); for (int i = 0; i < records.size(); i++) { int u = records[i][0]; int v = records[i][1]; graph[u].push_back(v); } int curStart = g1; for
(int i = 0; i <graph[curStart].size(); i++){ if(graph[curStart][i] == g2) // 直接相連的情況 return 1; // return else { curStart = graph[curStart][i]; // 下個要訪問的鄰接點 i = -1; // 繼續循環。又一次計數 } } return -1; // return } };

1.2 第2次提交 錯誤 遞歸過深

依舊沒考慮反復數據和矛盾數據。深度優先搜索,錯誤提示發生段錯誤,可能是數組越界,堆棧溢出。遞歸調用層過多,可是後來發現是ret的返回值推斷錯了,坑

int dfs(vector<vector<int> >& graph, int start, int end){
    if(start == end) return 1;
    for(int i = 0; i < graph[start].size(); ++i){
        int ret = dfs(graph, graph[start][i], end);
        if(ret){ // 這裏推斷條件應該改為ret == 1
            return 1;
        }
    }
    return -1;
}

int cmp(int g1, int g2, vector<vector<int> > records, int n) {
    // write code here
    // 轉換為鄰接表
    vector<int> tempVector;
    vector< vector<int> > graph;
    graph.resize(n+1, tempVector);
    for (int i = 0; i < records.size(); i++) {
        int u = records[i][0];
        int v = records[i][1];
        graph[u].push_back(v);
    }
    int ret = dfs(graph, g1, g2);
    return ret;

}

1.3 終於提交 AC

中間直接提交“return 1”的錯誤代碼時。系統給出提示。測試用例有反復數據比如[5,8][5,8],也有不可能數據比如[5,5],除此之外還要考慮無法判定的情況,這類情況包括是否有環路情況比如1<2<3<1等。還包括雙向均不連通的情形。


終於提交的版本號:


// 有向圖,給定起點終點。求是否存在路徑
#include <vector>
#include <map>
using namespace std;

vector<bool> mark; // 標記start節點是否已經有鏈表,有則直接push。否則申請vector再push
class Cmp{
public:
    // 遞歸找start到end是否存在路徑
    int dfs(map<int, vector<int> >& graph, int start, int end){
        if(start == end) return 1;
        for(int i = 0; i < graph[start].size(); ++i){
            int ret = dfs(graph, graph[start][i], end);
            if(ret == 1){
                return 1;
            }
        }
        return -1;
    }

    // 輸入有反復,比如[5,8].....[5,8]反復壓入造成遞歸規模增長
    // 壓入前推斷當前start頂點的數組中是否已有,是則去掉,否則壓入
    bool isExisted(vector<int>& v, int value){
        for(int i =0; i < v.size(); ++i){
            if(value == v[i])
                return true;
        }
        return false;
    }
    int cmp(int g1, int g2, vector<vector<int> > records, int n) {
        // write code here
        // 轉換為鄰接表
        map<int, vector<int> > graph; // 編號不連續。節省內存
        mark.resize(n+1, false); // mark初始化為false
        for (int i = 0; i < records.size(); i++) {
            int u = records[i][0];
            int v = records[i][1];
            // 去掉矛盾數據,比如[5,5]
            if(u == v) continue;
            if(!mark[u]){ // 當前start點沒鏈表,也肯定不存在反復數據
                mark[u] = true;
                vector<int> tempVec;
                tempVec.push_back(v);
                graph[u] = tempVec;
            } else { // 當前start有鏈表。繼續推斷是否有反復數據
                if(!isExisted(graph[u], v)){
                    graph[u].push_back(v);
                }
            }
        }
        // 無法判定的情況,包括了環路的矛盾情況
        int ret1 = dfs(graph, g1, g2);
        int ret2 = dfs(graph, g2, g1);
        if(ret1 == ret2) {return 0;}
        return ret1;
    }
};

附: 前兩次提交時用的測試程序

#include <vector>
#include <iostream>

using namespace std;
/*
// 2. 發生段錯誤。可能是數組越界,堆棧溢出(遞歸調用層數太多)
int dfs(vector< vector<int> >& graph, int start, int end){
    if(start == end) return 1;
    for(int i = 0; i < graph[start].size(); ++i){
        int ret = dfs(graph, graph[start][i], end);
        if(ret){ //事實上是錯在這裏了,推斷條件應該改為ret == 1
            return 1;
        }
    }
    return -1;
}
*/

// 3. 改進的dfs

int main() {
    vector< vector<int> > records;
    vector<int> tempVector;
    vector< vector<int> > graph;
    int n = 4;
    int start = 2;
    int end = 3;
    // 原始輸入
    tempVector.push_back(1);
    tempVector.push_back(2);
    records.push_back(tempVector);

    tempVector.clear();
    tempVector.push_back(2);
    tempVector.push_back(4);
    records.push_back(tempVector);

    tempVector.clear();
    tempVector.push_back(1);
    tempVector.push_back(3);
    records.push_back(tempVector);

    tempVector.clear();
    tempVector.push_back(4);
    tempVector.push_back(3);
    records.push_back(tempVector);

    /*
    // for test : print original input
    for (int u = 0; u < records.size(); u++) {
        for (int v = 0; v < records[u].size(); v++) {
            cout << records[u][v] << " ";
        }
        cout << endl;
    }
    */

    // 轉換為鄰接表
    tempVector.clear();
    graph.resize(n+1, tempVector);
    for (int i = 0; i < records.size(); i++) {
        int u = records[i][0];
        int v = records[i][1];
        graph[u].push_back(v);
    }

    /*
    // for test : print used input
    for (int u = 0; u < graph.size(); u++) {
        for (int v = 0; v < graph[u].size(); v++) {
            cout << graph[u][v] << " ";
        }
        cout << endl;
    }
    */



    // 1. 超時
    /*
    int curStart = start;

    for(int i = 0; i < graph[curStart].size(); i++){
        if(graph[curStart][i] == end) // 直接相連的情況
        {
            cout << 1; // return
            break;
        }
        else {
            curStart = graph[curStart][i]; // 下個要訪問的鄰接點
            i = -1; // 繼續循環,又一次計數
        }
    }
    cout << -1; // return
    */

    // 2.調用
    int g1 = start;
    int g2 = end;

    int ret = dfs(graph, g1, g2);
    cout << ret;
    return 0;
}

2 二叉樹

有一棵二叉樹,樹上每一個點標有權值。權值各不同樣。請設計一個算法算出權值最大的葉節點到權值最小的葉節點的距離。二叉樹每條邊的距離為1,一個節點經過多少條邊到達還有一個節點為這兩個節點之間的距離。
給定二叉樹的根節點root,請返回所求距離。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/

class Tree {
public:
    int getDis(TreeNode* root) {
        // write code here
    }
};

思路:先求最大、最小的葉節點,然後找兩個節點的最低公共祖先(LCA),然後求兩個節點到LCA的距離和

2.1 第1次提交 錯誤

第1次提交的時候腦殘了。推斷葉子節點的條件寫錯了。葉子節點應該是無左孩也無右孩,所以要在當前層推斷。然後繼續遞歸。

2.2 終於提交 AC

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
        val(x), left(NULL), right(NULL) {
    }
};*/
#include <climits>
#include <iostream>
using namespace std;

int current_min;
int current_max;

class Tree {
public:
    // 中序遍歷找最小、最大葉子節點
    void findMinMax(TreeNode* root){
        if(root == NULL) return;

        if(NULL == root->left && NULL == root->right){
            if(root->val < current_min) current_min = root->val;
            if(root->val > current_max) current_max = root->val;
        }

        findMinMax(root->left);
        findMinMax(root->right);
    }

    // LCA
    TreeNode* findLCA(TreeNode* root, int min, int max){
        if(root == NULL) return NULL;
        if(root->val == min || root->val == max)
            return root;
        TreeNode* left_lca = findLCA(root->left, min, max);
        TreeNode* right_lca = findLCA(root->right, min, max);
        if(left_lca != NULL && right_lca != NULL)
            return root;
        return left_lca != NULL ?

left_lca : right_lca; } // return level of node, root level is 0. int findLevel(TreeNode* root, int val){ if(root == NULL) return -1; if(root->val == val) return 0; int level = findLevel(root->left, val); if(level == -1){ level = findLevel(root->right, val); } if(level != -1) return level + 1; return -1; } int getDis(TreeNode* root) { // write code here current_min = INT_MAX; current_max = INT_MIN; findMinMax(root); TreeNode* lca = findLCA(root, current_min, current_max); int lev_lca = findLevel(root, lca->val); int lev_min = findLevel(root, current_min); int lev_max = findLevel(root, current_max); return lev_min + lev_max - 2*lev_lca; } };

3 尋找第K大

有一個整數數組。請你依據高速排序的思路,找出數組中第K大的數。
給定一個整數數組a,同一時候給定它的大小n和要找的K(K在1到n之間)。請返回第K大的數。保證答案存在。
測試例子:
[1,3,5,2,2],5,3
返回:2

class Finder {
public:
    int findKth(vector<int> a, int n, int K) {
        // write code here
    }
};

直接思路就是排序,然後找相應位置,比較簡單

3.1 復習 高速排序

// 高速排序, 交換次數較少的實現
#include <iostream>
#include <algorithm>
using namespace std;

void printArrayInt(int arr_int[], int size){
    for(int i = 0; i < size; ++i){
        cout << arr_int[i] << " ";
    }
    cout << endl;
}

//劃分操作
int partition(int arr[], int start, int end) {
    int i = start - 1;//指向開頭
    int j = end;//指向結尾
    int pivot = arr[end];
    while(true){
        while(i < end && arr[++i] < pivot);//從前到後  第一個比 基準(x)大的數。

i指向該數 while(j > start && arr[--j] > pivot);//從後向前找到第一個比 基準(x)小的數。j指向該數 if(i >= j) break; swap(arr[i], arr[j]); } swap(arr[i], arr[end]); return i; } void quickSort(int arr_int[], int start, int end){ if(start < end){ int partition_index = partition(arr_int, start, end); quickSort(arr_int, start, partition_index - 1); quickSort(arr_int, partition_index + 1, end); } } int main() { int arr_int[] = {10, 7, 8, 9, 1, 5}; int arr_size = sizeof(arr_int) / sizeof(arr_int[0]); quickSort(arr_int, 0, arr_size-1); return 0; }

3.2 第1次提交 快排 AC:

#include <iostream>
#include <algorithm>
using namespace std;

class Finder {
public:

    //劃分操作
    int partition(int arr[], int start, int end) {
        int i = start - 1;//指向開頭
        int j = end;//指向結尾
        int pivot = arr[end];
        while(true){
            while(i < end && arr[++i] < pivot);//從前到後  第一個比 基準(x)大的數。

i指向該數 while(j > start && arr[--j] > pivot);//從後向前找到第一個比 基準(x)小的數。

j指向該數 if(i >= j) break; swap(arr[i], arr[j]); } swap(arr[i], arr[end]); return i; } void quickSort(int arr_int[], int start, int end){ if(start < end){ int partition_index = partition(arr_int, start, end); quickSort(arr_int, start, partition_index - 1); quickSort(arr_int, partition_index + 1, end); } } int findKth(vector<int> a, int n, int K) { int* arr_int = new int[n]; for(int i = 0; i < n; ++i){ arr_int[i] = a[i]; } quickSort(arr_int, 0, n - 1); return arr_int[n - K]; } };

快排的時間復雜度為O(nlogn)。 比較慢,

更快的方法:
創建一個大小為k的數據容器,存儲k個最大的數字,過程:
a) 若容器中數字不足k個,則放入容器
b) 若容器已經滿了,則找出k個數字中最小的值,若當前值比容器中的最小值大,則替換容器中的最小值。否則拋棄當前值。

容器滿了之後。要做三件事:
(1)在k個整數中找到最小值
(2)有可能刪除最小值
(3)有可能插入新的值,並保證k個整數是已排序。

所以用二叉樹實現容器,則三種操作可在O(logk)時間內完畢,對於n個數字,總的時間是O(nlogk)

由於每次都要取出最小值。且容器保持排序狀態。所以想到最小堆, 直接使用STL中基於紅黑樹的multiset

3.3 第2次提交 最小堆 AC

// 終於提交:
#include <vector>
#include <set>
class Finder {
public:
    typedef multiset<int> MinHeapInt;

void find_kth_max_int(const vector<int>& vec_int, MinHeapInt& largestNumbers, int k){

    largestNumbers.clear();

    if(k <= 0 || vec_int.size() < k)
        return;

    vector<int>::const_iterator iter = vec_int.begin();
    for(; iter != vec_int.end(); ++iter){
        // 最小堆未滿時
        if(largestNumbers.size() < k){
            largestNumbers.insert(*iter);
        }
        else{
            // 最大堆已滿
            MinHeapInt::iterator iterFirst = largestNumbers.begin();
            // 當前元素大於堆中最小值時。替換
            if(*iter > *(largestNumbers.begin())){
                largestNumbers.erase(iterFirst);
                largestNumbers.insert(*iter);
            }
        }
    }
}

    int findKth(vector<int> a, int n, int K) {
        // write code here
        MinHeapInt largestNumbers;
        find_kth_max_int(a, largestNumbers, K);
        return *(largestNumbers.begin());
    }
};

附: 測試程序

#include <vector>
#include <set>
#include <iostream>
using namespace std;

typedef multiset<int> MinHeapInt;

void find_kth_max_int(const vector<int>& vec_int, MinHeapInt& largestNumbers, int k){

    largestNumbers.clear();

    if(k <= 0 || vec_int.size() < k)
        return;

    vector<int>::const_iterator iter = vec_int.begin();
    for(; iter != vec_int.end(); ++iter){
        // 最小堆未滿時
        if(largestNumbers.size() < k){
            largestNumbers.insert(*iter);
        }
        else{
            // 最大堆已滿
            MinHeapInt::iterator iterFirst = largestNumbers.begin();
            // 當前元素大於堆中最小值時,替換
            if(*iter > *(largestNumbers.begin())){
                largestNumbers.erase(iterFirst);
                largestNumbers.insert(*iter);
            }
        }
    }
}


int main(){
    vector<int> data;
    data.push_back(10);
    data.push_back(1);
    data.push_back(7);
    data.push_back(3);
    data.push_back(4);
    data.push_back(8);
    data.push_back(5);
    data.push_back(11);
    data.push_back(12);
    MinHeapInt largestNumbers;
    int k = 4;
    find_kth_max_int(data, largestNumbers, k);
    // for test
    multiset<int>::const_iterator iter = largestNumbers.begin();
    for(; iter!=largestNumbers.end(); ++iter){
        cout << *iter << " ";
    }
    cout << endl;
    cout << "第" << k << "大的數為" << *(largestNumbers.begin()) << endl;
    return 0;
}

3.4 第3次提交 半排序 AC

中間提交了兩次。當中一次發現少了return語句,由於半排序的遞歸過程也是要寫return的,一旦到達kth_max位置,就直接return了。第二次是發現查錯了kth_max的下標。

// 半排序思想
/*
快排的切割過程返回pivot_index位置時(從0計數)。
左側有pivot_index 個比pivot_value小的數。
右側有n - pivot_index-1個比pivot_value大的數。
所以pivot_value本身是第n-pivot_index大的數,
當pivot_index == n-k時,pivot_value為第k大的數
所以kth_max的index為n-k,設為kth_index

遞歸過程相似於二分查找。當pivot_index==kth_index時,程序結束,返回結果
否則,繼續在kth max所在的左側或右側繼續分治排序(相似於二分查找)
*/

#include <algorithm>
using namespace std;
class Finder {
public:
 int partition(int arr_int[], int start, int end){
    int pivot_value = arr_int[end];
    int i = start - 1;
    int j = end;
    while(true){
        while(i < end && arr_int[++i] < pivot_value);
        while(j > start && arr_int[--j] > pivot_value);
        if(i >= j)
            break;
        swap(arr_int[i], arr_int[j]);
    }
    swap(arr_int[i], arr_int[end]);
    return i;
}

int quick_sort_find_kth_max(int arr_int[], int start, int end, int k){

    int pivot_index = partition(arr_int, start, end);
    // kth max 在pivot_index左側,右側都比kth max大,不用管,排左側
    if(pivot_index > k){
        return quick_sort_find_kth_max(arr_int, start, pivot_index - 1, k);
    }
    // kth max 在pivot_index右側,左側逗比kth max小。不用管。排右側
    else if(pivot_index < k){
        return quick_sort_find_kth_max(arr_int, pivot_index + 1, end, k);
    }
    else{
        return arr_int[pivot_index];
    }
}

int findKth(vector<int> a, int n, int K) {
    // write code here
    int* arr_int = new int[n];
    for(int i = 0; i < n; ++i){
        arr_int[i] = a[i];
    }
    return quick_sort_find_kth_max(arr_int, 0, n-1, n-K);// 1,2,3。第3大的是1。所以kth max的index是n-k

}
};

網易 2016 實習研發project師 3道 編程題