二进制堆通过二叉树结构实现

时间:2011-09-30 10:24:50

标签: java data-structures

对于赋值,我们被指示创建一个通过二进制堆实现的优先级队列,而不使用任何内置类,我已经成功地通过使用数组来存储排队的对象。但是,我有兴趣学习如何使用实际的树结构来实现另一个队列,但是这样做我遇到了一些问题。

如何跟踪我将执行插入和删除的节点?我尝试使用链接列表,它在插入每个节点时附加新节点 - 从第一个列表节点,并从另一端删除。但是,当元素在树中重新排列时会出现这种情况,因为孩子被添加到了错误的位置。

编辑:也许我应该澄清一下 - 我不确定我怎么能找到最后占用的和第一个未被占用的叶子。例如,我总是能够告诉最后插入的叶子,但是如果我要删除它,当下次删除项目时,我怎么知道要删除哪个叶子?插入是一样的 - 在当前叶子有两个孩子占了之后,我怎么知道哪个叶子跳到下一个呢?

3 个答案:

答案 0 :(得分:1)

二进制堆的树实现使用complete tree [或几乎完整的树:每个级别都是满的,除了最深的一个]。
你总是'知道'哪一个是最后一个被占用的叶子 - 你从哪里删除[并在改变之后修改它是O(logn)所以它不是问题],你总是'知道'哪个是第一个非占用叶子,在其中添加元素[并再次修改它也是O(logn)后更改]。

算法理念很简单:
insert:将元素插入第一个非占用的叶子,并使用heapify [sift up]将此元素放到堆中的正确位置。

delete_min:将第一个元素替换为最后一个被占用的叶子,并删除最后一个被占用的叶子。然后,堆积[筛选]堆。

编辑:请注意,delete()可以对任何元素进行,而不仅仅是头部,但是 - 找到要用最后一个叶子替换的元素将是O(n ),这将使这个op昂贵。因此,delete()方法[除了head]之外通常不是堆数据结构的一部分。

答案 1 :(得分:0)

我真的很想这样做近十年。最后我今天坐下来写了它。任何想要它的人都可以使用它。我受到Quora创始人的启发,重新学习Heap。显然他被问到你怎么会发现K近他的答案是使用Max Heap并存储K值并在堆大小超过K后删除最大元素。这种方法非常简单,最糟糕的情况是他的答案是在他的谷歌手机屏幕上的一组n点。在大多数排序情况下,nlog K优于n ^ 2.这是代码。

import java.util.ArrayList;
import java.util.List;

/**
 * @author Harish R
 */
public class HeapPractise<T extends Comparable<T>> {

    private List<T> heapList;

    public List<T> getHeapList() {
        return heapList;
    }

    public void setHeapList(List<T> heapList) {
        this.heapList = heapList;
    }

    private int heapSize;

    public HeapPractise() {
        this.heapList = new ArrayList<>();
        this.heapSize = heapList.size();
    }

    public void insert(T item) {
        if (heapList.size() == 0) {
            heapList.add(item);
        } else {
            siftUp(item);
        }

    }

    public void siftUp(T item) {
        heapList.add(item);
        heapSize = heapList.size();
        int currentIndex = heapSize - 1;
        while (currentIndex > 0) {
            int parentIndex = (int) Math.floor((currentIndex - 1) / 2);
            T parentItem = heapList.get(parentIndex);
            if (parentItem != null) {
                if (item.compareTo(parentItem) > 0) {
                    heapList.set(parentIndex, item);
                    heapList.set(currentIndex, parentItem);
                    currentIndex = parentIndex;
                    continue;
                }
            }
            break;
        }
    }

    public T delete() {
        if (heapList.size() == 0) {
            return null;
        }
        if (heapList.size() == 1) {
            T item = heapList.get(0);
            heapList.remove(0);
            return item;
        }
        return siftDown();
    }

    public T siftDown() {
        T item = heapList.get(0);
        T lastItem = heapList.get(heapList.size() - 1);
        heapList.remove(heapList.size() - 1);
        heapList.set(0, lastItem);
        heapSize = heapList.size();
        int currentIndex = 0;
        while (currentIndex < heapSize) {
            int leftIndex = (2 * currentIndex) + 1;
            int rightIndex = (2 * currentIndex) + 2;
            T leftItem = null;
            T rightItem = null;
            int currentLargestItemIndex = -1;
            if (leftIndex <= heapSize - 1) {
                leftItem = heapList.get(leftIndex);
            }
            if (rightIndex <= heapSize - 1) {
                rightItem = heapList.get(rightIndex);
            }
            T currentLargestItem = null;
            if (leftItem != null && rightItem != null) {

                if (leftItem.compareTo(rightItem) >= 0) {
                    currentLargestItem = leftItem;
                    currentLargestItemIndex = leftIndex;
                } else {
                    currentLargestItem = rightItem;
                    currentLargestItemIndex = rightIndex;
                }
            } else if (leftItem != null && rightItem == null) {
                currentLargestItem = leftItem;
                currentLargestItemIndex = leftIndex;
            }
            if (currentLargestItem != null) {
                if (lastItem.compareTo(currentLargestItem) >= 0) {
                    break;
                } else {
                    heapList.set(currentLargestItemIndex, lastItem);
                    heapList.set(currentIndex, currentLargestItem);
                    currentIndex = currentLargestItemIndex;
                    continue;
                }
            }
        }
        return item;

    }

    public static void main(String[] args) {
        HeapPractise<Integer> heap = new HeapPractise<>();

        for (int i = 0; i < 32; i++) {
            heap.insert(i);
        }
        System.out.println(heap.getHeapList());
        List<Node<Integer>> nodeArray = new ArrayList<>(heap.getHeapList()
                .size());
        for (int i = 0; i < heap.getHeapList().size(); i++) {
            Integer heapElement = heap.getHeapList().get(i);
            Node<Integer> node = new Node<Integer>(heapElement);
            nodeArray.add(node);
        }
        for (int i = 0; i < nodeArray.size(); i++) {
            int leftNodeIndex = (2 * i) + 1;
            int rightNodeIndex = (2 * i) + 2;
            Node<Integer> node = nodeArray.get(i);
            if (leftNodeIndex <= heap.getHeapList().size() - 1) {
                Node<Integer> leftNode = nodeArray.get(leftNodeIndex);
                node.left = leftNode;
            }
            if (rightNodeIndex <= heap.getHeapList().size() - 1) {
                Node<Integer> rightNode = nodeArray.get(rightNodeIndex);
                node.right = rightNode;
            }
        }
        BTreePrinter.printNode(nodeArray.get(0));
    }
}

public class Node<T extends Comparable<?>> {
    Node<T> left, right;
    T data;

    public Node(T data) {
        this.data = data;
    }
}

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class BTreePrinter {

    public static <T extends Comparable<?>> void printNode(Node<T> root) {
        int maxLevel = BTreePrinter.maxLevel(root);

        printNodeInternal(Collections.singletonList(root), 1, maxLevel);
    }

    private static <T extends Comparable<?>> void printNodeInternal(
            List<Node<T>> nodes, int level, int maxLevel) {
        if (nodes.isEmpty() || BTreePrinter.isAllElementsNull(nodes))
            return;

        int floor = maxLevel - level;
        int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0)));
        int firstSpaces = (int) Math.pow(2, (floor)) - 1;
        int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1;

        BTreePrinter.printWhitespaces(firstSpaces);

        List<Node<T>> newNodes = new ArrayList<Node<T>>();
        for (Node<T> node : nodes) {
            if (node != null) {
                String nodeData = String.valueOf(node.data);
                if (nodeData != null) {
                    if (nodeData.length() == 1) {
                        nodeData = "0" + nodeData;
                    }
                }
                System.out.print(nodeData);
                newNodes.add(node.left);
                newNodes.add(node.right);
            } else {
                newNodes.add(null);
                newNodes.add(null);
                System.out.print("  ");
            }

            BTreePrinter.printWhitespaces(betweenSpaces);
        }
        System.out.println("");

        for (int i = 1; i <= endgeLines; i++) {
            for (int j = 0; j < nodes.size(); j++) {
                BTreePrinter.printWhitespaces(firstSpaces - i);
                if (nodes.get(j) == null) {
                    BTreePrinter.printWhitespaces(endgeLines + endgeLines + i
                            + 1);
                    continue;
                }

                if (nodes.get(j).left != null)
                    System.out.print("//");
                else
                    BTreePrinter.printWhitespaces(1);

                BTreePrinter.printWhitespaces(i + i - 1);

                if (nodes.get(j).right != null)
                    System.out.print("\\\\");
                else
                    BTreePrinter.printWhitespaces(1);

                BTreePrinter.printWhitespaces(endgeLines + endgeLines - i);
            }

            System.out.println("");
        }

        printNodeInternal(newNodes, level + 1, maxLevel);
    }

    private static void printWhitespaces(int count) {
        for (int i = 0; i < 2 * count; i++)
            System.out.print(" ");
    }

    private static <T extends Comparable<?>> int maxLevel(Node<T> node) {
        if (node == null)
            return 0;

        return Math.max(BTreePrinter.maxLevel(node.left),
                BTreePrinter.maxLevel(node.right)) + 1;
    }

    private static <T> boolean isAllElementsNull(List<T> list) {
        for (Object object : list) {
            if (object != null)
                return false;
        }

        return true;
    }

}

请注意,BTreePrinter是我在Stackoverflow中长期使用的代码,我修改为使用2位数字。如果我们移动到3位数字它将被破坏它只是为了简单理解堆结构看起来。对3位数字的修正是将所有内容保持为3的倍数。 也应该归功于Sesh Venugopal在Youtube上关于Heap数据结构的精彩教程

答案 2 :(得分:0)

public class PriorityQ<K extends Comparable<K>> {
private class TreeNode<T extends Comparable<T>> {
    T val;
    TreeNode<T> left, right, parent;
    public String toString() {
        return this.val.toString();
    }
    TreeNode(T v) {
        this.val = v;
        left = null;
        right = null;
    }
    public TreeNode<T> insert(T val, int position) {
        TreeNode<T> parent = findNode(position/2);
        TreeNode<T> node = new TreeNode<T>(val);
        if(position % 2 == 0) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        node.parent = parent;
        heapify(node);
        return node;
    }
    private void heapify(TreeNode<T> node) {
        while(node.parent != null && (node.parent.val.compareTo(node.val) < 0)) {
            T temp = node.val;
            node.val = node.parent.val;
            node.parent.val = temp; 
            node = node.parent;
        }
    }       
    private TreeNode<T> findNode(int pos) {
        TreeNode<T> node = this;
        int reversed = 1;
        while(pos > 0) {
            reversed <<= 1;
            reversed |= (pos&1);                
            pos >>= 1;
        }
        reversed >>= 1;

        while(reversed > 1) {
            if((reversed & 1) == 0) {
                node = node.left;
            } else {
                node = node.right;
            }
            reversed >>= 1;
        }
        return node;
    }
    public TreeNode<T> remove(int pos) {
        if(pos <= 1) {
            return null;
        }
        TreeNode<T> last = findNode(pos);
        if(last.parent.right == last) {
            last.parent.right = null;
        } else {
            last.parent.left = null;
        }
        this.val = last.val;
        bubbleDown();
        return null;            
    }   

    public void bubbleDown() {
        TreeNode<T> node = this;            
        do {
            TreeNode<T> left = node.left;
            TreeNode<T> right = node.right;
            if(left != null && right != null) {
                T max = left.val.compareTo(right.val) > 0 ? left.val : right.val;
                if(max.compareTo(node.val) > 0) {
                    if(left.val.equals(max)) {
                        left.val = node.val;
                        node.val = max;                         
                        node = left;
                    } else {
                        right.val = node.val;
                        node.val = max;
                        node = right;
                    } 
                } else {
                    break;
                }
            } else if(left != null) {
                T max = left.val;
                if(left.val.compareTo(node.val) > 0) {
                    left.val = node.val;
                    node.val = max;                         
                    node = left;
                } else {
                    break;
                }
            } else {
                break;
            }
        } while(true);
    }
}
private TreeNode<K> root;
private int position;
PriorityQ(){ 
    this.position = 1;
}
public void insert(K val) {
    if(val == null) {
        return;
    }
    if(root == null) {
        this.position = 1;
        root = new TreeNode<K>(val);
        this.position++;
        return ;
    }
    root.insert(val, position);
    position++;     
}
public K remove() {
    if(root == null) {
        return null;
    }
    K val = root.val;
    root.remove(this.position-1);
    this.position--;
    if(position == 1) {
        root = null;
    }
    return val;
}
public static void main(String[] args) {
    PriorityQ<Integer> q = new PriorityQ<>();
    System.out.println(q.remove());
    q.insert(1);
    q.insert(11);
    q.insert(111);
    q.insert(1111);
    q.remove();
    q.remove();
    q.remove();
    q.remove();
    q.insert(2);
    q.insert(4);
}

}