通过二叉树结构实现的二叉堆

4
为了完成作业,我们被要求使用二叉堆实现优先队列,不能使用任何内置类。我已经成功地使用数组存储了排队的对象。然而,我想学习如何使用实际的树结构实现另一个队列,但在这样做时遇到了一些问题。 如何跟踪执行插入和删除的节点? 我尝试使用链表,每次插入一个节点时,将其附加到列表尾部——新的子节点从第一个链表节点开始添加,并从相反的端点删除。然而,在树中重新排列元素时,这种方法会失败,因为子节点会添加到错误的位置。 编辑: 也许我应该澄清一下,我不确定如何找到最后一个被占用和第一个未被占用的叶子节点。例如,我总是能够找到最后插入的叶子节点,但如果我要删除它,下一次删除项时我怎么知道要删除哪个叶子节点?插入也是同样的问题——当前叶子节点有两个孩子后,我怎么知道要跳转到哪个叶子节点?
3个回答

1

二叉堆的树实现使用完全二叉树[或几乎满树:每一层都是满的,除了最深的一层]。
您总是“知道”哪个是最后一个被占用的叶子 - 在那里进行删除[并且在更改后修改它是O(logn),因此不是问题],并且您总是“知道”哪个是第一个未被占用的叶子,在其中添加元素[同样,在更改后修改它也是O(logn)]。

算法思路很简单:
插入:将元素插入到第一个未被占用的叶子中,并使用堆化[向上筛选]将该元素放置在堆中的正确位置。

delete_min:用最后一个被占用的叶子替换第一个元素,然后删除最后一个被占用的叶子。然后,对堆进行堆化[向下筛选]。

编辑:请注意,delete() 可以用于任何元素,而不仅限于头部。但是,查找要用最后一个叶子替换的元素将是 O(n),这将使此操作变得昂贵。因此,delete() 方法 [除了头部] 通常不是堆数据结构的一部分。


谢谢!虽然我很感激您的快速回答,但它并不完全涉及我目前遇到的问题。我已经更新了我的问题,以便更清楚地阐明它。 - Wintersmith

0

我真的想做这件事已经有将近十年了。今天终于坐下来写了它。任何想要使用它的人都可以使用。我受到 Quora 创始人的启发,重新学习了堆。显然,在他的 Google 手机屏幕上被问到如何在一组 n 个点中找到 K 个接近的点时,他的答案是使用最大堆并存储 K 个值,并在堆的大小超过 K 后删除最大元素。这种方法非常简单,最坏情况是 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某处获取的代码,并进行了修改以用于两位数。如果我们转移到三位数,它将会出错,而且仅用于简单理解堆结构的方式。针对三位数的修复方法是将所有内容保持为3的倍数。 此外,要感谢Sesh Venugopal在Youtube上提供的关于堆数据结构的精彩教程。

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);
}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接