【问题标题】:java heapify method with comparator带有比较器的java heapify方法
【发布时间】:2015-03-12 04:10:43
【问题描述】:

我正在尝试编写一个课程HeapQueue。我将根的左孩子存储在2 * indexOfRoot + 1 索引,右孩子存储在2 * indexOfRoot + 2

public class HeapQueue implements PriorityQueue, BinaryHeap {

public List<Task> queue;
public Comparator comparator;

public HeapQueue() {
    queue = new ArrayList();
}

public void setComparator(Comparator comparator) {
    this.comparator = comparator;
    heapify(0);
}

public Comparator getComparator() {
    return comparator;
}

public void offer(Task task) {
    int currentElement, previousElement;
    queue.add(task);
    currentElement = queue.size() - 1;
    previousElement = (currentElement - 1) / 2;
    while (previousElement >= 0 && 
             getComparator().compare(queue.get(currentElement), queue.get(previousElement)) > 0) {
        swap(currentElement, previousElement);
        currentElement = previousElement;
        previousElement = (currentElement - 1) / 2;
    }
}

private void swap(int i, int j) {
    Task t1 = queue.get(i);
    Task t2 = queue.get(j);
    Task t3 = t1;
    queue.set(i, t2);
    queue.set(j, t3);
}
}

Task的队列存储对象。

public class Task {

private final String name;
private final int priority;

public Task(String name, int priority) {
    this.name = name;
    this.priority = priority;
}

public int getPriority() {
    return priority;
}

@Override
public String toString() {
    return name + "\tpriority = " + priority;
}
}

我在HeapQueue 中有一个方法heapify()

public void heapify(int root) {
    int leftChild, rightChild;
    leftChild = 2 * root + 1;
    if (leftChild < queue.size()) {
        rightChild = leftChild + 1;
        if ((rightChild < queue.size())
                && getComparator().compare(queue.get(rightChild), queue.get(leftChild)) > 0) {
            leftChild = rightChild;
        }
        if (getComparator().compare(queue.get(leftChild), queue.get(root)) > 0) {
            swap(root, leftChild);
            heapify(leftChild);
        }
    }

}

将任务添加到队列后,我的任务比较器可能会通过方法setComparator() 进行更改。 默认Comparator 是:

public class Comparator{
    public int compare(Task t1, Task t2) {
        if (t1.getPriority() == t2.getPriority()) {
            return 0;
        } else if (t1.getPriority() < t2.getPriority()) {
            return -1;
        } else {
            return 1;
        } //sorting max
    }
 }

例如,其他比较器可能是:

public class otherComparator{
    public int compare(Task t1, Task t2) {
        if (t1.getPriority() == t2.getPriority()) {
            return 0;
        } else if (t1.getPriority() < t2.getPriority()) {
            return 1;
        } else {
            return -1; 
        } //sorting min
    }
 }

我创建了我的HeapQueue 并添加了一些元素。

HeapQueue heap = new HeapQueue();
heap.setComparator(comparator);
Task t1 = new Task("a", 1);
Task t2 = new Task("b", 2);
Task t3 = new Task("c", 3);
Task t4 = new Task("d", 4);
System.out.println(heap.queue.toString());

结果是:

[d priority = 4, c  priority = 3, b priority = 2, a priority = 1]

    4
   / \
  3   2
 /
1 

没错。但是当我将Comparator 更改为otherComparator 时:

otherComparator newComparator = new otherComparator();
heap.setComparator(newComparator);
System.out.println(heap.queue.toString());

结果是:

[b priority = 2, c  priority = 3, d priority = 4, a priority = 1]

    2
   / \
  3   4
 /
1 

这是错误的。正确答案是这样的:

[a priority = 1, b priority = 2, c  priority = 3, d priority = 4]

    1
   / \
  2   3
 /
4 

我认为heapify() 函数有问题。但我找不到错误。有人可以帮忙吗?

【问题讨论】:

  • 可以分享一下你的swap函数吗?
  • @PhamTung 就在那里。
  • 嗯,如何将otherComparator 设置为堆的比较器?它没有实现相同的接口?
  • @PhamTrung 通过可以更改堆的任务比较器,通过 setComparator(),我可以更改比较器并通过 heapify() 重新构建堆。但它不起作用。
  • 我明白了,好的,我现在可以重现这个错误了 :)

标签: java algorithm data-structures heap priority-queue


【解决方案1】:

您只需将最大堆更改为最小堆,这破坏了整个堆结构!。

问题是,当你更改比较器时,调用heapify(0) 是不够的,因为例如这个结构:

             4
            / \
           3   2
          /
         1

heapify之后,1不会上移,因为在heapify(0)之后,程序会跳转到右孩子,也就是2,从那里我们无法到达1。

您可以创建另一个堆!

你可以看看这个answer,基本上,在改变比较器的时候,你只是破坏了堆结构!

【讨论】:

  • @AlexeySharov 是的,原因是,我的树结构不同,所以幸运地运行正确,但是heapify(0) 还不够,正如我在上面指出的那样!
  • 如果你这样做:heap.offer(t1);heap.offer(t2);heap.offer(t3);heap.offer(t4);你会遇到我的问题。我不能忘记堆肥,因为我在setComparator() 中有heapify()
  • 为什么heapify(0) 中的setComparator() 还不够?应该够了。
  • @AlexeySharov,不,冷静点 :),请重新阅读我的解释,因为 heapify 是基于堆中的结构正确的事实,但是在这个堆中,当您从 max 更改到最小堆,所有条件都被违反了,所以你不能再指望 heapify 了。
  • question 不是我的问题。 “也就是说,如果我保持数组不变,当我将一个元素附加到内部数组时会发生什么?”但是我想在更改比较器并添加新元素后更改我的堆。
【解决方案2】:

您需要做的不仅仅是向下堆化(就像在 heapify 中所做的那样 - 将节点向下推到树中)或向上堆(就像在 offer 方法中所做的那样 - 将节点拉到树上)。这些仅适用于分别在删除或添加时修复单个节点。堆的其余部分必须已经遵守堆规则。

您需要完全重新堆积结构。这可以通过从结构的底部开始并将每个根下推到正确的子树来在线性时间内完成。可以把它想象成为每个节点运行 heapify,子节点从完整树的尾部/末端开始。

rehapify arraynodes:
    for i from arraynodes.length / 2:
      heapifydown( arraynodes, i )

heapifydown 是你的 heapify 函数。

【讨论】:

  • 对于每个heapifyDown,我看到它需要从上到下移动O(log n),基本上是O(nlogn),但是,这不是一个紧密的界限,你能提供一个数学证明这是 O(n)?
  • 你的时间复杂度是 log(n) + log(n-1) + ... + log 1 = log(n*(n-1)*(n-2)...) = log(n!) ~ O(n log n)。更多info
  • 看起来只是这样,但每次传递对树的排序越来越多。 geeksforgeeks.org/g-fact-85
【解决方案3】:

我通过创建函数rebuild()来解决问题:

private void rebuild() {
    HeapQueue reHeap = new HeapQueue();
    reHeap.setComparator(comparator);
    for (int i = 0; i < queue.size(); i++) {
        reHeap.offer(queue.get(i));
    }
    queue = reHeap.queue;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-25
    • 2010-12-19
    • 1970-01-01
    • 2021-07-08
    • 1970-01-01
    • 2015-07-22
    • 2019-01-13
    • 2019-01-16
    相关资源
    最近更新 更多