LeetCode 1993. 树上的操作:大模拟

news2024/11/25 4:33:00

【LetMeFly】1993.树上的操作:大模拟

力扣题目链接:https://leetcode.cn/problems/operations-on-tree/

给你一棵 n 个节点的树,编号从 0 到 n - 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 会被 升级

 

示例 1:

输入:
["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 表示一棵合法的树。
  • lock ,unlock 和 upgrade 的调用 总共 不超过 2000 次。

方法一:大模拟

使用三个数组:

  • parent[i]表示i的parent
  • child[i]表示i的孩子们
  • lockUser[i]表示i的上锁者(0表示未上锁)

初始化:记录每个节点的child。

上锁:看num是否已经被锁,若无则上锁并返回True,否则直接返回False

解锁:看num的上锁者是否恰好为user,若是则解锁并返回True,否则直接返回False

更新:写两个函数,hasLockedParent(num)用来判断num的祖先节点中是否有锁、hasLockedChildAndUnlock(num)用来判断num的孩子节点中是否有锁(若有锁,则顺便解锁)。如果“num无锁”且“其祖先节点无锁”且“其后代节点中有锁”,则上锁该节点并返回True(判断后代节点是否有锁时若有锁则已经顺便解锁了)。

对于函数hasLockedParent(num),其实只需要在num不为-1时不断将num赋值为parent[num],若某次lockUser[num]不为0则返回False

对于函数hasLockedChildAndUnlock(num),为什么能做到“后代节点有锁的话顺便解锁”呢?因为只要后代中存在锁,函数就一定返回True,早晚就一定要解锁这个带锁的后代。这就是为什么先判断num无锁其祖先节点无锁后再判断其后代节点中有锁

  • 时间复杂度:初始化 O ( l e n ( p a r e n t ) ) O(len(parent)) O(len(parent)),单次Lock、Unlock操作 O ( 1 ) O(1) O(1),单次Upgrade操作 O ( l e n ( p a r e n t s ) ) O(len(parents)) O(len(parents))
  • 空间复杂度 O ( l e n ( p a r e n t s ) ) O(len(parents)) O(len(parents))

AC代码

C++
class LockingTree {
private:
    vector<int> lockUser;  // 谁锁的这个节点(0表示未锁)
    vector<int> parent;
    vector<vector<int>> child;

    bool hasLockedParent(int num) {
        while (parent[num] != -1) {
            num = parent[num];
            if (lockUser[num]) {
                return true;
            }
        }
        return false;
    }

    bool hasLockedChildAndUnlock(int num) {
        bool hasLockedChild = false;
        if (lockUser[num]) {
            lockUser[num] = 0;
            hasLockedChild = true;
        }
        for (int thisChild : child[num]) {
            hasLockedChild |= hasLockedChildAndUnlock(thisChild);
        }
        return hasLockedChild;
    }
    
public:
    LockingTree(vector<int>& parent): parent(parent) {
        lockUser = vector<int>(parent.size());
        child = vector<vector<int>>(parent.size());
        for (int i = 1; i < parent.size(); i++) {
            child[parent[i]].push_back(i);
        }
    }
    
    bool lock(int num, int user) {
        if (lockUser[num]) {
            return false;
        }
        lockUser[num] = user;
        return true;
    }
    
    bool unlock(int num, int user) {
        if (lockUser[num] == user) {
            lockUser[num] = 0;
            return true;
        }
        return false;
    }
    
    bool upgrade(int num, int user) {
        if (!lockUser[num] && !hasLockedParent(num) && hasLockedChildAndUnlock(num)) {
            lockUser[num] = user;
            return true;
        }
        return false;
    }
};
Python
# from typing import List

class LockingTree:

    def __init__(self, parent: List[int]):
        self.parent = parent
        self.lockUser = [0] * len(parent)
        self.child = [[] for _ in range(len(parent))]
        for i in range(1, len(parent)):
            self.child[parent[i]].append(i)


    def lock(self, num: int, user: int) -> bool:
        if self.lockUser[num]:
            return False
        self.lockUser[num] = user
        return True


    def unlock(self, num: int, user: int) -> bool:
        if self.lockUser[num] == user:
            self.lockUser[num] = 0
            return True
        return False


    def upgrade(self, num: int, user: int) -> bool:
        if not self.lockUser[num] and not self.__hasLockedParent__(num) and self.__hasLockedChildAndUnlock__(num):
            self.lockUser[num] = user
            return True
        return False
    

    def __hasLockedParent__(self, num: int) -> bool:
        while self.parent[num] != -1:
            num = self.parent[num]
            if self.lockUser[num]:
                return True
        return False
    

    def __hasLockedChildAndUnlock__(self, num: int) -> bool:
        hasLockedChild = False
        if self.lockUser[num]:
            self.lockUser[num] = 0
            hasLockedChild = True
        for thisChild in self.child[num]:
            hasLockedChild |= self.__hasLockedChildAndUnlock__(thisChild)
        return hasLockedChild

同步发文于CSDN,原创不易,转载经作者同意后请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/133198960

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1033777.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于微信小程序的健康评估系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户微信端的主要功能有&#xff1a;医生微信端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文参考源码获取…

iOS17正式版BUG汇总:无法正常拨打电话、小组件不可用、无线充电不可用等问题

今天凌晨 iOS 17 正式版发布&#xff0c;相信不少尝鲜派已经更新体验了iOS17的新功能了&#xff0c;但还有很多用户选择观望看是否要升级&#xff0c;小编汇总了目前已更新的用户反馈的已知BUG&#xff0c;供大家查看是否要更新iOS17正式版&#xff01; 目前已知BUG&#xff1…

【LeetCode75】第六十二题 多米诺和托米诺平铺

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我一个数字n&#xff0c;表示我们有2*n大小的地板需要铺。 我们拥有两种瓷砖&#xff0c;一种的长度为2的多米诺&#xff0c;另一…

Jetpack Compose干货,如何让Compose Dialog从屏幕任意方向进入

一、前言 来个效果图&#xff0c;基于Compose Dialog&#xff0c;最终要实现的库能力如下&#xff1a; 这里使用的是这个包下面的&#xff1a; androidx.compose.ui.window.Dialog androidx.compose.material3.AlertDialog它内部调用的也是androidx.compose.ui.window.Dialog …

Centos7 安装部署 Kubernetes(k8s) 高可用集群

1&#xff1a;基础环境准备 宿主机系统集群角色服务器IP主机名称容器centos7.6master192.168.2.150ks-m1dockercentos7.6master192.168.2.151ks-n1dockercentos7.6master192.168.2.152ks-n2docker 1.1 服务器初始化及网络配置 VMware安装Centos7并初始化网络使外部可以访问*…

No2.详解【2023年全国大学生数学建模竞赛】C题——蔬菜类商品的自动定价与补货决策(代码 + 详细输出 + 数据集代码 下载)

只有不回避痛苦和迷茫的人,才有资格去谈乐观和坚定。命运不会厚待谁,悲喜也不会单为你准备。 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3] 2022年度博客之星人工智能…

爬虫获取接口数据

上一讲讲的是获取静态网页数据的教程&#xff0c;适用于我们要爬取的数据在网页源代码中出现&#xff0c;但是还是有很多的数据是源代码中没有的&#xff0c;需要通过接口访问服务器来获得&#xff0c;下面我就来讲讲如何爬取这类数据。 以巨潮资讯网爬取比亚迪企业年报为例。…

解决windows端口占用

WINR打开cmd窗口&#xff1b;输入命令查看哪个进程占用&#xff0c;8848为要查询占用的端口号&#xff1a;netstat -ano | findstr 8848&#xff1b; 3.杀死进程&#xff0c;输入taskkill /f /t /im 10672 其中10672为上面命令查出来的进程号。

24. 图论 - 图的表示种类

Hi&#xff0c;你好。我是茶桁。 之前的一节课中&#xff0c;我们了解了图的来由和构成&#xff0c;简单的理解了一下图的一些相关概念。那么这节课&#xff0c;我们要了解一下图的表示&#xff0c;种类。相应的&#xff0c;我们中间需要穿插一些新的知识点用于更好的去理解图…

Python异步编程并发执行爬虫任务,用回调函数解析响应

一、问题&#xff1a;当发送API请求&#xff0c;读写数据库任务较重时&#xff0c;程序运行效率急剧下降。 异步技术是Python编程中对提升性能非常重要的一项技术。在实际应用&#xff0c;经常面临对外发送网络请求&#xff0c;调用外部接口&#xff0c;或者不断更新数据库或文…

漏刻有时数据可视化Echarts组件开发(31):geomap伪3D配置示例

echarts.registerMap("丹东", getData());let data = getData().features.map((item) => {return {name: item.properties.name,};});const points = [[116.289929,40.265374],[116.754101,40.063877],[116.229504,39.764735],[115.883434,39.899721]]let option …

Unity的AB包相关

1、打包 在这个界面左边右键&#xff0c;CreateNewBundle 将要打包的模型制作成预设体 在下面勾选 选好平台路径&#xff0c;点击Build 2、加载AB包 public class ABTest : MonoBehaviour {// Start is called before the first frame updatevoid Start(){//加载AB包AssetB…

pymysql调用存储过程

视频版教程 Python操作Mysql数据库之pymysql模块技术 我们首先创建一个简单的存储过程 DELIMITER //CREATE PROCEDURE test_add(m INT,n INT, OUT result INT) BEGIN SET resultmn;END; //测试&#xff1a; SET s0; CALL test_add(1,2,s); SELECT sPymysql调用存储过程实现&…

从植隆业务中台到金蝶云星空通过接口配置打通数据

从植隆业务中台到金蝶云星空通过接口配置打通数据 数据源系统:植隆业务中台 核心能力以数字化形式沉淀为各种服务中心&#xff0c;其目的是“提供企业能够快速&#xff0c;低成本创新的能力”。业务中台的核心是“构建企业共享服务中心”&#xff0c;其过程是通过业务板块之间的…

代码随想录算法训练营第57天| 647. 回文子串,516.最长回文子序列,动态规划总结

链接: 647. 回文子串 链接: 516.最长回文子序列 链接: 动态规划总结 647. 回文子串 理解dp数组的含义很重 class Solution {public int countSubstrings(String s) {char[] chars s.toCharArray();boolean[][] dp new boolean[s.length()][s.length()];int res 0;// 遍…

FL Studio21水果编曲软件怎么下载中文版?

FL Studio21这款软件在国内被广泛使用&#xff0c;因此又被称为"水果"。它提供音符编辑器&#xff0c;可以针对作曲者的要求编辑出不同音律的节奏&#xff0c;例如鼓、镲、锣、钢琴、笛、大提琴、筝、扬琴等等任何乐器的节奏律动。此外&#xff0c;它还提供了方便快捷…

以小见大,彻底理解 cookie,session,token 之间的关系,通俗易懂

发展史 1、很久很久以前&#xff0c;Web 基本上就是文档的浏览而已&#xff0c;既然是浏览&#xff0c;作为服务器&#xff0c; 不需要记录谁在某一段时间里都浏览了什么文档&#xff0c;每次请求都是一个新的 HTTP 协议&#xff0c;就是请求加响应&#xff0c;尤其是我不用记…

菜单栏图标管理软件Bartender mac 5.0.10中文版介绍

Bartender mac是一款菜单栏图标管理软件&#xff0c;功能强大&#xff0c;可以快速管理菜单栏的图标、显示内容和时间&#xff0c;只需在菜单栏中滑动或滚动、单击菜单栏&#xff0c;或者如果您愿意&#xff0c;只需将鼠标悬停即可立即访问隐藏的菜单栏项目。 Bartender软件介绍…

识别准确率达 95%,华能东方电厂财务机器人实践探索

摘 要&#xff1a;基于华能集团公司大数据与人工智能构想理念&#xff0c;结合东方电厂实际工作需要&#xff0c;财务工作要向数字化、智能化纵深推进&#xff0c;随着财务数字化转型和升级加速&#xff0c;信息化水平不断提升&#xff0c;以及内部信息互联互通不断加深&#x…

AI机器人写作-AI机器人写作技术和工具

在这个数字化时代&#xff0c;文本创作是信息传播的主要方式之一。然而&#xff0c;对于许多人来说&#xff0c;写作可能是一项具有挑战性和耗时的任务。文本创作是广告、文章、社交媒体、小说等领域的核心&#xff0c;但却常常需要大量时间和精力来思考、编写和编辑。许多人可…