【问题标题】:How do I access minimum element using array-based binary heap?如何使用基于数组的二进制堆访问最小元素?
【发布时间】:2020-03-09 13:48:59
【问题描述】:

我正在尝试实现一个基于数组的、固定大小的最小二进制堆 ADT。当我测试我的程序时,我编写的所有函数似乎都工作正常,除了找到只应该返回存储在根节点的整数值的最小元素。此实现中的根节点位于索引 1 处。 我不断收到的错误是读取访问冲突。 下面是二叉堆类的定义和函数的实现:

class BinaryHeap {

public:
    BinaryHeap(int); // constructor that takes the capacity of the structure
    ~BinaryHeap(); // destructor
    void insert(int); // inserts a new element to the heap
    void deleteMin(); // removes the minimum element from the heap
    int getMin(); // returns the minimum element int the heap, returns -1 if the heap is empty

private:
    int *heap; // array to store the elements of the heap
    int size; // keeps the number of elements in the heap
    int capacity; // keeps the total capacity of the heap
    void percolateDown(int);
    void percolateUp(int);
    void swap(int, int);
};


BinaryHeap::BinaryHeap(int capacity) {
    this->capacity = capacity;

    heap = new int[capacity+1];
    size = 0;
}

BinaryHeap::~BinaryHeap() {
    delete [] heap;
}

void BinaryHeap::insert(int element) {

    if (size < capacity) {
        size++;
        heap[size] = element;
        percolateUp(size);
    }
    else return;
}

void BinaryHeap::deleteMin() {

    if (size < 1) 
        return;
    else {
        heap[1] = heap[size];
        size--;
        percolateDown(1);
    }
}

int BinaryHeap::getMin() {
    if (size < 1)
        return -1;
    else return heap[1];
}

void BinaryHeap::percolateDown(int hole) {


    int leftChildIndex, rightChildIndex, minIndex;
    leftChildIndex = hole * 2;
    rightChildIndex = hole * 2 + 1;
    if (rightChildIndex >= size) {
        if (leftChildIndex >= size) return;
        else minIndex = leftChildIndex;
    }
    else {
        if (heap[leftChildIndex] <= heap[rightChildIndex])
            minIndex = leftChildIndex;
        else
            minIndex = rightChildIndex;
    }

    if (heap[hole] > heap[minIndex]) {
        swap(hole, minIndex);
        percolateDown(minIndex);
    }
}
void BinaryHeap::percolateUp(int index) {
    int parentIndex(1);
    if (index != 1) {
        parentIndex = index / 2;
    }
    if (heap[parentIndex] > heap[index]) {
        swap(parentIndex, index);
        percolateUp(parentIndex);
    }

}

void BinaryHeap::swap(int i, int j) {
    int t = heap[i];
    heap[i] = heap[j];
    heap[j] = t;
}

【问题讨论】:

  • 我正在尝试实现一个基于数组的、固定大小的最小二进制堆 ADT -- 你不使用现成的heap functions 有什么原因吗?
  • 为什么不在索引零处?
  • 这是因为使用 leftchild=index*2、rightchild=index*2+1 和 parent=index/2 可以更轻松地访问子/父索引。 - - 所以你的教授正试图把一个方形钉子装进一个圆孔里。 C++ 从 0 开始数组,而不是 1。通过伪造一个基于 1 的数组,您可能会面临右边缘下降的风险,从而导致内存访问错误,或者错误地访问元素 0,从而导致错误的计算。相信我,我之前已经看过数百次了,编码人员正在尝试这样做,然后最终遇到这些问题。
  • 相关:stackoverflow.com/a/49806133/56778。我认为你的导师应该读这个。还有stackoverflow.com/a/22900767/56778

标签: c++ data-structures


【解决方案1】:

您的 percolateDown 函数中存在错误。

void BinaryHeap::percolateDown(int hole) {
    int leftChildIndex, rightChildIndex, minIndex;
    leftChildIndex = hole * 2;
    rightChildIndex = hole * 2 + 1;

    if (rightChildIndex >= size) {
        if (leftChildIndex >= size) return;
        else minIndex = leftChildIndex;
    }
    else {
        if (heap[leftChildIndex] <= heap[rightChildIndex])
            minIndex = leftChildIndex;
        else
            minIndex = rightChildIndex;
    }

    if (heap[hole] > heap[minIndex]) {
        swap(heap[hole], heap[minIndex]);
        percolateDown(minIndex);
    }
}

假设您的堆中有 4 个项目。在视觉上,它看起来像这样:

         1
       /   \
      3     2
     /
    4

由于您从 1 开始堆,因此该数组将包含 [0,1,3,2,4],而 size4。你调用deleteMin,它将最后一项移到前面,给你[0,4,3,2,4]size = 3。然后它调用percolateDown(1)

percolateDown 计算 leftChildIndex = 2rightChildIndex = 3。然后你打这个:

`if (rightChildIndex >= size)`

但是rightChildIndex等于3,和size是一样的。所以代码永远不会输入条件来比较右孩子和左孩子。相反,它会将左孩子与父母进行比较,发现它更少,然后交换节点。你最终会得到这个无效的堆:

         3
       /   \
      4     2

有趣的是,&gt;= 检查索引是处理从 0 开始的数组时的常见习惯用法。但是您正在使用的代码将数组视为从 1 开始的。对于基于 1 的数组,标准习语是 &gt;。这是我强烈建议在使用具有基于 0 的数组的语言时将二进制堆的根节点设置为 0 的原因之一。

更简洁(且正确)的方法是:

    int leftChildIndex, rightChildIndex, minIndex;
    leftChildIndex = hole * 2;
    rightChildIndex = hole * 2 + 1;

    if (leftChildIndex > size) {
        // if the left child index is outside the heap,
        // then the right child index will be, too.
        return;
    }

    // assume left is smallest
    minIndex = leftChildIndex;

    // and only check the right if it exists
    if (rightChildIndex <= size) {
        if (heap[rightChildIndex] < heap[leftChildIndex]) {
            minIndex = rightChildIndex;
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-18
    • 2015-02-25
    • 1970-01-01
    • 2011-10-24
    • 1970-01-01
    • 2012-08-27
    相关资源
    最近更新 更多