C ++ codility挑战中的性能问题

时间:2014-11-27 10:37:13

标签: c++ performance algorithm

编辑:问题实际上是算法(感谢molbdnilo下面的回答)失败的情况是O(N2) - >二次。下面的人实际上试图找到一个真正的最坏情况O(N Log(N))时间复杂度算法。

我参加了本月的抄袭挑战。我花了大约一个小时来获得100%正确的O(N Log(N))时间复杂度算法。

但是你可以在下面看到我的性能达到了75%,因为其中一项测试需要花费10倍才能运行。我不明白为什么! 你能指出我的错误吗?

  1. https://codility.com/cert/view/
  2. https://codility.com/cert/view/certD6ZTBR-RJTNQE24V242YRCV/details
  3. 第2点包含我的解决方案的完整问题描述和完整报告(测试用例和时间)。

    粗略地说,我逐条添加每根绳子并从添加的节点位置更新根(祖先)的路径,新的最大权重可以在每个祖先的“下/下”添加。

    以下是代码:

        // you can use includes, for example:
        #include <algorithm>
        #include <vector>
        #include <map>
        #include <iostream>
    
        using namespace std;
        // you can write to stdout for debugging purposes, e.g.
        // cout << "this is a debug message" << endl;
    
    
        struct node
        {
            int max_to_add;
            int id;
            node* mummy;
        };
    
        std::map< int, node* > nodes;
    
        bool insertRope( int durability, int pos, int Id, int weight )
        {
            node* n = new node;
            n->id = Id;
            nodes[Id] = n;
    
            if( pos == -1 )
            {
                n->max_to_add = durability - weight;
                n->mummy = NULL;
                if( n->max_to_add < 0 ) return false;
            }
            else
            {
                std::map< int, node* >::iterator it = nodes.find(pos);
                if( it != nodes.end() )
                {
                    node* parent = (*it).second;
    
                    n->mummy = parent;
                    n->max_to_add = std::min( ( parent->max_to_add - weight),  (durability - weight) ) ;
                    if( n->max_to_add < 0 ) return false;
    
                    node* current = n;
                    while ( (current = current->mummy) != NULL )
                    {
                        current->max_to_add = current->max_to_add - weight;
                        if( current->max_to_add < 0 ) 
                        {
                            return false;
                        }
                    }
                }
    
            }
            return true;
        }
    
        int solution(vector<int> &A, vector<int> &B, vector<int> &C) {
            // write your code in C++11
    
            for(int i = 0; i < A.size() ; ++i) 
            {
                if( insertRope( A[i], C[i],i,B[i] ) == false ) {return i;} 
            }
    
            return A.size();
        }
    
        int main()
        {
    
            /*static const int arrA[] = {4, 3, 1};
            vector<int> vecA (arrA, arrA + sizeof(arrA) / sizeof(arrA[0]) );
    
            static const int arrB[] = {2, 2, 1};
            vector<int> vecB (arrB, arrB + sizeof(arrB) / sizeof(arrB[0]) );
    
    
            static const int arrC[] = {-1, 0, 1};
            vector<int> vecC (arrC, arrC + sizeof(arrC) / sizeof(arrC[0]) );
            */
    
    
            static const int arrA[] = {5, 3, 6, 3, 3};
            vector<int> vecA (arrA, arrA + sizeof(arrA) / sizeof(arrA[0]) );
    
            static const int arrB[] = {2, 3, 1, 1, 2};
            vector<int> vecB (arrB, arrB + sizeof(arrB) / sizeof(arrB[0]) );
    
    
            static const int arrC[] = {-1, 0, -1, 0, 3};
            vector<int> vecC (arrC, arrC + sizeof(arrC) / sizeof(arrC[0]) );
    
            int sol = solution(vecA,vecB,vecC);
    
            system("PAUSE");
            return 0;
        }
    

    编辑1: 根据Rafid的建议我使用new [],更好,但我仍然有perf问题: https://codility.com/cert/view/certRT5YDP-W65HGPF28B5RN5AY/details

3 个答案:

答案 0 :(得分:1)

我可以指出的一个性能提示是:避免重复使用'new'运算符,因为它很昂贵。您可以创建一个大的内存块,然后在需要时使用它,这样就不会在堆中重复分配内存。

答案 1 :(得分:0)

免责声明:我并非100%确定这是导致您出现问题的原因。

请注意,您执行非常糟糕的情况如下: “行”配置中的100K项目。

如果你看看你的while循环,你会发现你的算法没有提供O(NlogN)最坏情况的复杂性。最糟糕的情况是所有绳索都是对齐的,每次添加节点时都必须遍历整个树以更改max_to_add

这并没有改变这样一个事实:你可能(正如在一些评论中所建议的那样)单独使用std :: map而没有指针,这可能会提供更好的性能,因为你不需要new分配每次创建节点时,都会在堆栈上创建它们。也许甚至使用std :: unordered_map。

编辑:

好的,我找到了一种更好的复杂性。您不需要更新所有绳索的负载,只需要在树上的叶子节点上维护正确的值。此值应该是此分支上节点的max_to_add的最小值。

我会尽可能发布一些代码。

答案 2 :(得分:0)

如果我们只是跳过将元素添加到地图并尝试计算节点的当前容量,该怎么办?现在很难检查它,但这是代码,它显示了这个想法。

bool process_node(std::vector<int> &A, std::vector<int> &B, std::vector<int> &C, int index) {

    int next_parent = C[index];

    A[index] -= B[index];

    while(next_parent != -1) {

        A[next_parent] -= B[index];
        if (A[next_parent] < 0) {
            return false;
        }

        next_parent = C[next_parent];
    }

    return true;
}

int solution(std::vector<int> &A, std::vector<int> &B, std::vector<int> &C) {

    for (int i = 0; i < A.size(); ++i) {
        if (!process_node(A, B, C, i)) {
            return i;
        }
    }

    return A.size();
}

看起来像O(NlogN)时间,因为我们在树中进行N次父查找。也许我们可以避免一些额外的遍历。

相关问题