[leetcode]690. Employee Importance三种解法及性能分析

You are given a data structure of employee information, which includes the employee’s unique id, his importance value and his direct subordinates’ id.
For example, employee 1 is the leader of employee 2, and employee 2 is the leader of employee 3. They have importance value 15, 10 and 5, respectively. Then employee 1 has a data structure like [1, 15, [2]], and employee 2 has [2, 10, [3]], and employee 3 has [3, 5, []]. Note that although employee 3 is also a subordinate of employee 1, the relationship is not direct.
Now given the employee information of a company, and an employee id, you need to return the total importance value of this employee and all his subordinates.
Example 1:
Input: [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]], 1
Output: 11
Explanation:
Employee 1 has importance value 5, and he has two direct subordinates: employee 2 and employee 3. They both have importance value 3. So the total importance value of employee 1 is 5 + 3 + 3 = 11.
Note:
One employee has at most one direct leader and may have several subordinates.
The maximum number of employees won’t exceed 2000.

这道题本身并没有什么技巧,就按照最直观的想法暴力求解即可。但在实现过程中,有一些值得思考的性能问题。

首先我们考虑最直接的方法,递归求解

// 题目中给出的数据结构
class Employee {
public:
    // It's the unique ID of each node.
    // unique id of this employee
    int id;
    // the importance value of this employee
    int importance;
    // the id of direct subordinates
    vector<int> subordinates;
};

// 代码实现
int getImportance(vector<Employee*> employees, int id) {
	 int res = 0, num = 0;
        while(num < employees.size() && employees[num]->id != id) num++;
        if(num == employees.size()) return 0;
        res += employees[num]->importance;
        
        if(employees[num]->subordinates.size()) {
            for(int i = 0; i < employees[num]->subordinates.size(); i++) res += getImportance(employees, employees[num]->subordinates[i]);
        }
        return res;
}

在leetcode上运行,时间为36ms, 比35.99%的解法要快,空间为25.6M,显然空间占用太大,时间上也可以提升。
于是我们将递归改为递推,参考BFS思想,leader出队时将employees压入队列中,依次遍历每一个employee的importance。

// 题目中给出的数据结构
class Employee {
public:
    // It's the unique ID of each node.
    // unique id of this employee
    int id;
    // the importance value of this employee
    int importance;
    // the id of direct subordinates
    vector<int> subordinates;
};

// 代码实现
int getImportance(vector<Employee*> employees, int id) {
        int res = 0;
        queue<int> q;
        q.push(id);
        
        while(!q.empty()) {
            int num = 0;
            while(num < employees.size() && employees[num]->id != q.front()) num++;
            res += employees[num]->importance;
            for(int i = 0; i < employees[num]->subordinates.size(); i++) q.push(employees[num]->subordinates[i]);
            q.pop();
        }
        return res;
}

运行结果为32ms,比69.90%的解法要快,空间复杂度降到了14.3M,优化效果很明显。
但这种解法还可以继续优化。注意到该问题中,时间耗费最多的操作是对所有employees的遍历。我们自然可以想到,由于employees无序,传统的顺序查找是效率比较低的,而采用二分查找等方法会提升查找性能。因此我们建立一个hashmap,将id与importance对应起来:

// 题目中给出的数据结构
class Employee {
public:
    // It's the unique ID of each node.
    // unique id of this employee
    int id;
    // the importance value of this employee
    int importance;
    // the id of direct subordinates
    vector<int> subordinates;
};

// 代码实现
int getImportance(vector<Employee*> employees, int id) {
        int res = 0;
        queue<int> q;
        q.push(id);
        map<int, int> score;
        for(int i = 0; i < employees.size(); i++) score.insert(pair<int, int>(employees[i]->id, employees[i]->importance));
        
        while(!q.empty()) {
            int num = 0;
            while(num < employees.size() && employees[num]->id != q.front()) num++;
            res += score[employees[num]->id];
            for(int i = 0; i < employees[num]->subordinates.size(); i++) q.push(employees[num]->subordinates[i]);
            q.pop();
        }
        return res;
}

但是实际运行时,该解法运行时间为40ms,仅比18.47%的解法更优,而空间占用达到了15.2M。空间开销变大很好理解,但是时间甚至不如递归解法,我认为是建立map的过程耗费时间是无法忽略的。注意到题目中说明employees不超过2000,因此可以认为在这个量级上直接查找会更有效。但是如果数据规模更大,我认为第三种解法是有优势的。
那么最好的解法是什么呢?我在讨论区找到一个20ms,超过99.58%的C++解法,但是它也是采用相同的思路,只是map换成了unordered_map:

// 题目中给出的数据结构
class Employee {
public:
    // It's the unique ID of each node.
    // unique id of this employee
    int id;
    // the importance value of this employee
    int importance;
    // the id of direct subordinates
    vector<int> subordinates;
};

// 代码实现
int getImportance(vector<Employee*> employees, int id) {
    int result = 0;

    // populate mapping from id(int) to emp(Employee*)
    std::unordered_map<int, Employee*> id2emp;
    for(auto em_ptr: employees)
        id2emp[em_ptr->id] = em_ptr;

    // return 0 when the start_id doesn't exist
    if(id2emp.find(start_id) == id2emp.end())
        return result;

    std::queue<int> q;
    q.push(start_id); // start searching from the start_id
    while(!q.empty())
    {
        // pop and increase
        int current_id = q.front();
        q.pop();
        result += id2emp[current_id]->importance;

        // push subordinates
        for(const int& sub_id: id2emp[current_id]->subordinates)
        {
            q.push(sub_id);
        }
    }
    return result;
}

我在实际测试后发现时间为32ms,超过69.90%,空间为14.9M,相比之前的方法确实有一些提升,但是远没有达到20ms的水平。我想单纯从这道题上来看,做到这一步优化空间已经比较小了。如果大家有什么想法,欢迎留言交流。

发布了11 篇原创文章 · 获赞 5 · 访问量 3331

猜你喜欢

转载自blog.csdn.net/Liut2016/article/details/103178713