如何垂直打印优先级队列

时间:2016-12-29 00:25:00

标签: java algorithm

我想以树格式打印队列。我意识到转换为数组然后打印或使用迭代器打印遍历,但我希望我的输出顺序如下。

输入:键的优先级队列1 - 10

                      1
                   /     \
                 2         3
               /   \     /   \
              4     5   6     7
             /              /   \ 
            8              9     10

输出

1
.2
..4
...8
..5
.3
..6
..7
...9
...10

所以基本上我试图将输出表示为树格式。是否有任何内置的java方法可以为我做到这一点?

我尝试通过为每个树级别创建新数组来解决这些问题但我意识到它不是最佳的,特别是如果队列具有大的大小。

4 个答案:

答案 0 :(得分:1)

您想要的输出格式称为Binary tree的{​​{3}}。要获得该输出,您必须将数字存储为二进制树(不一定是二进制搜索树),这需要O(nlogn)次,然后您可以使用预订遍历轻松计算此订单。整个计划的时间复杂度为O(nlogn)

答案 1 :(得分:1)

优先级队列的最常见实现是将其存储在binary heap中,这是一个简单的数组,其中索引 i 的项目的子项位于索引2 i +1和2 i +2(假设数组从0开始索引)。这不是唯一可能的优先级队列实现,但它简单,实用,并且在大多数情况下效率最高,除非优先级队列非常大或者您需要其他原语快速(例如合并两个优先级队列)。

但是,Java不要求标准库使用此(或任何)算法,因此它不会公开有关优先级队列的任何内部信息。 java.util.PriorityQueue被指定为抽象数据类型(ADT);实现者可以自由地使用他们认为方便的任何算法和底层存储,并且不可能直接访问底层数组(如果有的话),也不可能找到项目的子项。

因此,如果您需要执行需要访问优先级队列实现的操作,则需要实现自己的优先级队列。这种实现有无数的例子,简单的网络搜索应该找到几个[注1]

相比之下,C ++标准库具有二进制堆原语的显式实现(make_heappush_heappop_heap),其priority queue class是一个随机的适配器 - 使用二进制堆原语的可访问容器,因此它确实提供了对底层数组的直接访问。

这样就可以在C ++中轻松解决您的问题,如果您在Java中实现了二进制堆,则可以轻松调整以下代码片段以生成所需的输出。这是一个简单的递归深度优先遍历二进制堆中的隐式树。

#include <iostream>
#include <queue>
#include <vector>

template<typename V, typename C = std::vector<V>, typename Comp = std::less<V>>
class PrintablePriorityQueue : public std::priority_queue<V, C, Comp> {

  public:
    /* A real implementation would have more constructors :) */
    PrintablePriorityQueue() {}

    std::ostream& print(std::ostream& out, size_t i = 0, size_t depth = 0) const;
};

/* I left out the operator<< overload, which just calls print. With that,
   print would be made protected.
 */

template<typename V, typename C, typename Comp>
std::ostream& PrintablePriorityQueue<V, C, Comp>::print(std::ostream& out, size_t i, size_t depth) const {
  if (i < this->size()) {
    for (size_t j = depth; j; --j) out << '.';
    out << this->c[i] << '\n'; /* c is the underlying container */
    print(out, 2*i + 1, depth + 1);
    print(out, 2*i + 2, depth + 1);
  }
  return out;
}

一个简单的驱动程序:

#include <cstdlib>
int main(int argc, char** argv) {
  PrintablePriorityQueue<int, std::vector<int>, std::greater<int>> queue;
  for (int i = 1; i < argc; ++i) queue.push(atoi(argv[i]));
  queue.print(std::cout);
  return 0;
}

产生以下输出:

$ ./pq 1 9 4 7 2 8 3 5 6 10
1
.2
..5
...9
...6
..7
...10
.3
..8
..4

请注意,二进制堆始终从左到右填充,因此图表的形状与您的样本不同。

注释

  1. 就个人而言,我建议您查看Robert Sedgewick's excellent text book,特别是如果它是您正在学习的课程的教科书,但任何好的算法教科书都可能会描述该算法。

答案 2 :(得分:0)

没有实际的方法可以为您格式化代码。很可能你必须找到自己的方式来格式化信息,或者使用for循环,或类似的东西。快乐的密码':)

答案 3 :(得分:0)

我认为对于这种树的预订遍历递归是合适的。

例如(在C / C ++中)如果您将node定义为

typedef struct node
{
    int data;
    node * left;
    node * right;
};

输出的递归函数可以(使用C ++标准流输出),如

void recursiveOut(node * root, int level = 0)
// root - pointer to the top of tree (subtree)
// level - level of recursion (equal the number of . before value)
{
    // check that node exists
    if (!root)
        return;
    // output data of current node (i.e. root)
    for (int i = 0; i < level; i++)
    {
        std::cout << ".";
    }
    std::cout << root->data << std::endl;
    // process the left branch
    recursiveOut(root->left, level + 1); // +1 for adding level
    // process the right branch
    recursiveOut(root->right, level + 1); // +1 for adding level
}

可以很容易地调用如下

int main(void)
{
    node * root = NULL;
    // init the tree
    for (int i = 1; i <= 10; i++)
        add(root, i);   // I suppose you have some functions to make tree
    recursiveOut(root); // output the tree
    return 0;
}

请注意,我的解决方案root在函数开头检查一次(if (!root)等于if (root == NULL)),以防止对不存在的对象进行操作。< / p>

或许,您会发现在递归调用处理相应的子树之前检查root->leftroot->right很有用,例如

 if( root->left )
      recursiveOut(root->left, level + 1);