【并查集】LeetCode - 1361. 验证二叉树

题目描述

题目链接
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解法1:并查集

  • 容易想到, 对于一棵树关键在于图中结点不能成环, 而对于连通性的判断, 很容易想到使用并查集
  • 对于并查集, 先判断当前结点与其左右子结点的连通性, 若之前已经连通, 那么本次操作就会成环; 若不连通则进行连通操作
  • 当所有结点操作完后, 在判断当前并查集的连通域数量,若为1, 则证明所有结点连通,即是一颗树
  • 二叉是肯定二叉的,因为题目输入就直接是左右子树
class Solution {
    //并查集
    class UnionFind{
        int *father;
        int cnt;
    public:
        UnionFind(int n)
        {
            father = new int[n];
            cnt = n;
            for(int i = 0; i < n; i++)
             father[i] = i;//初始化
        }
        int getFather(int x)
        {
            //路径压缩
            if(father[x] != x) father[x] = getFather(father[x]);
            return father[x];
        }
        void Union(int v1, int v2)//连通父子结点,v1为父,v2为子
        {
            int f1 = getFather(v1),f2 = getFather(v2);
            if(f1 == f2) return;
            father[v2] = v1;//v2连接到v1上
        }
        //判断是否连通
        bool Is_connected(int v1, int v2)
        {
        	return getFather(v1) == getFather(v2);
		}
        //计算并查集中集合的数量
        int countRoot() 
        {
            int num = 0;
            for(int i = 0; i < cnt; i++)
            if(father[i] == i)
             num++;
            return num;
        }
        ~UnionFind(){delete[] father;}
    };
public:
    bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
        UnionFind uf(n);
        for(int i = 0; i < n; i++)
        {
            if(leftChild[i] != -1)
            {
                //若之前已经连通则会成环返回false
                if(uf.Is_connected(i, leftChild[i])) return false;
                uf.Union(i, leftChild[i]);
            }
            if(rightChild[i] != -1)
            {
                if(uf.Is_connected(i, rightChild[i])) return false;
                uf.Union(i, rightChild[i]);
            }
        }
        if(uf.countRoot() == 1)
         return true;
        return false;
    }
};

封装的思想是我借鉴的,值得学习

解法2:找到根结点 + DFS判断连通性

  • 已知树的性质:除了根结点的入度是0外,其余每个结点的入度必须是1才可。也就是说入度为0的只能有一个,入度为2的不能有
  • 但仅仅这样还不行

在这里插入图片描述
满足入度为0的只能有一个,入度为2的没有,但它不是树。所以还需要DFS(或者BFS、先中后序遍历、层序遍历都行)

class Solution {
public:
    bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
        //除了根结点的入度是0外,其余每个结点的入度必须是1才可
        //也就是入度为0的只能有一个,入度为2的不能有
        vector<int> in(n);
        for(int i=0; i<n; i++)
        {
            if(leftChild[i] != -1) in[leftChild[i]]++;
            if(rightChild[i] != -1) in[rightChild[i]]++;
        }
        int cnt0 = 0,cnt2 = 0, root = -1;
        for(int i=0; i<n; i++)
        {
            if(in[i] == 0) {cnt0++; root = i;}
            if(in[i] > 1) cnt2++;
        }
        if(!(cnt0 == 1 && cnt2 == 0))
         return false;
        //DFS判断连通性
        vector<int> visited(n);
        DFS(visited, leftChild, rightChild, root);
        for(int i : visited)
         if(!i)
          return false;
        return true;
    }
    void DFS(vector<int>& visited, vector<int>& leftChild, vector<int>& rightChild, int v)
    {
        if(v==-1) return;
        visited[v]=1;
        DFS(visited, leftChild, rightChild, leftChild[v]);
        DFS(visited, leftChild, rightChild, rightChild[v]);
    }
};

另外,再附上先序遍历的代码

class Solution {
public:
    bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
        //除了根结点的入度是0外,其余每个结点的入度必须是1才可
        //也就是入度为0的只能有一个,入度为2的不能有
        vector<int> in(n);
        for(int i=0; i<n; i++)
        {
            if(leftChild[i] != -1) in[leftChild[i]]++;
            if(rightChild[i] != -1) in[rightChild[i]]++;
        }
        int cnt0 = 0,cnt2 = 0, root = -1;
        for(int i=0; i<n; i++)
        {
            if(in[i] == 0) {cnt0++; root = i;}
            if(in[i] > 1) cnt2++;
        }
        if(!(cnt0 == 1 && cnt2 == 0))
         return false;
        //先序遍历判断连通性
        stack<int> s;//辅助栈
        s.push(root);
        while(n && !s.empty())
        {
            root = s.top();
            s.pop();
            if(rightChild[root] != -1)
             s.push(rightChild[root]);
            if(leftChild[root] != -1)
             s.push(leftChild[root]);
            n--;
        }
        //若搜索次数少于n次或搜索n次后辅助栈还存在节点说明不是一颗二叉树 
        if (n != 0 || !s.empty())   
            return false;        
        return true;
    }
};

最后发现,竟然是先序遍历的运行时间最短,难道是因为前两种有迭代?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值