zoukankan      html  css  js  c++  java
  • 最近公共祖先 tarjan离线算法 C++

    最近做到一道题目,大概的意思就是求一个多叉树中两个节点的最近公共祖先,输入是用邻接矩阵表示的。

    要想理解tarjan算法并实现它,需要先理解一下内容:

    1) 深度优先搜索;tarjan算法核心思想:当某节点刚刚搜索完毕时,看与其相关的结点v是否已经被访问,如果v已经被访问过了,则它们的最近公共祖先就是v的祖先。

    2) 并查集原理和实现方法,并查集的代表和祖先的区别(其实也可以一起表示),祖先的更新时刻

    3) 如何表示多叉数(邻接链表,邻接矩阵),如何表示查询对,如何记录查询结果

    下面是c++实现代码,比较偷懒,每次调用函数就查询一个。查询对的数据结构可以用下面提到的邻接表来表示。

    #include <vector>
    #include <string>
    #include <algorithm>
    #include <iostream>
    
    using namespace std;
    
    
    class lca{
    public:
        lca(const vector<string> &vstr) 
            :str(vstr),f(vstr.size(),-1),ancestor(vstr.size(),-1),visit(vstr.size(),0) { }
        int lca_query(int vertex,int u, int v){
            static int ans=-1;
            ancestor[vertex] = vertex;
            visit[vertex] = true;
            for (int i = 0; i < str.size(); ++i){
                if ((str[vertex][i] == '1') && (visit[i] == 0)){
                    ans=lca_query(i, u, v);
                    unite(vertex, i);
                    ancestor[find(vertex)] = vertex;
                }
            }
            if (vertex == u&&visit[v])
                ans = ancestor[find(v)];
            if (vertex == v&&visit[u])
                ans = ancestor[find(u)];
            return ans;
        }
    private:
        const vector<string> &str;
        vector<int> f;  //并查集
        vector<int> ancestor;
        vector<bool> visit;
        int find(int i)
        {
            if (f[i] == -1)
                return i;
            return f[i] = find(f[i]);
        }
    
        void unite( int u, int v)
        {
            int x = find(u);
            int y = find(v);
            if (x != y)
                f[x] = y;
        }
    
    };
    
    
    
    int main(){
        vector<string> v123 = { "01100001", "10011000", "10000000", "01000000", "01000110","00001000","00000100","10000000" };
        lca query1(v123);
        cout << query1.lca_query(0,4, 7);
    
    }

    实际上用邻接矩阵来表示多叉数是很浪费时间的,单颗多叉树作为无环图,只有(n-1)条边,这里n是节点数。网上有很好的方法来表示邻接链表,

    struct Edge
    {
        int to, next;
    }edge[maxn * 2];
    int head[maxn], tot;
    void addedge(int u, int v)//邻接表头插法加边
    {
        edge[tot].to = v;
        edge[tot].next = head[u];
        head[u] = tot++;
    }

    本质上就是一个单链表,不过这个单链表的next指针只是一个数组下标值。head[u]记录的是上一次从u出发的边的数组下边。

    要理解tarjan算法,得先理解并查集

  • 相关阅读:
    JS控制文本框输入的内容
    JavaScript属性
    无缝滚动
    CCS实例,网页栏目
    CSS特效
    HTML技巧
    如何解决问题
    判断输入的字符是不是数字
    删除txt文件内容
    能组成几个单词
  • 原文地址:https://www.cnblogs.com/Nastukashii/p/4457713.html
Copyright © 2011-2022 走看看