Làm sao để tìm nút trung tâm của cây N-ary?

2 min read

Cho một cây N-ary có N nút được đánh số từ 0 đến N-1 và danh sách các cạnh vô hướng, nhiệm vụ là tìm (các) nút ở tâm của cây đã cho.

Trước khi tìm hiểu cách tìm nút ở giữa của cây ta cần hiểu một vài thuật ngữ sau:

  • Độ lệch tâm: Độ lệch tâm của bất kỳ đỉnh V nào trong một cây nhất định là khoảng cách tối đa giữa đỉnh V đã cho và bất kỳ đỉnh nào khác của cây. 
  • Tâm: Tâm của cây là đỉnh có độ lệch tâm nhỏ nhất.

Vây, để tìm được tâm chúng ta phải giảm thiểu độ lệch tâm này. 

Ví dụ:

Đầu vào: 

  • N = 4,
  • Cạnh[] = { (1, 0), (1, 2), (1, 3)} 

Đầu ra: 1 
Giải thích: 

Đầu vào: 

  • N = 6,
  • Cạnh[] = { (0, 3), (1, 3), (2, 3), (4, 3), (5, 4)} 

Đầu ra: 3, 4 
Giải thích: 

Có thể quan sát thấy đường đi có độ lệch tâm cực đại là đường kính của cây. Do đó, tâm của đường kính cây cũng sẽ là tâm của cây.

  • Nếu đường kính bao gồm số nút lẻ thì chỉ tồn tại 1 tâm.
  • Nếu đường kính bao gồm số nút chẵn thì có 2 nút trung tâm.
  • Độ phức tạp về thời gian: O(N) 
  • Không gian phụ trợ: O(N)

Dưới đây là cách thực hiện phương pháp trên với c++:  

Hàm thêm cạnh giữa 2 điểm

void addedge(int a, int b)
{
    tree[a].push_back(b);
    tree[b].push_back(a);
}

Hàm lấy và trả về nút xa nhất từ ​​một đỉnh nhất định

void farthestNode(int vertex, int parent,
                  int height, int& maxHeight,
                  int& maxHeightNode)
{
    if (height > maxHeight) {
        maxHeight = height;
        maxHeightNode = vertex;
    }
    for (auto i : tree[vertex]) {
        if (i == parent)
            continue;
 
        farthestNode(i, vertex,
                     height + 1,
                     maxHeight,
                     maxHeightNode);
    }
}

Hàm lưu trữ đường đi từ đỉnh đã cho đến đỉnh đích trong đường dẫn vectơ

bool getDiameterPath(int vertex,
                     int targetVertex,
                     int parent,
                     vector<int>& path)
{
    if (vertex == targetVertex) {
 
        path.push_back(vertex);
        return true;
    }
 
    for (auto i : tree[vertex]) {
        if (i == parent)
            continue;
 
        if (getDiameterPath(i, targetVertex,
                            vertex, path)) {
            path.push_back(vertex);
            return true;
        }
    }
 
    return false;
}

Hàm tìm node trung tâm

void FindCenter(int n)
{
    int maxHeight = -1;

    int maxHeightNode = -1;
 
    farthestNode(0, -1, 0, maxHeight,
                 maxHeightNode);

    int leaf1 = maxHeightNode;

    maxHeight = -1;
    farthestNode(maxHeightNode,
                 -1, 0, maxHeight,
                 maxHeightNode);
    int leaf2 = maxHeightNode;
 
    vector<int> path;
 
    getDiameterPath(leaf1, leaf2,
                    -1, path);
 
    int pathSize = path.size();
 
    if (pathSize % 2) {
        cout << path[pathSize / 2]
             << endl;
    }
    else {
        cout << path[pathSize / 2]
             << ", "
             << path[(pathSize - 1) / 2]
             << endl;
    }
}

Kết hợp code

#include <bits/stdc++.h>
using namespace std;

void addedge(int a, int b)
{
    tree[a].push_back(b);
    tree[b].push_back(a);
} 

void farthestNode(int vertex, int parent,
                  int height, int& maxHeight,
                  int& maxHeightNode)
{
    if (height > maxHeight) {
        maxHeight = height;
        maxHeightNode = vertex;
    }
    for (auto i : tree[vertex]) {
        if (i == parent)
            continue;
 
        farthestNode(i, vertex,
                     height + 1,
                     maxHeight,
                     maxHeightNode);
    }
}

bool getDiameterPath(int vertex,
                     int targetVertex,
                     int parent,
                     vector<int>& path)
{
    if (vertex == targetVertex) {
 
        path.push_back(vertex);
        return true;
    }
 
    for (auto i : tree[vertex]) {
        if (i == parent)
            continue;
 
        if (getDiameterPath(i, targetVertex,
                            vertex, path)) {
            path.push_back(vertex);
            return true;
        }
    }
 
    return false;
}

void FindCenter(int n)
{
    int maxHeight = -1;

    int maxHeightNode = -1;
 
    farthestNode(0, -1, 0, maxHeight,
                 maxHeightNode);

    int leaf1 = maxHeightNode;

    maxHeight = -1;
    farthestNode(maxHeightNode,
                 -1, 0, maxHeight,
                 maxHeightNode);
    int leaf2 = maxHeightNode;
 
    vector<int> path;
 
    getDiameterPath(leaf1, leaf2,
                    -1, path);
 
    int pathSize = path.size();
 
    if (pathSize % 2) {
        cout << path[pathSize / 2]
             << endl;
    }
    else {
        cout << path[pathSize / 2]
             << ", "
             << path[(pathSize - 1) / 2]
             << endl;
    }
}

map<int, vector<int> > tree;
int main()
{
    int N = 4;
    addedge(1, 0);
    addedge(1, 2);
    addedge(1, 3);
 
    FindCenter(N);
 
    return 0;
}

Tài liệu tham khảo: Find the node at center

Avatar photo

Clean Code: Nguyên tắc viết hàm trong lập trình…

Trong quá trình phát triển phần mềm, việc viết mã nguồn dễ đọc, dễ hiểu là yếu tố then chốt để đảm bảo code...
Avatar photo Dat Tran Thanh
3 min read

Leave a Reply

Your email address will not be published. Required fields are marked *