• 网易 2016 实习研发project师 3道 编程题


    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
    
    }
    };
    
  • 相关阅读:
    Fortran编译器之一GUN Fortran安装(Windows XP)
    c++动态绑定的技术实现
    c++标准库比较
    java array
    java常用的基础容器
    mac sublime text 3 add ctags plugin
    git fetch
    查看远程分支的log
    git删除分支
    detached HEAD state
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7380954.html
Copyright © 2020-2023  润新知