📌  相关文章
📜  教资会网络 | UGC-NET CS 2017 年 12 月 2 日 |问题 5(1)

📅  最后修改于: 2023-12-03 15:10:16.761000             🧑  作者: Mango

UGC-NET CS 2017 年 12 月 2 日 - 问题 5

简介

UGC-NET CS 2017 年 12 月 2 日 - 问题 5 是一道涉及二叉树的编程问题,需要程序员实现以下两个功能:

  1. 对于给定的两个数 node1 和 node2,找出它们的 LCA(最近公共祖先)。
  2. 对于给定的一个树节点 node 和一个整数 k,找出树中所有距离该节点 k 个距离的节点的个数。

题目要求使用 C++ 或 Java 编写代码。

测试数据

测试数据的格式如下:

第一行包括一个整数 T,表示测试用例的数量。

对于每个测试用例,第一行包括一个整数 N,表示树中节点的数量。

接下来的 N - 1 行给出了树中的边。

最后一行包括两个整数,表示要查询的节点。

解答
构建二叉树

首先需要定义一个树节点的数据结构,例如:

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

接下来,可以按照给定的节点信息构建二叉树。对于每条边 (u,v),把 u 作为 v 的一个子节点。构建完整个二叉树后,将根节点返回。

#define MAXN 100005
vector<int> adj[MAXN];

TreeNode* buildTree(vector<int>& values) {
  unordered_map<int, TreeNode*> nodes;
  for (int i = 0; i < values.size(); i++) {
    int val = values[i];
    nodes[i] = new TreeNode(val);
  }
  for (int i = 0; i < values.size() - 1; i++) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
    adj[v].push_back(u);
  }
  bool visited[MAXN] = { false };
  queue<int> Q;
  Q.push(0);
  visited[0] = true;
  while (!Q.empty()) {
    int u = Q.front();
    Q.pop();
    for (int i = 0; i < adj[u].size(); i++) {
      int v = adj[u][i];
      if (!visited[v]) {
        visited[v] = true;
        if (!nodes[u]->left) {
          nodes[u]->left = nodes[v];
        } else {
          nodes[u]->right = nodes[v];
        }
        Q.push(v);
      }
    }
  }
  return nodes[0];
}

此处使用了 STL 中的 unordered_mapqueue 数据结构,需要添加以下头文件:

#include <unordered_map>
#include <queue>
找出两个节点的 LCA

LCA 是指两个节点在二叉树中的最近公共祖先。首先需要找到两个节点所在的路径(从根节点到该节点的所有节点组成的链),并将它们转换为链表。然后,问题就变为了求两个链表的最后一个公共节点。

bool getPath(TreeNode* root, int target, vector<TreeNode*>& path) {
  if (!root) return false;
  path.push_back(root);
  if (root->val == target)
    return true;
  if (getPath(root->left, target, path) || getPath(root->right, target, path))
    return true;
  path.pop_back();
  return false;
}

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
  vector<TreeNode*> path1;
  vector<TreeNode*> path2;
  getPath(root, p->val, path1);
  getPath(root, q->val, path2);
  int i = 0;
  while (i < path1.size() && i < path2.size() && path1[i] == path2[i])
    i++;
  return path1[i - 1];
}
找出所有距离给定节点 k 个距离的节点的数量

假设要从根节点开始搜索,对于当前节点,需要分别计算以下三种情况:

  1. 该节点距离给定节点 k 个距离。
  2. 该节点的一个子节点距离给定节点 k 个距离,而它自己距离给定节点小于 k 个距离(即,如果向该节点的子节点继续搜索,距离会进一步减小)。
  3. 该节点的一个子节点距离给定节点大于 k 个距离(即,如果向该节点的子节点继续搜索,距离会进一步增大)。
int getDistanceToTarget(TreeNode* root, TreeNode* target, int k, int& ans) {
  if (!root) return -1;
  if (root == target) {
    findNodes(root, k, ans);
    return 0;
  }
  int left = getDistanceToTarget(root->left, target, k, ans);
  int right = getDistanceToTarget(root->right, target, k, ans);
  if (left != -1) {
    if (left + 1 == k) {
      ans++;
    } else {
      findNodes(root->right, k - left - 2, ans);
    }
    return left + 1;
  }
  if (right != -1) {
    if (right + 1 == k) {
      ans++;
    } else {
      findNodes(root->left, k - right - 2, ans);
    }
    return right + 1;
  }
  return -1;
}

void findNodes(TreeNode* root, int k, int& ans) {
  if (!root || k < 0) return;
  if (k == 0) {
    ans++;
    return;
  }
  findNodes(root->left, k - 1, ans);
  findNodes(root->right, k - 1, ans);
}
完整代码
#include <iostream>
#include <vector>
#include <unordered_map>
#include <queue>

using namespace std;

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

#define MAXN 100005
vector<int> adj[MAXN];

TreeNode* buildTree(vector<int>& values) {
  unordered_map<int, TreeNode*> nodes;
  for (int i = 0; i < values.size(); i++) {
    int val = values[i];
    nodes[i] = new TreeNode(val);
  }
  for (int i = 0; i < values.size() - 1; i++) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
    adj[v].push_back(u);
  }
  bool visited[MAXN] = { false };
  queue<int> Q;
  Q.push(0);
  visited[0] = true;
  while (!Q.empty()) {
    int u = Q.front();
    Q.pop();
    for (int i = 0; i < adj[u].size(); i++) {
      int v = adj[u][i];
      if (!visited[v]) {
        visited[v] = true;
        if (!nodes[u]->left) {
          nodes[u]->left = nodes[v];
        } else {
          nodes[u]->right = nodes[v];
        }
        Q.push(v);
      }
    }
  }
  return nodes[0];
}

bool getPath(TreeNode* root, int target, vector<TreeNode*>& path) {
  if (!root) return false;
  path.push_back(root);
  if (root->val == target)
    return true;
  if (getPath(root->left, target, path) || getPath(root->right, target, path))
    return true;
  path.pop_back();
  return false;
}

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
  vector<TreeNode*> path1;
  vector<TreeNode*> path2;
  getPath(root, p->val, path1);
  getPath(root, q->val, path2);
  int i = 0;
  while (i < path1.size() && i < path2.size() && path1[i] == path2[i])
    i++;
  return path1[i - 1];
}

void findNodes(TreeNode* root, int k, int& ans) {
  if (!root || k < 0) return;
  if (k == 0) {
    ans++;
    return;
  }
  findNodes(root->left, k - 1, ans);
  findNodes(root->right, k - 1, ans);
}

int getDistanceToTarget(TreeNode* root, TreeNode* target, int k, int& ans) {
  if (!root) return -1;
  if (root == target) {
    findNodes(root, k, ans);
    return 0;
  }
  int left = getDistanceToTarget(root->left, target, k, ans);
  int right = getDistanceToTarget(root->right, target, k, ans);
  if (left != -1) {
    if (left + 1 == k) {
      ans++;
    } else {
      findNodes(root->right, k - left - 2, ans);
    }
    return left + 1;
  }
  if (right != -1) {
    if (right + 1 == k) {
      ans++;
    } else {
      findNodes(root->left, k - right - 2, ans);
    }
    return right + 1;
  }
  return -1;
}


int main() {
  int T;
  cin >> T;
  while (T--) {
    for (int i = 0; i < MAXN; i++) {
      adj[i].clear();
    }
    int n;
    cin >> n;
    vector<int> values(n);
    for (int i = 0; i < n; i++) {
      cin >> values[i];
    }
    TreeNode* root = buildTree(values);
    int node1, node2;
    cin >> node1 >> node2;
    TreeNode* p = new TreeNode(node1);
    TreeNode* q = new TreeNode(node2);
    TreeNode* lca = lowestCommonAncestor(root, p, q);
    int k;
    cin >> k;
    int ans = 0;
    getDistanceToTarget(root, lca, k, ans);
    getDistanceToTarget(root, p, k, ans);
    getDistanceToTarget(root, q, k, ans);
    cout << ans << endl;
  }
  return 0;
}
结语

以上是本人对 UGC-NET CS 2017 年 12 月 2 日问题 5 的解答。对于涉及二叉树的问题,建议程序员先考虑如何构建二叉树、如何找到一个节点的父节点以及如何找到一个节点所在的路径,并将其转换为链表,然后再解决具体问题,这样可以避免重复计算和错误的结果。