Open In App

Locking and Unlocking of Resources arranged in the form of n-ary Tree

Improve
Improve
Like Article
Like
Save
Share
Report

Given an n-ary tree of resources arranged hierarchically such that height of tree is O(Log N) where N is total number of nodes (or resources). A process needs to lock a resource node in order to use it. But a node cannot be locked if any of its descendant or ancestor is locked. 
Following operations are required for a given resource node:  

  • islock()- returns true if a given node is locked and false if it is not. A node is locked if lock() has successfully executed for the node.
  • Lock()- locks the given node if possible and updates lock information. Lock is possible only if ancestors and descendants of current node are not locked.
  • unLock()- unlocks the node and updates information.

How design resource nodes and implement above operations such that following time complexities are achieved. 

    islock()  O(1)
    Lock()    O(log N)
    unLock()  O(log N)

We strongly recommend you to minimize your browser and try this yourself first. 
It is given that resources need to be stored in the form of the n-ary tree. Now the question is, how to augment the tree to achieve the above complexity bounds. 

Some General Questions:

Q1. Why doesn’t setting Lock = true alone solve the purpose?
A1. Because for the approaches where we move towards the parent to check if locking is possible if a request comes for any node between a locked node and the root, then there’s no way of telling that a descendant node is locked. Hence, variables like isLockable are used to maintain this info.

Q2. Why not lock all nodes from the current node to the root?
A2. Because locking, in general, is a resource-intensive operation and performing locking for all nodes right up to the root would be a waste of resources. Hence, lightweight solutions such as introducing a isLockable variable are used.

Method 1 (Simple)
A Simple Solution is to store a boolean variable isLocked with every resource node. The boolean variable isLocked is true if the current node is locked, else false. 
Let us see how operations work using this Approach. 
 

  • isLock(): Check isLocked of the given node.
  • Lock(): If isLocked is set, then the node cannot be locked. Else check all descendants and ancestors of the node and if any of them is locked, then also this node cannot be locked. If none of the above conditions is true, then lock this node by setting isLocked as true.
  • unLock(): If isLocked of given node is false, nothing to do. Else set isLocked as false.

Time Complexities: 

isLock()  O(1) 
Lock()    O(N)
unLock()  O(1)

Lock is O(N) because there can be O(N) descendants. 

Method 2 (Time Complexities according to question) 
The idea is to augment tree with following three fields: 
 

  1. A boolean field isLocked (same as above method).
  2. Parent-Pointer to access all ancestors in O(Log n) time.
  3. Count-of-locked-descendants. Note that a node can be ancestor of many descendants. We can check if any of the descendants is locked using this count.

Let us see how operations work using this Approach. 

  • isLock(): Check isLocked of the given node.
  • Lock(): Traverse all ancestors. If none of the ancestors is locked, Count-of-locked-descendants is 0 and isLocked is false, set isLocked of current node as true. And increment Count-of-locked-descendants for all ancestors. Time complexity is O(Log N) as there can be at most O(Log N) ancestors.
  • unLock(): Traverse all ancestors and decrease Count-of-locked-descendants for all ancestors. Set isLocked of current node as false. Time complexity is O(Log N)

Thanks to Utkarsh Trivedi for suggesting this approach. 

Method 3 (Time Complexities according to question and better memory usage) The idea is to augment tree with following three fields:

  1. A boolean field isLocked (same as above method).
  2. Parent-Pointer to access all ancestors in O(Log n) time.
  3. isLockable. We can check if any of the descendants is locked using this variable. isLockable is true if none of the descendants are locked else false.

Let us see how operations work using this Approach.

  • isLock(): Check isLocked of the given node.
  • Lock(): Traverse all ancestors. If none of the ancestors is locked, isLockable is true and isLocked is false, set isLocked of current node as true. And mark isLockable false for all ancestors. Time complexity is O(Log N) as there can be at most O(Log N) ancestors.
  • unLock(): Traverse all ancestors and mark isLockable true for all ancestors. Set isLocked of current node as false. Time complexity is O(Log N)

C++




#include <bits/stdc++.h>
using namespace std;
 
class narytree {
public:
    bool isLock;
    bool isLockable;
    narytree* parent;
    vector<narytree*> children;
    narytree()
    {
        isLock = false;
        isLockable = true;
        parent = NULL;
    }
    narytree(narytree* parent)
    {
        isLock = false;
        isLockable = true;
        this->parent = parent;
    }
};
 
bool isLock(narytree* node) { return node->isLock; }
 
void Lock(narytree* node)
{
    if (node->isLockable == false)
        return;
 
    narytree* T = node;
    bool flag = false;
    while (T != NULL) {
        if (T->isLock == true) {
            flag = true;
            break;
        }
        T = T->parent;
    }
    if (flag)
        return;
    else {
        node->isLock = true;
        T = node;
        // marking isLockable as false for ancestor nodes.
        while (T != NULL) {
            T->isLockable = false;
            T = T->parent;
        }
    }
}
 
void unLock(narytree* node)
{
    if (node->isLock == false)
        return;
    narytree* T = node;
    node->isLock = false;
    // marking isLockable as true for ancestor nodes.
    while (T != NULL) {
        T->isLockable = true;
        T = T->parent;
    }
}
 
int main()
{
    // Creating N-Array Tree
    narytree* root = new narytree();
 
    narytree* t1 = new narytree(root);
    narytree* t2 = new narytree(root);
    narytree* t3 = new narytree(root);
 
    root->children.push_back(t1);
    root->children.push_back(t2);
    root->children.push_back(t3);
 
    narytree* t5 = new narytree(root->children[0]);
    root->children[0]->children.push_back(t5);
    narytree* t4 = new narytree(root->children[1]);
    root->children[1]->children.push_back(t4);
 
    // Locking t4 node.
    Lock(t4);
    //    Checking if the t4 node is locked.
    cout << "t4 node is locked:"
         << ((isLock(t4) == true) ? "true" : "false")
         << "\n";
    Lock(t2);
    cout << "t2 node is locked:"
         << ((isLock(t2) == true) ? "true" : "false")
         << "\n";
    // Unlocking t4 node.
    unLock(t4);
    //    Now we should be able to lock the tree.
    Lock(t2);
    cout << "t2 node is locked:"
         << ((isLock(t2) == true) ? "true" : "false");
 
    return 0;
}


Java




import java.io.*;
import java.util.*;
 
public class GFG {
 
    static class narytree {
        boolean isLock;
        boolean isLockable;
        narytree parent;
        List<narytree> children;
 
        narytree()
        {
            isLock = false;
            isLockable = true;
            parent = null;
        }
 
        narytree(narytree parent)
        {
            isLock = false;
            isLockable = true;
            this.parent = parent;
        }
    }
 
    static boolean isLock(narytree node)
    {
        return node.isLock;
    }
 
    static void lock(narytree node)
    {
        if (node.isLockable == false) {
            return;
        }
 
        narytree T = node;
        boolean flag = false;
        while (T != null) {
            if (T.isLock == true) {
                flag = true;
                break;
            }
            T = T.parent;
        }
 
        if (flag) {
            return;
        }
        else {
            T = node;
            node.isLock = true;
            // marking isLockable as false for ancestor
            // nodes.
            while (T != null) {
                T.isLockable = false;
                T = T.parent;
            }
        }
    }
 
    static void unLock(narytree node)
    {
        if (node.isLock == false) {
            return;
        }
 
        narytree T = node;
        node.isLock = false;
        // marking isLockable as true for ancestor nodes.
        while (T != null) {
            T.isLockable = true;
            T = T.parent;
        }
    }
 
    public static void main(String[] args)
    {
        // Creating N-Array Tree
        narytree root = new narytree();
 
        narytree t1 = new narytree(root);
        narytree t2 = new narytree(root);
        narytree t3 = new narytree(root);
 
        root.children = new ArrayList<>();
        root.children.add(t1);
        root.children.add(t2);
        root.children.add(t3);
 
        narytree t5 = new narytree(root.children.get(0));
        root.children.get(0).children = new ArrayList<>();
        root.children.get(0).children.add(t5);
 
        narytree t4 = new narytree(root.children.get(1));
        root.children.get(1).children = new ArrayList<>();
        root.children.get(1).children.add(t4);
 
        // Locking t4 node.
        lock(t4);
        // Checking if the t4 node is locked.
        System.out.println(
            "t4 node is locked:"
            + ((isLock(t4) == true) ? "true" : "false"));
 
        lock(t2);
        System.out.println(
            "t2 node is locked:"
            + ((isLock(t2) == true) ? "true" : "false"));
        // Unlocking t4 node.
        unLock(t4);
        // Now we should be able to lock the tree.
        lock(t2);
        System.out.println(
            "t2 node is locked:"
            + ((isLock(t2) == true) ? "true" : "false"));
    }
}
 
// This code is contributed by Snigdha Patil


Python3




#Python code for the above approach
class narytree:
    def __init__(self):
        self.isLock = False
        self.isLockable = True
        self.parent = None
        self.children = []
         
    def isLock(self):
        return self.isLock
     
    def Lock(self, node):
        if node.isLockable == False:
            return
        T = node
        flag = False
        while T != None:
            if T.isLock == True:
                flag = True
                break
            T = T.parent
        if flag:
            return
        else:
            node.isLock = True
            T = node
            while T != None:
                T.isLockable = False
                T = T.parent
                 
    def unLock(self, node):
        if node.isLock == False:
            return
        T = node
        node.isLock = False
        while T != None:
            T.isLockable = True
            T = T.parent
 
# Creating N-Array Tree
root = narytree()
 
t1 = narytree()
t1.parent = root
t2 = narytree()
t2.parent = root
t3 = narytree()
t3.parent = root
 
root.children.append(t1)
root.children.append(t2)
root.children.append(t3)
 
t5 = narytree()
t5.parent = root.children[0]
root.children[0].children.append(t5)
t4 = narytree()
t4.parent = root.children[1]
root.children[1].children.append(t4)
 
# Locking t4 node.
root.Lock(t4)
 
# Checking if the t4 node is locked.
print("t4 node is locked:", t4.isLock)
 
root.Lock(t2)
print("t2 node is locked:", t2.isLock)
 
# Unlocking t4 node.
root.unLock(t4)
 
# Now we should be able to lock the tree.
root.Lock(t2)
print("t2 node is locked:", t2.isLock)


C#




using System;
using System.Collections.Generic;
 
public class narytree {
    public bool isLock;
    public bool isLockable;
    public narytree parent;
    public List<narytree> children;
 
    public narytree()
    {
        isLock = false;
        isLockable = true;
        parent = null;
        children = new List<narytree>();
    }
 
    public narytree(narytree parent)
    {
        isLock = false;
        isLockable = true;
        this.parent = parent;
        children = new List<narytree>();
    }
}
 
public class Program {
    public static bool isLock(narytree node)
    {
        return node.isLock;
    }
 
    public static void Lock(narytree node)
    {
        if (!node.isLockable) {
            return;
        }
 
        narytree T = node;
        bool flag = false;
        while (T != null) {
            if (T.isLock) {
                flag = true;
                break;
            }
            T = T.parent;
        }
 
        if (flag) {
            return;
        }
        else {
            node.isLock = true;
            T = node;
            // marking isLockable as false for ancestor
            // nodes.
            while (T != null) {
                T.isLockable = false;
                T = T.parent;
            }
        }
    }
 
    public static void unLock(narytree node)
    {
        if (!node.isLock) {
            return;
        }
 
        narytree T = node;
        node.isLock = false;
        // marking isLockable as true for ancestor nodes.
        while (T != null) {
            T.isLockable = true;
            T = T.parent;
        }
    }
 
    public static void Main()
    {
        // Creating N-Array Tree
        narytree root = new narytree();
 
        narytree t1 = new narytree(root);
        narytree t2 = new narytree(root);
        narytree t3 = new narytree(root);
 
        root.children.Add(t1);
        root.children.Add(t2);
        root.children.Add(t3);
 
        narytree t5 = new narytree(root.children[0]);
        root.children[0].children.Add(t5);
        narytree t4 = new narytree(root.children[1]);
        root.children[1].children.Add(t4);
 
        // Locking t4 node.
        Lock(t4);
        // Checking if the t4 node is locked.
        Console.WriteLine(
            "t4 node is locked: "
            + ((isLock(t4) == true) ? "true" : "false"));
        Lock(t2);
        Console.WriteLine(
            "t2 node is locked: "
            + ((isLock(t2) == true) ? "true" : "false"));
        // Unlocking t4 node.
        unLock(t4);
        // Now we should be able to lock the tree.
        Lock(t2);
        Console.WriteLine(
            "t2 node is locked: "
            + ((isLock(t2) == true) ? "true" : "false"));
    }
}
// This code is contributed by divyansh2212


Javascript




class narytree {
    constructor() {
        this.isLock = false;
        this.isLockable = true;
        this.parent = null;
        this.children = [];
    }
 
 
    isLocked() {
        return this.isLock;
    }
 
    Lock(node) {
        if (!node.isLockable) {
            return;
        }
        let T = node;
        let flag = false;
        while (T != null) {
            if (T.isLock == true) {
                flag = true;
                break;
            }
            T = T.parent;
        }
        if (flag) {
            return;
        } else {
            node.isLock = true;
            T = node;
            while (T != null) {
                T.isLockable = false;
                T = T.parent;
            }
        }
    }
 
    unLock(node) {
        if (!node.isLock) {
            return;
        }
        let T = node;
        node.isLock = false;
        while (T != null) {
            T.isLockable = true;
            T = T.parent;
        }
    }
}
 
// Creating N-Array Tree
let root = new narytree();
 
let t1 = new narytree();
t1.parent = root;
let t2 = new narytree();
t2.parent = root;
let t3 = new narytree();
t3.parent = root;
 
root.children.push(t1);
root.children.push(t2);
root.children.push(t3);
 
let t5 = new narytree();
t5.parent = root.children[0];
root.children[0].children.push(t5);
let t4 = new narytree();
t4.parent = root.children[1];
root.children[1].children.push(t4);
 
// Locking t4 node.
root.Lock(t4);
 
// Checking if the t4 node is locked.
console.log("t4 node is locked:", t4.isLock);
 
root.Lock(t2);
console.log("t2 node is locked:", t2.isLock);
 
// Unlocking t4 node.
root.unLock(t4);
 
// Now we should be able to lock the tree.
root.Lock(t2);
console.log("t2 node is locked:", t2.isLock);


Output

t4 node is locked:true
t2 node is locked:false
t2 node is locked:true

Time Complexity: O(Log N) where N is total number of nodes (or resources).
Auxiliary Space : O(N) 

Thanks to Adarsh Singh for suggesting this approach. 



Last Updated : 15 Mar, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads