dfs力扣1993树上的操作

dfs力扣1993树上的操作

题目

题目链接

给你一棵 n 个节点的树,编号从 0n - 1 ,以父节点数组 parent 的形式给出,其中 parent[i] 是第 i 个节点的父节点。树的根节点为 0 号节点,所以 parent[0] = -1 ,因为它没有父节点。你想要设计一个数据结构实现树里面对节点的加锁,解锁和升级操作。

数据结构需要支持如下函数:

  • **Lock:**指定用户给指定节点 上锁 ,上锁后其他用户将无法给同一节点上锁。只有当节点处于未上锁的状态下,才能进行上锁操作。

  • **Unlock:**指定用户给指定节点 解锁 ,只有当指定节点当前正被指定用户锁住时,才能执行该解锁操作。

  • Upgrade:指定用户给指定节点 上锁 ,并且将该节点的所有子孙节点 解锁 。只有如下 3 个条件

    全部满足时才能执行升级操作:

    • 指定节点当前状态为未上锁。
    • 指定节点至少有一个上锁状态的子孙节点(可以是 任意 用户上锁的)。
    • 指定节点没有任何上锁的祖先节点。

请你实现 LockingTree 类:

  • LockingTree(int[] parent) 用父节点数组初始化数据结构。
  • lock(int num, int user) 如果 id 为 user 的用户可以给节点 num 上锁,那么返回 true ,否则返回 false 。如果可以执行此操作,节点 num 会被 id 为 user 的用户 上锁
  • unlock(int num, int user) 如果 id 为 user 的用户可以给节点 num 解锁,那么返回 true ,否则返回 false 。如果可以执行此操作,节点 num 变为 未上锁 状态。
  • upgrade(int num, int user) 如果 id 为 user 的用户可以给节点 num 升级,那么返回 true ,否则返回 false 。如果可以执行此操作,节点 num 会被 升级
示例

示例图片

输入:
[“LockingTree”, “lock”, “unlock”, “unlock”, “lock”, “upgrade”, “lock”]
[[[-1, 0, 0, 1, 1, 2, 2]], [2, 2], [2, 3], [2, 2], [4, 5], [0, 1], [0, 1]]
输出:
[null, true, false, true, true, true, false]

解释:
LockingTree lockingTree = new LockingTree([-1, 0, 0, 1, 1, 2, 2]);
lockingTree.lock(2, 2); // 返回 true ,因为节点 2 未上锁。
// 节点 2 被用户 2 上锁。
lockingTree.unlock(2, 3); // 返回 false ,因为用户 3 无法解锁被用户 2 上锁的节点。
lockingTree.unlock(2, 2); // 返回 true ,因为节点 2 之前被用户 2 上锁。
// 节点 2 现在变为未上锁状态。
lockingTree.lock(4, 5); // 返回 true ,因为节点 4 未上锁。
// 节点 4 被用户 5 上锁。
lockingTree.upgrade(0, 1); // 返回 true ,因为节点 0 未上锁且至少有一个被上锁的子孙节点(节点 4)。
// 节点 0 被用户 1 上锁,节点 4 变为未上锁。
lockingTree.lock(0, 1); // 返回 false ,因为节点 0 已经被上锁了。

提示
  • n == parent.length
  • 2 <= n <= 2000
  • 对于 i != 0 ,满足 0 <= parent[i] <= n - 1
  • parent[0] == -1
  • 0 <= num <= n - 1
  • 1 <= user <= 104
  • parent 表示一棵合法的树。
  • lockunlockupgrade 的调用 总共 不超过 2000 次。
做题历程

​ 这个题乍一看看不懂啥意思,但是你仔细看看,就会发现确实看不懂。最近做题老是看不懂题意,这就导致我最近写算发的第一道难关就是弄明白题目到底要我做什么。不夸张的说,这个题我前前后后看了有五六遍,才大致明白了他要我干什么,但是我还是没完全明白,遗漏了许多小细节,导致我之后在错误的道路上一路狂奔,不过还好没偏离大致方向。因为这道题错的实在是太多,自己给自己挖的坑也实在是不少,所以就想把这道题记录一下,也算是没白踩这么多坑。先说一下我在做这道题的时候遇到的坑。

  • 第一个就是在看完这道题之后,结合题目给出的案例,让我自以为这道题给出的parent数组都是跟示例一样,排列好的,这给了我第一个误差,让我以为知道了父结点就可以根据规律求出他的子节点。
  • 第二个就是我自以为题目中的树就是示例给出的二叉树,一个父结点只有两个子节点,但是题目上没有任何字眼说明这个树就是二叉树。当我意识道这个的时候我就知道,我的代码白写了。
做题思路

​ 虽然自己给自己挖的坑不少,单也是在对树的结构上的问题,整体大的方向还是对的,也就是我的思路没有太大的问题。

数组定义

​ 首先题目给出一个parent数组,里面记录了每一个节点的父结点是谁,根据这个数组,我们可以从一个节点向上搜寻,一直找到树的根节点,可以用来解决第三个方法锁升级的向上搜寻。

​ 然后就是题目要求要记录是谁给节点加的锁,这个需要额外开一个数组来记录每个节点的锁持有者信息,因为题目明确表明加锁的用户数据范围是1 <= user <= 104,所以锁数组不需要进行初始化,默认的0值就可以标识此节点目前没有用户加锁。

​ 以上都是我一开始的思路,都是正确的,关于子节点的搜寻的思路,我一开始的方向是错的,我一开始以为可以根据规律求出子节点,就没有开辟额外的数组空间来记录子节点,之后发现不行,就开辟了一个二维数组来存储子节点。但是每个父结点的子节点数量不是固定的,第二次关于子节点的思路又错了。然后我又改成ArrayList<ArrayList> 类型,但是由于这个时候我的脑子已经一团糊了,这个又比较麻烦,老有错误,之后就实在没办法了,翻了一下题解,有一个题解是这样定义子存储子节点的结构的,他使用了一个ArrayList[]类型,觉得很对,就按照这个定义了。

    private int[] parent;
    private ArrayList<Integer>[] children;
    private int[] locak;
    private int len;
    public LockingTree(int[] parent) {
        this.parent = parent;
        this.len = parent.length;
        locak = new int[len];
        this.children = new ArrayList[len];
        Arrays.setAll(children, i -> new ArrayList<>());
        for (int i = 1; i < len; i++) {
            children[parent[i]].add(i);
        }
    }
编写代码

locak方法:加锁方法比较简单,传入需要加锁的节点和加锁的用户,如果这个节点当前属于未加锁的状态,则,用户加锁成功,返回true,若当前节点已加锁,则加锁失败。

    public boolean lock(int num, int user) {
        if(locak[num] == 0){
            locak[num] = user;
            return true;
        }
        return false;
    }

unlocak方法:解锁方法也不复杂,传入需要解锁的节点,查看当前节点上锁的用户与解锁的用户是否为同一人,若是则解锁成功,不是则解锁失败。

    public boolean unlock(int num, int user) {
        if(locak[num] == user){
            locak[num] = 0;
            return true;
        }
        return false;
    }

upgrade方法:升级锁方法比较麻烦,需要满足三个条件,当前节点和父结点(一直到根节点为止)不能有加锁的节点,并且所有子节点中至少需要有一个加锁的节点。升级成功需要将当前节点变为加锁状态,将加锁的子节点解锁。

因为我的思路不对,所以我折腾了大半晌也没弄出来,看了一个使用了dfs的题解,我一开始的思路是使用队列一个一个遍历子节点,不过还是深度优先跟简便一些。

public boolean upgrade(int num, int user) {
        int x = num;
        while (x != -1) {
            if (locak[x] != 0) {
                return false;
            }
            x = parent[x];
        }
        boolean[] find = new boolean[1];
        dfs(num, find);
        if(find[0]){
            locak[num]=user;
        }
        return find[0];
    }
    void dfs(int num,boolean[] find){
        for(int a : children[num]){
            if(locak[a] != 0){
                locak[a] = 0;
                find[0] = true;
            }
            dfs(a,find);
        }
    }
}
完整代码
class LockingTree {
    private int[] parent;
    private ArrayList<Integer>[] children;
    private int[] locak;
    private int len;
    public LockingTree(int[] parent) {
        this.parent = parent;
        this.len = parent.length;
        locak = new int[len];
        this.children = new ArrayList[len];
        Arrays.setAll(children, i -> new ArrayList<>());
        for (int i = 1; i < len; i++) {
            children[parent[i]].add(i);
        }
    }

    public boolean lock(int num, int user) {
        if(locak[num] == 0){
            locak[num] = user;
            return true;
        }
        return false;
    }

    public boolean unlock(int num, int user) {
        if(locak[num] == user){
            locak[num] = 0;
            return true;
        }
        return false;
    }

    public boolean upgrade(int num, int user) {
        int x = num;
        while (x != -1) {
            if (locak[x] != 0) {
                return false;
            }
            x = parent[x];
        }
        boolean[] find = new boolean[1];
        dfs(num, find);
        if(find[0]){
            locak[num]=user;
        }
        return find[0];
    }
    void dfs(int num,boolean[] find){
        for(int a : children[num]){
            if(locak[a] != 0){
                locak[a] = 0;
                find[0] = true;
            }
            dfs(a,find);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值