首页 > 编程语言 >上机编程[文件目录权限管理系统]学习交流

上机编程[文件目录权限管理系统]学习交流

时间:2023-12-12 21:56:36浏览次数:40  
标签:权限 文件目录 上机 int 编程 userId dirId true 目录

请你设计一个文件目录权限管理系统,实现以下功能:

·  DirPermSystem(int[] path, int[] statuses) —— 初始化文件目录树及其初始状态

o   path[i] 下标表示目录编号,值表示其上一级目录的编号(根目录编号为 0,path[0]固定为 -1)。

o   statuses[i] 表示目录 i 的初始状态。仅两种:

§  1 表示「公开」状态,所有用户均可访问(无论是否授权);

§  2 表示「加密」状态,仅拥有权限的用户可访问;

·  changeStatus(int dirId, int status) —— 修改目录 dirId 的状态为 status。(不涉及子目录)

·  grantRight(int userId, int dirId) —— 授予用户 userId 对目录 dirId 持有「目录树权限」。

o 持有对 dirId 的「目录树权限」后,代表该用户可以访问 dirId 及其所有子目录(包含直接子目录、子目录的子目录等),即使加密的目录也可以访问。

o 注意:重复授予同一用户对目录 dirId 的「目录树权限」授权,只保留最后一次。

·  removeRight(int userId, int dirId) —— 移除用户 userId 对目录 dirId 所持有的「目录树权限」。若授予过,则移除对这个目录的授权,并返回 true;否则不做处理,返回 false。

·  queryRight(int userId, int dirId) —— 查询用户 userId 是否可以访问目录 dirId,是返回 true;否则返回 false。

·  queryNum(int userId) —— 查询用户 userId 所有可访问的目录数量。

注: 输入用例保证函数中的 dirId 已存在。

示例 1:

输入:
["DirPermSystem","grantRight","changeStatus","queryRight","queryNum","removeRight"]
[[[-1,4,4,1,0],[1,1,2,1,1]],[101,1],[1,2],[101,3],[101],[101,1]]

输出:[null,null,null,true,4,true]

解释:
DirPermSystem sys = DirPermSystem([-1,4,4,1,0],[1,1,2,1,1]); // 初始化,目录结构示意:

0 (公开)

└ 4 (公开)

  ├ 1 (公开)

  │ └ 3 (公开)

  └ 2 (加密)

sys.grantRight(101,1); // 授予用户 101 对目录 1 的「目录树权限」,即可访问目录1、3
sys.changeStatus(1,2); // 目录 1 从「公开」状态修改为「加密」状态
sys.queryRight(101,3); // 目录 3 为「公开」,因此用户 101 可以进行访问,返回 true
sys.queryNum(101); // 目录 0、4、3 为「公开」状态,用户 101 可以访问;此外,用户有目录 1 的权限,所以返回 4
sys.removeRight(101,1); // 用户 101 有目录 1 的「目录树权限」;移除该权限,返回 true
注:输出中的 null 表示此对应函数无输出(其中:C 的构造函数有返回值,但是也是无需输出)

示例 2:

输入:
["DirPermSystem","grantRight","grantRight","changeStatus","grantRight","grantRight","queryNum","removeRight","queryRight","changeStatus","grantRight","removeRight","queryNum","grantRight","changeStatus","removeRight","queryNum","changeStatus","removeRight","queryNum"]
[[[-1,2,0,6,6,0,0,2,3],[2,1,2,2,2,1,1,2,1]],[105,6],[103,3],[8,2],[105,3],[103,6],[105],[101,6],[103,3],[6,2],[105,6],[103,6],[103],[105,2],[2,1],[105,3],[105],[6,2],[105,6],[105]]

输出:
[null,null,null,null,null,null,6,false,true,null,null,true,4,null,null,true,8,null,true,4]

解释:
DirPermSystem sys = DirPermSystem([-1,2,0,6,6,0,0,2,3],[2,1,2,2,2,1,1,2,1]); // 初始化

0 (加密)

├ 6 (公开)

│ ├ 3 (加密)

│ │ └ 8(公开)

│ └ 4 (加密)

├ 2 (加密)

│ ├ 1 (公开)

│ └ 7 (加密)

└ 5 (公开)

sys.grantRight(105,6); // 授予用户 105 对目录 6 的「目录树权限」,即可访问目录 6 及其子目录 3、8、4
sys.grantRight(103,3);
sys.changeStatus(8,2);
sys.grantRight(105,3);
sys.grantRight(103,6);
sys.queryNum(105); // 用户 105 可以访问目录6、3、8、4、1、5。返回 6
sys.removeRight(101,6); // 返回 false
sys.queryRight(103,3); // 返回 true
sys.changeStatus(6,2);
sys.grantRight(105,6);
sys.removeRight(103,6); // 返回 true
sys.queryNum(103); // 之前用户 103 被授予过目录 3 和 6 的「目录树权限」,虽然随后删除了目录 6 的「目录树权限」,但仍保留着对目录 3 的「目录树权限」,因此仍可访问目录 3 及其子目录8,加上「公开」状态的目录 1、5。所以返回 4
sys.grantRight(105,2);
sys.changeStatus(2,1);
sys.removeRight(105,3); // 返回 true
sys.queryNum(105); // 用户 105 仍保留着对目录 6 和 2 的「目录树权限」,可访问目录为 6、3、8、4、2、1、7,以及公开的目录 5,总共可以访问 8 个目录
sys.changeStatus(6,2);
sys.removeRight(105,6); // 返回 true
sys.queryNum(105); // 用户 105 对于目录 2、1、7 有访问权限,以及公开的目录 5,总共可以访问 4 个目录
注:输出中的 null 表示此对应函数无输出(其中:C 的构造函数有返回值,但是也是无需输出)

提示:

· 2 <= changeStatus,grantRight,removeRight,queryRight,queryNum 累计操作数 <= 10^3

· 0 <= path.length == statuses.length <= 1000

· -1 <= path[i] <= path.length - 1

· 0 <= dirId <= path.length - 1

· 1 <= statuses[i], status <= 2

· 0 <= userId <= 1000

· 用例保证目录的深度不超过 30

 

 

 

2.    题目分析

题目理解:

需要设计一个文件目录权限管理系统,支持以下功能:修改目录加密状态,授予用户目录树权限,移除用户目录树权限,查询用户是否有访问目录权限,查询用户可访问目录数量。

Ø  目录的访问权限和linux的目录权限管理不一样

对某个用户授予持有某目录的「目录树权限」,可以视作对这个目录配了一把“钥匙”,对同一个用户一个目录最多配一把“钥匙”。

有了这把“钥匙”后,此用户就有访问此目录及其子目录的能力,把这把“钥匙”拿走,能力则随之消失。

假设有如下三个目录,目录均为加密状态,层次如下:

7

├ 8

│ └ 9

假定某用户持有目录 7、8 的「目录树权限」,移除目录 7 的权限后,由于仍持有目录 8 的权限,所以可以访问目录 8 和 9。如下图所示:

Ø  重复授予同一用户对目录 dirId 的「目录树权限」授权,只保留最后一次

对同一用户授予对目录7、目录8的「目录树权限」,这两个授权目录是独立且不同的。

只保留最后一次指的是同一个用户对同一个目录授权多次,只保留一次,举个例子:

假设对目录7授权了两次,然后删除授权一次,那么对于目录7也不存在授权了。

思路解析:

我们用节点表示目录,边表示目录之间的层级关系,如下图所示:

将对用户对目录的“授权”和判断用户“对某目录有访问权限”两个动作分开管理:

Ø  授权只管对目录增加授权、删除授权;

Ø  查询用户是否有访问目录权限时,如果目录状态是公开,可直接访问;如果是加密,有两个遍历方向:

l  从下往上,即从 dirId 开始往祖先目录迭代查找,如果某祖先目录授予过目录树权限,则说明此用户可以访问dirId

例如上图中从节点9向上查找,找到节点8的时候发现8被授权过,所以可以访问9。

这种做法,不需要记录每个节点的children节点,只需要记录每个节点的父节点。

l  从上往下,即从授予过目录树权限的目录开始,通过DFS/BFS往子目录遍历。如果子目录编号等于 dirId,说明此用户可以访问dirId

这种做法,就必须要记录每个节点的children节点。

 

因为目录编号是输入参数path的数组下标,必然是唯一的,并且目录编号不超过1000,所以可以使用整型数组statuses来记录每个目录的授权:下标为目录编号、值为授权状态。

可以定义一个TreeNode结构表示一个目录,也可以用数组记录目录的编号、父目录编号。

下面来看看大家具体的实现代码:

##########bfs + 递归树 待验证########
package main

import "fmt"

type dirNode struct {
    id       int // 树id
    status   int // 1代表 公开 2代表 私有
    userId   int
    children []*dirNode
}

type DirPermSystem struct {
    tree map[int]*dirNode
}

func buildTree(parent, child int, statuses []int, nodes map[int]*dirNode) {
    if nodes[child] == nil {
        nodes[child] = &dirNode{id: child, status: statuses[child]}
    }
    if nodes[parent] == nil && parent != -1 {
        nodes[parent] = &dirNode{id: parent, status: statuses[parent]}
    }
    if parent != -1 {
        nodes[parent].children = append(nodes[parent].children, nodes[child])
    }
}

func Constructor(path []int, statuses []int) DirPermSystem {
    // 初始化一个map存储多叉树
    nodes := make(map[int]*dirNode)
    for child, parent := range path {
        buildTree(parent, child, statuses, nodes)
    }
    return DirPermSystem{tree: nodes}
}

func (d *DirPermSystem) ChangeStatus(dirId int, status int) {
    if d, exist := d.tree[dirId]; exist {
        d.status = status
    }
}

func (d *DirPermSystem) GrantRight(userId int, dirId int) {
    grantTree := d.tree[dirId]
    grantTree.userId = userId
    d.SetChildRight(grantTree, userId)
}

func (d *DirPermSystem) SetChildRight(childTree *dirNode, userId int) {
    if childTree == nil {
        return
    }
    for _, child := range childTree.children {
        child.userId = userId
        d.SetChildRight(child, userId)
    }
    return
}

func (d *DirPermSystem) RemoveRight(userId int, dirId int) bool {
    //判断是否移除的权限属于当前用户
    if grantTree, exist := d.tree[dirId]; exist {
        if grantTree.userId != userId {
            return false
        }
        grantTree.userId = 0
        // 递归移除userId授权
        d.SetChildRight(grantTree, 0)
    }
    return true
}

func (d *DirPermSystem) QueryRight(userId int, dirId int) bool {
    // 任一给一个目录id,查看userid是否是授权的userId
    if grantTree, exist := d.tree[dirId]; exist {
        if grantTree.userId == userId {
            return true
        }
        return false
    }
    return false
}

func (d *DirPermSystem) QueryNum(userId int) int {
    // bfs查看所有公开的节点数量+userId授权了的数量
    count := 0
    rootNode := d.tree[0]
    queue := []*dirNode{rootNode}
    for len(queue) != 0 {
        queueLen := len(queue)
        for i := 0; i < queueLen; i++ {
            cur := queue[0]
            queue = queue[1:]
            if cur.userId == userId || cur.status == 1 {
                count++
            }
            queue = append(queue, cur.children...)
        }
    }
    return count
}

func main() {
    obj := Constructor([]int{-1, 4, 4, 1, 0}, []int{1, 1, 2, 1, 1})
    obj.GrantRight(101, 1)
    obj.ChangeStatus(1, 2)
    fmt.Println(obj.QueryRight(101, 3)) // true
    fmt.Println(obj.QueryNum(101))      // 4
    fmt.Println(obj.RemoveRight(101, 1))
    fmt.Println(obj.QueryRight(101, 1)) // true
}
View Code
#############官方给的代码 newbing 翻译的
package main

import (
    "container/list"
    "fmt"
)

type TreeNode struct {
    dirId    int
    children []*TreeNode
}

type DirPermSystem struct {
    path         []int
    statuses     []int
    nodes        []*TreeNode
    userRightMap map[int]map[int]bool
}

func NewDirPermSystem(path []int, statuses []int) *DirPermSystem {
    d := &DirPermSystem{
        path:         path,
        statuses:     statuses,
        userRightMap: make(map[int]map[int]bool),
        nodes:        make([]*TreeNode, len(path)),
    }
    for i := 0; i < len(path); i++ {
        node := &TreeNode{
            dirId:    i,
            children: make([]*TreeNode, 0),
        }
        d.nodes[i] = node
    }
    for i := 1; i < len(path); i++ {
        d.nodes[path[i]].children = append(d.nodes[path[i]].children, d.nodes[i])
    }
    return d
}

func (d *DirPermSystem) ChangeStatus(dirId int, status int) {
    d.statuses[dirId] = status
}

func (d *DirPermSystem) GrantRight(userId int, dirId int) {
    if _, ok := d.userRightMap[userId]; !ok {
        d.userRightMap[userId] = make(map[int]bool)
    }
    d.userRightMap[userId][dirId] = true
}

func (d *DirPermSystem) RemoveRight(userId int, dirId int) bool {
    if _, ok := d.userRightMap[userId]; !ok {
        return false
    }
    delete(d.userRightMap[userId], dirId)
    return true
}

func (d *DirPermSystem) QueryRight(userId int, dirId int) bool {
    if d.statuses[dirId] == 1 {
        return true
    }
    if _, ok := d.userRightMap[userId]; !ok {
        return false
    }
    queue := list.New()
    for dirId := range d.userRightMap[userId] {
        queue.PushBack(d.nodes[dirId])
    }
    for queue.Len() > 0 {
        node := queue.Remove(queue.Front()).(*TreeNode)
        if node.dirId == dirId {
            return true
        }
        for _, child := range node.children {
            queue.PushBack(child)
        }
    }
    return false
}

func (d *DirPermSystem) QueryNum(userId int) int {
    set := make(map[int]bool)
    for i := 0; i < len(d.statuses); i++ {
        if d.statuses[i] == 1 || d.QueryRight(userId, i) {
            set[i] = true
        }
    }
    return len(set)
}

func main() {
    //path := []int{0, 1, 2, 3}
    //statuses := []int{0, 0, 0, 0}
    //system := NewDirPermSystem(path, statuses)
    //system.GrantRight(1, 1)
    //system.GrantRight(1, 2)
    //system.GrantRight(2, 3)
    //fmt.Println(system.QueryRight(1, 1)) // true
    //fmt.Println(system.QueryRight(1, 2)) // true
    //fmt.Println(system.QueryRight(1, 3)) // false
    //fmt.Println(system.QueryRight(2, 1)) // false
    //fmt.Println(system.QueryRight(2, 2)) // true
    //fmt.Println(system.QueryRight(2, 3)) // true

    obj := NewDirPermSystem([]int{-1, 4, 4, 1, 0}, []int{1, 1, 2, 1, 1})
    obj.GrantRight(101, 1)
    obj.ChangeStatus(1, 2)
    fmt.Println(obj.QueryRight(101, 3))  // true
    fmt.Println(obj.QueryNum(101))       // 4
    fmt.Println(obj.RemoveRight(101, 1)) // true
    fmt.Println(obj.QueryRight(101, 1))  // true
}
View Code

 

 

####感受####
1.不够熟悉bfs,导致做题时间太慢了,需要多练习100遍
2.RemoveRight中调用SetChildRight方法没有注意本身的动作,导致结果相悖
3.不够熟悉go自身封装的"container/list",多了解一下,利用起来方便快速编码
4. newbing翻译的基本靠谱,但是没有demo,CoodGEEX有demo可以参考,但代码不全,不准

8.    总结

 

1)        解法总结

判断能否访问加密目录的方法有下面几种:

l  记录每个节点的父节点,然后从目标目录dirId对应的节点向上查找,如果任意一个父节点被授权过,那么说明可以访问dirId

l  从授权过的节点出发,DFS遍历其子节点,如果找到dirId,说明可以访问dirId

l  从授权过的节点出发,BFS遍历其子节点,如果找到dirId,说明可以访问dirId

标签:权限,文件目录,上机,int,编程,userId,dirId,true,目录
From: https://www.cnblogs.com/gongxianjin/p/17897908.html

相关文章

  • 深入解析Python网络编程与Web开发:urllib、requests和http模块的功能、用法及在构建现
     网络和Web开发是Python中不可或缺的重要领域,而其核心模块如urllib、requests和http在处理网络请求、HTTP请求和响应以及Web开发中扮演着关键的角色。这些模块为开发者提供了丰富的工具,使其能够灵活处理网络通信、构建Web应用和与远程服务器进行交互。深入了解这些模块的用法和作......
  • 面向对象编程,看这篇就够了
    一、面向对象编程的概念面向对象编程,是一种程序设计范式,也是一种编程语言的分类。它以对象作为程序的基本单元,将算法和数据封装其中,程序可以访问和修改对象关联的数据。这就像我们在真实世界中操作各种物体一样,比如我们可以打开电视、调整音量、切换频道,而不需要知道电视的内部......
  • 1.并发编程(上)
    1.何为进程和线程? 1.1何为进程?进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。在Java中,我们启动main函数是启动类JVM的进程,其中main函数所在的线程就是该进程的主线程。1.2何为线程?线程......
  • 【并发编程】(二)锁与并发
    并发编程是编程中重要的一环,在特定的场景下,熟悉并发知识并且掌握并发编程显得尤为重要在本篇开篇前针对几个知识点进行说明,虽然有些组件不是位于juc下并且它本身是无锁实现的,但是它却能解决并发相关的问题ThreadLocal的原理ThreadLocal应该是java工程师很熟悉的一个组......
  • 实验6 c语言结构体、枚举应用编程
    实验任务4程序源码1#include<stdio.h>2#defineN1034typedefstruct{5charisbn[20];//isbn号6charname[80];//书名7charauthor[80];//作者8doublesales_price;//售价9intsales_......
  • CodeGeeX智能编程
    一、写在前面大家遇到代码不会的问题,本能的就会去求助chatGPT,但是没有梯子的话,chatGPT是不是也帮不上忙了?秉着白嫖的精神,分享给大家一款非常牛的插件CodeGeex。二、CodeGreex简介CodeGreex支持多种主流IDE,如VSCode、IntelliJIEAD、PyCharm、vim等,同时支持Python、java、C++/C......
  • 实验6 C语言结构体、枚举应用编程
    一、实验目的二、实验准备三、实验内容四、实验结果1.实验任务4源代码:1#include<stdio.h>2#defineN1034typedefstruct{5charisbn[20];//isbn号6charname[80];//书名7charauthor[80];//作者8......
  • C++基础 -4- C/C++混合编程
    ———————C/C++混合编程———————......
  • Java多线程编程
    本文中简单介绍一些java多线程相关的内容1.多线程基础Java通过java.lang.Thread类和java.util.concurrent包提供了多线程支持。一个线程可以通过继承Thread类或实现Runnable接口来创建。classMyThreadextendsThread{publicvoidrun(){//线程执行的代码}......
  • 实验6 C语言结构体,枚举应用编程(附实验5 C语言指针应用编程)
    实验6一,实验目的二,实验准备三,实验内容1,实验任务1task1.c1#include<stdio.h>2#include<string.h>3#defineN3//运行程序输入测试时,可以把这个数组改小一些输入测试45typedefstructstudent{6intid;//学号7......