【问题标题】:Removing elements from heap从堆中删除元素
【发布时间】:2013-10-31 07:41:53
【问题描述】:

我做了一堆。我很好奇我的删除功能是否有一些微妙的问题:

int Heap::remove() {
    if (n == 0)
        exit(1);

    int temp = arr[0];
    arr[0] = arr[--n];
    heapDown(0);
    arr[n] = 0;

    return temp;
}

void Heap::heapDown(int i)
{
    int l = left(i);
    int r = right(i);
    // comparing parent to left/right child
    // each has an inner if to handle if the first swap causes a second swap
    //  ie    1   ->   3   ->   5
    //      3   5    1   5    1   3

    if (l < n && arr[i] < arr[l])
    {
        swap(arr[i], arr[l]);
        heapDown(l);

        if (r < n && arr[i] < arr[r])
        {
            swap(arr[i], arr[r]);
            heapDown(r);
        }
    }
    else if (r < n && arr[i] < arr[r]) 
    {
        swap(arr[i], arr[r]);
        heapDown(r);

        if (l < n && arr[i] < arr[l])
        {
            swap(arr[i], arr[l]);
            heapDown(l);
        }
    }
}

这是我的输出

i1i2i3i4i5i6i7
p
Active heap: 7 4 6 1 3 2 5

r
Removed 7
r
Removed 6
p
Active heap: 5 3 4 1 2

这是我老师的示例输出:

p
Active heap : 7 4 6 1 3 2 5
r   
Removed 7
r
Removed 6
p
Active heap : 5 4 2 1 3
s
Heapsorted : 1 2 3 4 5

虽然我们的输出完全不同,但我似乎确实持有 maxheap 原则,即所有节点都面向左,并且所有节点都是父节点 > 子节点(在每种情况下我都尝试过)。我尝试从头开始做这样的算法,所以也许我只是在做一些非常奇怪和错误的事情(如果它>O(lg n),我只会认为它“错误”,因为删除是为了堆)。我的删除有什么特别“错误”吗?谢谢,

http://ideone.com/PPh4eQ

【问题讨论】:

  • +1 关于这个问题。你提供了代码、你的既定目标、你看到的正在发生的事情、你认为应该发生的事情,以及你与这两者的脱节。示例代码直截了当,虽然它不是一个完整的、可编译的代码库,但您认为问题所在的核心是完整​​且易于理解的。谢谢你的好问题。

标签: c++ algorithm data-structures heap heapsort


【解决方案1】:

首先,我假设您的意思是除了您不需要它,因为我们在 C++ 标准库中设置了完整的堆管理函数,包括 make_heap、push_heap、pop_heap 甚至 sort_heap。

也就是说,我想我知道你的问题是什么。您的堆中有不必要的元素移动。它处理堆下的交换算法:左右两侧都存在同样的问题,所以我将展示第一个:

if (l < n && arr[i] < arr[l])
{
    swap(arr[i], arr[l]);
    heapDown(l);

    if (r < n && arr[i] < arr[r])
    {
        swap(arr[i], arr[r]);
        heapDown(r);
    }
}

这里的逻辑对于最小运动不是最优的。被下推的元素的“较小”状态必须属于两个基本类别之一,并且针对每个类别采取不同的操作:

  1. 元素不小于 either left 或 right。什么都不做。
  2. 元素小于左右,只与最大的交换,然后向下移动到only那个子树。李>

该列表中的上述 #2 是您的代码中的问题。如果item 然后较大的交换。我希望这很清楚。如果您想要一个解决您的逻辑的建议,我可以提供一个,但我认为如果您理解我上面描述的内容,您可能会处理它。


剧透

void Heap::heapDown(int i)
{
    int l = left(i);
    int r = right(i);
    int x = 0;

    if (l < n && arr[i] < arr[l])
    {
        x = l;
        if (r < n && arr[l] < arr[r])
            x = r;
    }

    else if (r < n && arr[i] < arr[r])
        x = r;

    if (x != 0)
    {
        swap(arr[i], arr[x]);
        heapDown(x);
    }
}

注意:;如果不是很明显,这就是尾递归的定义,因此可以很容易地转换为简单的迭代循环。

【讨论】:

  • 不错的收获。我相信堆仍然有效,但它需要更多的工作,因为左右分支都必须重新排序(而你所描述的较大孩子上的 heapDown 就足够了)。
  • @zennehoy 如果我正确阅读代码,如果出现我提到的情况,它可能交换左右孩子,这将是非常糟糕的。我必须得到一块白板才能确定以某种方式知道,但它似乎可能。如果按照我描述的那样更改,我知道它应该以最小的动作正常工作。
  • 是的,是的,我喜欢 stl。数据结构类:)
猜你喜欢
  • 2012-04-27
  • 1970-01-01
  • 2012-08-27
  • 2021-05-27
  • 2013-11-07
  • 2021-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多