以n叉树形式排列的资源的加锁和解锁
给定一棵按层次排列的资源树,使得树的高度为 O(Log N),其中 N 是节点(或资源)的总数。进程需要锁定资源节点才能使用它。但是,如果节点的任何后代或祖先被锁定,则无法锁定节点。
给定资源节点需要以下操作:
- islock() - 如果给定节点被锁定,则返回 true,否则返回 false。如果 lock() 已成功执行该节点,则该节点被锁定。
- Lock() - 如果可能,锁定给定节点并更新锁定信息。仅当当前节点的祖先和后代未锁定时才可以锁定。
- unLock() - 解锁节点并更新信息。
如何设计资源节点并实现上述操作以实现以下时间复杂度。
islock() O(1)
Lock() O(log N)
unLock() O(log N)
我们强烈建议您最小化您的浏览器并首先自己尝试。
假设资源需要以n叉树的形式存储。现在的问题是,如何扩充树以实现上述复杂性界限。
一些一般性问题:
Q1。为什么单独设置 Lock = true 不能解决目的?
A1。因为对于我们向父节点移动以检查是否可以锁定的方法,如果请求来自锁定节点和根节点之间的任何节点,那么没有办法告诉后代节点被锁定。因此,诸如 isLockable 之类的变量用于维护此信息。
Q2。为什么不锁定当前节点到根节点的所有节点呢?
A2。因为锁定通常是一种资源密集型操作,并且对直到根节点的所有节点执行锁定将浪费资源。因此,使用了轻量级解决方案,例如引入 isLockable 变量。
方法1(简单)
一个简单的解决方案是为每个资源节点存储一个布尔变量isLocked 。如果当前节点被锁定,布尔变量 isLocked 为真,否则为假。
让我们看看如何使用这种方法进行操作。
- isLock():检查给定节点的isLocked 。
- Lock():如果设置了isLocked ,则无法锁定节点。否则检查节点的所有后代和祖先,如果其中任何一个被锁定,则该节点也不能被锁定。如果以上条件均不成立,则通过将isLocked设置为 true 来锁定该节点。
- unLock():如果给定节点的isLocked为 false,则不做任何事情。否则将isLocked设置为 false。
时间复杂度:
isLock() O(1)
Lock() O(N)
unLock() O(1)
Lock is O(N) because there can be O(N) descendants.
方法2(根据问题的时间复杂度)
这个想法是用以下三个字段来扩充树:
- 布尔字段isLocked (与上述方法相同)。
- 在 O(Log n) 时间内访问所有祖先的父指针。
- 锁定后代的计数。请注意,一个节点可以是许多后代的祖先。我们可以使用此计数检查是否有任何后代被锁定。
让我们看看如何使用这种方法进行操作。
- isLock():检查给定节点的isLocked 。
- Lock():遍历所有祖先。如果没有一个祖先被锁定, Count-of-locked-descendants为 0 并且isLocked为 false,则将当前节点的isLocked设置为 true。并为所有祖先增加Count-of-locked-descendants 。时间复杂度为 O(Log N),因为最多可以有 O(Log N) 个祖先。
- unLock():遍历所有祖先,减少所有祖先的 Count-of-locked-descendants 。将当前节点的isLocked设置为 false。时间复杂度为 O(Log N)
感谢 Utkarsh Trivedi 提出这种方法。
方法3(根据问题的时间复杂度和更好的内存使用)这个想法是用以下三个字段来扩充树:
- 布尔字段 isLocked (与上述方法相同)。
- 在 O(Log n) 时间内访问所有祖先的父指针。
- 是可锁定的。我们可以使用这个变量检查是否有任何后代被锁定。如果没有后代被锁定,isLockable 为真,否则为假。
让我们看看如何使用这种方法进行操作。
- isLock():检查给定节点的isLocked。
- Lock():遍历所有祖先。如果没有一个祖先被锁定,isLockable 为真,isLocked 为假,则设置当前节点的 isLocked 为真。并且为所有祖先标记 isLockable false。时间复杂度为 O(Log N),因为最多可以有 O(Log N) 个祖先。
- unLock():遍历所有祖先并将所有祖先的isLockable标记为true。将当前节点的 isLocked 设置为 false。时间复杂度为 O(Log N)
C++
#include
using namespace std;
class narytree {
public:
bool isLock;
bool isLockable;
narytree* parent;
vector 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;
}
感谢 Adarsh Singh 提出这种方法。