【问题标题】:Binary Heap Implemented via a Binary Tree Structure通过二叉树结构实现的二叉堆
【发布时间】:2011-11-28 08:55:48
【问题描述】:

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

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

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

【问题讨论】:

    标签: java data-structures


    【解决方案1】:

    二叉堆的树实现使用complete tree [或几乎满的树:每一层都是满的,除了最深的一层]。
    您总是“知道”哪个是最后一个被占用的叶子-您从中删除[并在更改后对其进行修改是 O(logn),因此这不是问题],并且您始终“知道”哪个是第一个非- 占用的叶子,在其中添加元素[再次修改它也是 O(logn) 在它改变之后]。

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

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

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

    【讨论】:

    • 谢谢!但是,虽然我很欣赏您的及时回答,但这与我目前遇到的问题并不完全相关。我已经更新了我上面的问题以澄清一点。
    【解决方案2】:

    我真的很想做这个将近十年。今天终于坐下来写了。任何想要它的人都可以使用它。我受到Quora创始人的启发重新学习Heap。显然他被问到你如何在附近找到K在他的谷歌手机屏幕上的一组 n 点中的点。显然他的答案是使用 Max Heap 并存储 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 中使用的代码,我修改为使用 2 位数字。如果我们移动到 3 位数字,它将被破坏,它只是为了简单理解堆结构的方式看起来。对于 3 位数字的解决方法是将所有内容保持为 3 的倍数。 还要感谢 Sesh Venugopal 在 Youtube 上关于堆数据结构的精彩教程

    【讨论】:

      【解决方案3】:
      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);
      }
      

      }

      【讨论】:

        猜你喜欢
        • 2012-01-09
        • 2013-08-16
        • 1970-01-01
        • 1970-01-01
        • 2014-10-01
        • 2011-01-02
        • 1970-01-01
        • 1970-01-01
        • 2023-01-11
        相关资源
        最近更新 更多