【问题标题】:Deleting a node from a skip list从跳过列表中删除节点
【发布时间】:2011-02-25 21:36:46
【问题描述】:

我在从跳过列表中删除节点时遇到了一些问题。我有以下结构:

struct Node
{
    int info;
    Node **link_;

    Node(int v, int levels)
    {
        info = v;
        link_ = new Node*[levels];

        for ( int i = 0; i < levels; ++i )
            link_[i] = NULL;
    }
};

struct List
{
    int H; // list height

    Node *Header;

    List()
    {
        Header = new Node(0, maxH);
        H = 1;
    }
};

我认为,问题在于搜索具有给定值的节点然后将其删除的函数。函数实现如下:

void Remove(int v, List *L)
{
    Node *current = L->Header;

    for ( int i = L->H - 1; i >= 0; --i )
    {
        for ( ; current->link_[i] != NULL; current = current->link_[i] )
            if ( current->link_[i]->info > v )
                break;
            else if ( current->link_[i]->info == v )
            {
                // Node *del = current->link_[i];
                current->link_[i] = current->link_[i]->link_[i];

                // delete del;
                break;
            }
    }
}

该函数可以正常工作,但是如果我取消注释两条注释行,列表似乎会中断。中断是指以下测试代码永远不会完成:

int main()
{
    srand((unsigned)time(0));

    List *SKL = new List();


    for ( int i = 0; i < 1000000; ++i )
        Insert(rand() * rand(), SKL);

    for ( int i = 0; i < 1000000; ++i )
        Search(rand() * rand(), SKL);

    for ( int i = 0; i < 1000000; ++i )
        Remove(rand() * rand(), SKL);

    for ( int i = 0; i < 1000000; ++i )
        Insert(rand() * rand(), SKL);


    return 0;
}

问题在于最后一个for 循环在列表中插入更多值。如果这两行被注释掉,程序将在大约 10 秒内完成。如果我取消注释它们,它似乎进入了一个无限循环,因为它甚至没有在几分钟内完成。我不确定这是否是从跳过列表中删除节点的正确方法,所以我也发布了我的插入函数:

void Insert(int v, List *L)
{
    // this section just determines the levels the new node will be part of
    int newH = 0;

    int tmp;
    unsigned int rnd = rand() * rand(); 
    do
    {
        tmp = lookup[rnd & 255];
        rnd >>= 8;
        newH += tmp;
    } while ( tmp == 8 );

    if ( newH >= L->H )
        ++L->H;

    // this does the actual insertion
    Node *newNode = new Node(v, L->H);
    Node *current = L->Header;
    for ( int i = L->H - 1; i >= 0; --i )
    {
        for ( ; current->link_[i] != NULL; current = current->link_[i] )
            if ( current->link_[i]->info >= v )
                break;

        if ( i <= newH )
        {
            newNode->link_[i] = current->link_[i];
            current->link_[i] = newNode;
        }
    }
}

所以,我的问题是:

  1. 程序为什么会中断?如何在实际删除我想从内存中删除的节点时让它工作?
  2. 这是实现跳过列表的正确方法,还是我使用了错误的方法?

【问题讨论】:

  • 它看起来很像 C,但有一些构造函数。当节点被销毁时,您可以利用节点的析构函数自动将“前一个”重新映射到“下一个”。

标签: c++ algorithm data-structures implementation skip-lists


【解决方案1】:

如您所料,Remove 方法存在问题:

void Remove(int v, List *L)
{
    Node *current = L->Header;

    for ( int i = L->H - 1; i >= 0; --i )
    {
        for ( ; current->link_[i] != NULL; current = current->link_[i] )
        {
            if ( current->link_[i]->info > v )
            {
                break;
            }
            else if ( current->link_[i]->info == v )
            {
                // Node *del = current->link_[i];
                current->link_[i] = current->link_[i]->link_[i];

                // if (0 == i) delete del;
                break;
            }
        }
    }
}

为了举例,假设我们要删除的节点出现在 2 个级别:0 和 1。

for 级别 L-&gt;H 到 2 的循环不会执行任何操作。

在级别 1 上,它会找到请求的节点并将其删除。

在级别 0 上,它将尝试跟随指向已删除节点的指针,从而导致未定义的行为。

解决方案:

只有在0级时才删除节点。只要你在上层,节点仍然被引用,你需要保持它活着。

【讨论】:

  • 这行得通,谢谢。这不会从级别 0 中删除节点吗?我不应该从所有级别中删除它吗?还是这种设计不可能做到这一点?
  • 问题是:只有一个节点,从多个级别索引。您需要删除对它的所有引用,然后将其删除(仅一次)。由于您按降序迭代级别,因此实际删除应该发生在最后一个级别 (0)。
  • 啊,是的,这是有道理的。感谢您的帮助。
【解决方案2】:

在您的 Remove 函数中,您的外部循环遍历列表以计算当前条目数。然后在循环中删除其中一个 ntries,但继续迭代旧计数。

【讨论】:

  • 我不确定我是否遵循。计数是跳过列表中的级别数。我从最高(较少节点)向最低(所有节点)迭代。理想情况下,如果一个级别变空,我应该减少级别计数,是的,但我不明白空级别如何导致无限循环。如果它为空,则内部循环立即命中 NULL,并且计数递减。
猜你喜欢
  • 1970-01-01
  • 2016-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-31
相关资源
最近更新 更多