📜  二叉树中最低的共同祖先组合3(使用RMQ)

📅  最后修改于: 2021-04-16 08:31:02             🧑  作者: Mango

给定一棵有根的树,以及树中的两个节点,找到两个节点的最低共同祖先。两个节点u和v的LCA定义为从作为祖先的根到u和v的最远节点。
先决条件:LCA |设置1

lca

上图的示例:

Input : 4 5
Output : 2

Input : 4 7
Output : 1

将LCA转换为RMQ(范围最小查询):
取一个名为E []的数组,该数组存储dfs遍历的顺序,即在dfs遍历期间覆盖节点的顺序。例如,

上面给出的树具有以下顺序的dfs遍历:1-2-4-2-5-2-1-3。
取另一个数组L [],其中L [i]是节点E [i]的级别。
还有数组H [],它存储数组E []中第i个节点的第一个匹配项的索引。
所以,对于上面的树,
E [] = {1,2,4,2,5,2,1,3}
L [] = {1、2、3、2、3、2、1、2}
H [] = {0,1,7,2,4}
请注意,数组E和L具有基于1的索引,但是数组H具有基于0的索引。
现在,要查找LCA(4,3),首先,使用数组H并找到在E中找到4和3的索引,即H [4]和H [3]。因此,索引的值为2和7。现在,查看子数组L [2:7],并在此子数组中找到最小值1(在第6个索引处),并找到数组E中的相应元素。即E [6]是LCA(4,3)。
要了解为什么这样做,请再次采用LCA(4,3)。子数组E [2:7]是一个可以从节点4到达节点3的路径。并且,如果在该路径中存在最低级别的节点,则可以简单地将其声明为LCA(4,3)。
现在,问题是要在子数组E [H [u]….H [v]]中找到最小值(假设H [u]> = H [v])。并且,可以使用段树或稀疏表来完成。下面是使用段树的代码。

C++
// CPP code to find LCA of given
// two nodes in a tree
#include 
 
#define sz(x) x.size()
#define pb push_back
#define left 2 * i + 1
#define right 2 * i + 2
using namespace std;
 
const int maxn = 100005;
 
// the graph
vector> g(maxn);
 
// level of each node
int level[maxn];
 
vector e;
vector l;
int h[maxn];
 
// the segment tree
int st[5 * maxn];
 
// adding edges to the graph(tree)
void add_edge(int u, int v) {
  g[u].pb(v);
  g[v].pb(u);
}
 
// assigning level to nodes
void leveling(int src) {
  for (int i = 0; i < sz(g[src]); i++) {
    int des = g[src][i];
    if (!level[des]) {
      level[des] = level[src] + 1;
      leveling(des);
    }
  }
}
 
bool visited[maxn];
 
// storing the dfs traversal
// in the array e
void dfs(int src) {
  e.pb(src);
  visited[src] = 1;
  for (int i = 0; i < sz(g[src]); i++) {
    int des = g[src][i];
    if (!visited[des]) {
      dfs(des);
      e.pb(src);
    }
  }
}
 
// making the array l
void setting_l(int n) {
  for (int i = 0; i < sz(e); i++)
    l.pb(level[e[i]]);
}
 
// making the array h
void setting_h(int n) {
  for (int i = 0; i <= n; i++)
    h[i] = -1;
  for (int i = 0; i < sz(e); i++) {
    // if is already stored
    if (h[e[i]] == -1)
      h[e[i]] = i;
  }
}
 
// Range minimum query to return the index
// of minimum in the subarray L[qs:qe]
int RMQ(int ss, int se, int qs, int qe, int i) {
  if (ss > se)
    return -1;
 
  // out of range
  if (se < qs || qe < ss)
    return -1;
 
  // in the range
  if (qs <= ss && se <= qe)
    return st[i];
 
  int mid = (ss + se) >> 1;
  int st = RMQ(ss, mid, qs, qe, left);
  int en = RMQ(mid + 1, se, qs, qe, right);
 
  if (st != -1 && en != -1) {
    if (l[st] < l[en])
      return st;
    return en;
  } else if (st != -1)
    return st;
  else if (en != -1)
    return en;
}
 
// constructs the segment tree
void SegmentTreeConstruction(int ss, int se, int i) {
  if (ss > se)
    return;
  if (ss == se) // leaf
  {
    st[i] = ss;
    return;
  }
  int mid = (ss + se) >> 1;
 
  SegmentTreeConstruction(ss, mid, left);
  SegmentTreeConstruction(mid + 1, se, right);
 
  if (l[st[left]] < l[st[right]])
    st[i] = st[left];
  else
    st[i] = st[right];
}
 
// Funtion to get LCA
int LCA(int x, int y) {
  if (h[x] > h[y])
    swap(x, y);
  return e[RMQ(0, sz(l) - 1, h[x], h[y], 0)];
}
 
// Driver code
int main() {
  ios::sync_with_stdio(0);
 
  // n=number of nodes in the tree
  // q=number of queries to answer
  int n = 15, q = 5;
 
  // making the tree
  /*
                   1
                 / | \
                2  3  4
                   |   \
                   5    6
                 / |  \
               8   7    9 (right of 5)
                 / | \   | \
               10 11 12 13 14
                      |
                      15
  */
  add_edge(1, 2);
  add_edge(1, 3);
  add_edge(1, 4);
  add_edge(3, 5);
  add_edge(4, 6);
  add_edge(5, 7);
  add_edge(5, 8);
  add_edge(5, 9);
  add_edge(7, 10);
  add_edge(7, 11);
  add_edge(7, 12);
  add_edge(9, 13);
  add_edge(9, 14);
  add_edge(12, 15);
 
  level[1] = 1;
  leveling(1);
 
  dfs(1);
 
  setting_l(n);
 
  setting_h(n);
 
  SegmentTreeConstruction(0, sz(l) - 1, 0);
 
  cout << LCA(10, 15) << endl;
  cout << LCA(11, 14) << endl;
 
  return 0;
}


Java
// JAVA code to find LCA of given
// two nodes in a tree
import java.util.*;
public class GFG
{
 
  static int maxn = 100005;
  static  int left(int i)
  {
    return  (2 * i + 1);
  }
  static  int right(int i) { return 2 * i + 2;}
 
  // the graph
  static Vector []g = new Vector[maxn];
 
  // level of each node
  static int []level = new int[maxn];
  static Vector e = new Vector<>();
  static Vector l= new Vector<>();
  static int []h = new int[maxn];
 
  // the segment tree
  static int []st = new int[5 * maxn];
 
  // adding edges to the graph(tree)
  static void add_edge(int u, int v)
  {
    g[u].add(v);
    g[v].add(u);
  }
 
  // assigning level to nodes
  static void leveling(int src)
  {
    for (int i = 0; i < (g[src].size()); i++)
    {
      int des = g[src].get(i);
      if (level[des] != 0)
      {
        level[des] = level[src] + 1;
        leveling(des);
      }
    }
  }
 
  static boolean []visited = new boolean[maxn];
 
  // storing the dfs traversal
  // in the array e
  static void dfs(int src)
  {
    e.add(src);
    visited[src] = true;
    for (int i = 0; i < (g[src]).size(); i++)
    {
      int des = g[src].get(i);
      if (!visited[des])
      {
        dfs(des);
        e.add(src);
      }
    }
  }
 
  // making the array l
  static void setting_l(int n)
  {
    for (int i = 0; i < e.size(); i++)
      l.add(level[e.get(i)]);
  }
 
  // making the array h
  static void setting_h(int n)
  {
    for (int i = 0; i <= n; i++)
      h[i] = -1;
    for (int i = 0; i < e.size(); i++)
    {
 
      // if is already stored
      if (h[e.get(i)] == -1)
        h[e.get(i)] = i;
    }
  }
 
  // Range minimum query to return the index
  // of minimum in the subarray L[qs:qe]
  static int RMQ(int ss, int se, int qs, int qe, int i)
  {
    if (ss > se)
      return -1;
 
    // out of range
    if (se < qs || qe < ss)
      return -1;
 
    // in the range
    if (qs <= ss && se <= qe)
      return st[i];
 
    int mid = (ss + se)/2 ;
    int st = RMQ(ss, mid, qs, qe, left(i));
    int en = RMQ(mid + 1, se, qs, qe, right(i));
 
    if (st != -1 && en != -1)
    {
      if (l.get(st) < l.get(en))
        return st;
      return en;
    } else if (st != -1)
      return st-2;
    else if (en != -1)
      return en-1;
    return 0;
  }
 
  // constructs the segment tree
  static void SegmentTreeConstruction(int ss,
                                      int se, int i)
  {
    if (ss > se)
      return;
    if (ss == se) // leaf
    {
      st[i] = ss;
      return;
    }
    int mid = (ss + se) /2;
 
    SegmentTreeConstruction(ss, mid, left(i));
    SegmentTreeConstruction(mid + 1, se, right(i));
    if (l.get(st[left(i)]) < l.get(st[right(i)]))
      st[i] = st[left(i)];
    else
      st[i] = st[right(i)];
  }
 
  // Funtion to get LCA
  static int LCA(int x, int y)
  {
    if (h[x] > h[y])
    {
      int t = x;
      x = y;
      y = t;
    }
    return e.get(RMQ(0, l.size() - 1, h[x], h[y], 0));
  }
 
  // Driver code
  public static void main(String[] args)
  {
 
    // n=number of nodes in the tree
    // q=number of queries to answer
    int n = 15, q = 5;
    for (int i = 0; i < g.length; i++)
      g[i] = new Vector();
 
    // making the tree
    /*
                   1
                 / | \
                2  3  4
                   |   \
                   5    6
                 / |  \
               8   7    9 (right of 5)
                 / | \   | \
               10 11 12 13 14
                      |
                      15
  */
    add_edge(1, 2);
    add_edge(1, 3);
    add_edge(1, 4);
    add_edge(3, 5);
    add_edge(4, 6);
    add_edge(5, 7);
    add_edge(5, 8);
    add_edge(5, 9);
    add_edge(7, 10);
    add_edge(7, 11);
    add_edge(7, 12);
    add_edge(9, 13);
    add_edge(9, 14);
    add_edge(12, 15);
    level[1] = 1;
    leveling(1);
    dfs(1);
    setting_l(n);
    setting_h(n);
    SegmentTreeConstruction(0, l.size() - 1, 0);
    System.out.print(LCA(10, 15) +"\n");
    System.out.print(LCA(11, 14) +"\n");
  }
}
 
// This code is contributed by Rajput-Ji


Python3
# Python code to find LCA of given
# two nodes in a tree
 
maxn = 100005
 
# the graph
g = [[] for i in range(maxn)]
 
# level of each node
level = [0] * maxn
 
e = []
l = []
h = [0] * maxn
 
# the segment tree
st = [0] * (5 * maxn)
 
# adding edges to the graph(tree)
def add_edge(u: int, v: int):
    g[u].append(v)
    g[v].append(u)
 
# assigning level to nodes
def leveling(src: int):
    for i in range(len(g[src])):
        des = g[src][i]
        if not level[des]:
            level[des] = level[src] + 1
            leveling(des)
 
visited = [False] * maxn
 
# storing the dfs traversal
# in the array e
def dfs(src: int):
    e.append(src)
    visited[src] = True
    for i in range(len(g[src])):
        des = g[src][i]
        if not visited[des]:
            dfs(des)
            e.append(src)
 
# making the array l
def setting_l(n: int):
    for i in range(len(e)):
        l.append(level[e[i]])
 
# making the array h
def setting_h(n: int):
    for i in range(n + 1):
        h[i] = -1
    for i in range(len(e)):
 
        # if is already stored
        if h[e[i]] == -1:
            h[e[i]] = i
 
# Range minimum query to return the index
# of minimum in the subarray L[qs:qe]
def RMQ(ss: int, se: int, qs: int, qe: int, i: int) -> int:
    global st
    if ss > se:
        return -1
 
    # out of range
    if se < qs or qe < ss:
        return -1
 
    # in the range
    if qs <= ss and se <= qe:
        return st[i]
 
    mid = (se + ss) >> 1
    stt = RMQ(ss, mid, qs, qe, 2 * i + 1)
    en = RMQ(mid + 1, se, qs, qe, 2 * i + 2)
 
    if stt != -1 and en != -1:
        if l[stt] < l[en]:
            return stt
        return en
    elif stt != -1:
        return stt
    elif en != -1:
        return en
 
# constructs the segment tree
def segmentTreeConstruction(ss: int, se: int, i: int):
    if ss > se:
        return
    if ss == se: # leaf
        st[i] = ss
        return
 
    mid = (ss + se) >> 1
    segmentTreeConstruction(ss, mid, 2 * i + 1)
    segmentTreeConstruction(mid + 1, se, 2 * i + 2)
 
    if l[st[2 * i + 1]] < l[st[2 * i + 2]]:
        st[i] = st[2 * i + 1]
    else:
        st[i] = st[2 * i + 2]
 
# Funtion to get LCA
def LCA(x: int, y: int) -> int:
    if h[x] > h[y]:
        x, y = y, x
    return e[RMQ(0, len(l) - 1, h[x], h[y], 0)]
 
# Driver Code
if __name__ == "__main__":
 
    # n=number of nodes in the tree
    # q=number of queries to answer
    n = 15
    q = 5
 
    # making the tree
    # /*
    #         1
    #     / | \
    #     2 3 4
    #         | \
    #         5 6
    #     / | \
    #     8 7 9 (right of 5)
    #     / | \ | \
    #     10 11 12 13 14
    #             |
    #             15
    # */
    add_edge(1, 2)
    add_edge(1, 3)
    add_edge(1, 4)
    add_edge(3, 5)
    add_edge(4, 6)
    add_edge(5, 7)
    add_edge(5, 8)
    add_edge(5, 9)
    add_edge(7, 10)
    add_edge(7, 11)
    add_edge(7, 12)
    add_edge(9, 13)
    add_edge(9, 14)
    add_edge(12, 15)
 
    level[1] = 1
    leveling(1)
    dfs(1)
    setting_l(n)
    setting_h(n)
 
    segmentTreeConstruction(0, len(l) - 1, 0)
 
    print(LCA(10, 15))
    print(LCA(11, 14))
 
# This code is contributed by
# sanjeev2552


C#
// C# code to find LCA of given
// two nodes in a tree
using System;
using System.Collections.Generic;
public class GFG
{
  static int maxn = 100005;
  static  int left(int i)
  {
    return  (2 * i + 1);
  }
  static  int right(int i) { return 2 * i + 2;}
 
  // the graph
  static List []g = new List[maxn];
 
  // level of each node
  static int []level = new int[maxn];
  static List e = new List();
  static List l= new List();
  static int []h = new int[maxn];
 
  // the segment tree
  static int []st;
 
  // adding edges to the graph(tree)
  static void add_edge(int u, int v)
  {
    g[u].Add(v);
    g[v].Add(u);
  }
 
  // assigning level to nodes
  static void leveling(int src)
  {
    for (int i = 0; i < (g[src].Count); i++)
    {
      int des = g[src][i];
      if (level[des] != 0)
      {
        level[des] = level[src] + 1;
        leveling(des);
      }
    }
  }
  static bool []visited = new bool[maxn];
 
  // storing the dfs traversal
  // in the array e
  static void dfs(int src)
  {
    e.Add(src);
    visited[src] = true;
    for (int i = 0; i < (g[src]).Count; i++)
    {
      int des = g[src][i];
      if (!visited[des])
      {
        dfs(des);
        e.Add(src);
      }
    }
  }
 
  // making the array l
  static void setting_l(int n)
  {
    for (int i = 0; i < e.Count; i++)
      l.Add(level[e[i]]);
  }
 
  // making the array h
  static void setting_h(int n)
  {
    for (int i = 0; i <= n; i++)
      h[i] = -1;
    for (int i = 0; i < e.Count; i++)
    {
 
      // if is already stored
      if (h[e[i]] == -1)
        h[e[i]] = i;
    }
  }
 
  // Range minimum query to return the index
  // of minimum in the subarray L[qs:qe]
  static int RMQ(int ss, int se, int qs, int qe, int i)
  {
    if (ss > se)
      return -1;
 
    // out of range
    if (se < qs || qe < ss)
      return -1;
 
    // in the range
    if (qs <= ss && se <= qe)
      return st[i];
 
    int mid = (ss + se)/2 ;
    int sti = RMQ(ss, mid, qs, qe, left(i));
    int en = RMQ(mid + 1, se, qs, qe, right(i));
 
    if (sti != -1 && en != -1)
    {
      if (l[sti] < l[en])
        return sti;
      return en;
    } else if (sti != -1)
      return sti-2;
    else if (en != -1)
      return en-1;
    return 0;
  }
 
  // constructs the segment tree
  static void SegmentTreeConstruction(int ss,
                                      int se, int i)
  {
    if (ss > se)
      return;
    if (ss == se) // leaf
    {
      st[i] = ss;
      return;
    }
    int mid = (ss + se) /2;
 
    SegmentTreeConstruction(ss, mid, left(i));
    SegmentTreeConstruction(mid + 1, se, right(i));
    if (l[st[left(i)]] < l[st[right(i)]])
      st[i] = st[left(i)];
    else
      st[i] = st[right(i)];
  }
 
  // Funtion to get LCA
  static int LCA(int x, int y)
  {
    if (h[x] > h[y])
    {
      int t = x;
      x = y;
      y = t;
    }
    return e[RMQ(0, l.Count - 1, h[x], h[y], 0)];
  }
 
  // Driver code
  public static void Main(String[] args)
  {
    st = new int[5 * maxn];
     
    // n=number of nodes in the tree
    // q=number of queries to answer
    int n = 15;
    for (int i = 0; i < g.Length; i++)
      g[i] = new List();
 
    // making the tree
    /*
                   1
                 / | \
                2  3  4
                   |   \
                   5    6
                 / |  \
               8   7    9 (right of 5)
                 / | \   | \
               10 11 12 13 14
                      |
                      15
  */
    add_edge(1, 2);
    add_edge(1, 3);
    add_edge(1, 4);
    add_edge(3, 5);
    add_edge(4, 6);
    add_edge(5, 7);
    add_edge(5, 8);
    add_edge(5, 9);
    add_edge(7, 10);
    add_edge(7, 11);
    add_edge(7, 12);
    add_edge(9, 13);
    add_edge(9, 14);
    add_edge(12, 15);
    level[1] = 1;
    leveling(1);
    dfs(1);
    setting_l(n);
    setting_h(n);
    SegmentTreeConstruction(0, l.Count - 1, 0);
    Console.Write(LCA(10, 15) +"\n");
    Console.Write(LCA(11, 14) +"\n");
  }
}
 
// This code is contributed by gauravrajput1


输出:
7
5

时间复杂度:
定义的数组存储在O(n)中。段树的构造也需要O(n)时间。 LCA函数调用函数RMQ,该函数每个查询占用O(logn)(因为它使用段树)。因此,总体时间复杂度为O(n + q * logn)