Java:如何逐层打印存储为数组的堆

时间:2016-04-03 13:17:28

标签: java heap

我有一个代表Max Heap的数组。例如

84 81 41 79 17 38 33 15 61 6

所以根是最大的。索引i处的每个中间层节点最多可以有两个子节点。他们将是2 * i + 1和2 * i + 2。

如何以逐级方式打印此堆?像

                             84(0)

                 81(1)                  41(2)

            79(3)        17(4)     38(5)       33(6) 

       15(7)    61(8)         6(9)

数组中每个元素的索引都显示在paranthesis中以便澄清。我不必打印索引。我认为它类似于按级别顺序打印BST但是在这里,堆存储在一个数组而不是列表中,这使得它有点棘手!

6 个答案:

答案 0 :(得分:4)

试试这段代码:

public class NewClass56 {
public static void main(String args[]){

    int a[] = new int[] {84 ,81 ,41 ,79 ,17 ,38 ,33 ,15 ,61 ,6};

    for(int i=0;i<10;i++){
        for(int j=0;j<Math.pow(2,i)&&j+Math.pow(2,i)<10;j++){
            System.out.print(a[j+(int)Math.pow(2,i)-1]+" ");

        }
        System.out.println();
    }



    }
}

如果您有n个号码,请将10替换为n

然后你想要空格然后尝试这个代码:

public class NewClass56 {
public static void main(String args[]){

    int a[] = new int[] {84 ,81 ,41 ,79 ,17 ,38 ,33 ,15 ,61 ,6};
    StringBuilder sb = new StringBuilder();
    int max=0;
    for(int i=0;i<10;i++){
        for(int j=0;j<Math.pow(2,i)&&j+Math.pow(2,i)<10;j++){

            if(j>max){
                max=j;
            }
        }

    }

    for(int i=0;i<10;i++){
        for(int j=0;j<Math.pow(2,i)&&j+Math.pow(2,i)<10;j++){

            for(int k=0;(k<max/((int)Math.pow(2, i)));k++){
                sb.append(" ");
            }
            sb.append(a[j+(int)Math.pow(2,i)-1]+" ");

        }
        sb.append("\n");

    }



    System.out.println(sb.toString());

}
}

答案 1 :(得分:3)

还有另一种打印堆的方法。想象一下,您的结构具有以下索引(索引0是监护人,等于Integer.MIN_VALUE,此处未显示):

          1
      /      \
    2          3
  /    \       / \
 4     5      6   7
/ \    /\     /\  /\
8 9  10 11 12 13 14 15 

它由数字数组代表。你在这看到什么?对,1, 3, 7, 15。如果将其增加1,则为2, 4, 8, 16

这些数字是什么?它只是2^level。其中level的级别为1到4。

我们如何计算这个水平?它与基数2的索引的对数。

以下是实现此方法的代码(请参阅dump函数):

package org.solutions;
import java.util.ArrayList;
import java.util.Arrays;

class Heap {
    public ArrayList<Integer> arr;
    public Heap() {
        this.arr = new ArrayList<>();
        arr.add(Integer.MIN_VALUE); // add guardian
    }

    public void add(int x) {
        int i = arr.size();
        arr.add(x);
        while(arr.get(i) < arr.get(i / 2)) {
            swap(i, i/2);
            i = i / 2;
        }
    }

    private void swap(int i, int j) {
        int tmp = arr.get(i);
        arr.set(i, arr.get(j));
        arr.set(j, tmp);
    }

    public void dump() {
        int height = log2(arr.size()) + 1;

        for (int i = 1, len = arr.size(); i < len; i++) {
            int x = arr.get(i);
            int level = log2(i) + 1;
            int spaces = (height - level + 1) * 2;

            System.out.print(stringOfSize(spaces, ' '));
            System.out.print(x);

            if((int)Math.pow(2, level) - 1 == i) System.out.println();
        }
    }

    private String stringOfSize(int size, char ch) {
        char[] a = new char[size];
        Arrays.fill(a, ch);
        return new String(a);
    }

    // log with base 2
    private int log2(int x) {
        return (int)(Math.log(x) / Math.log(2)); // = log(x) with base 10 / log(2) with base 10
    }
}

public class Main {

    public static void main(String[] args) {
        Heap heap = new Heap();
        heap.add(30);
        heap.add(2);
        heap.add(15);
        heap.add(10);
        heap.add(31);
        heap.dump();
    }
}

答案 2 :(得分:1)

划分&amp;征服。创建子树的行列表,连接行并在子树的根节点前面添加String。还要确保线条长度相同,并且所有线条都居中:

static String pad(String s, int lengthRigth, int length) {
    StringBuilder sb = new StringBuilder();

    for (int i = length - lengthRigth - s.length(); i > 0; i--) {
        sb.append(' ');
    }

    sb.append(s);

    for (int i = 0; i < lengthRigth; i++) {
        sb.append(' ');
    }

    return sb.toString();
}

static StringBuilder withSpacesAppended(String s, int spaceCount) {
    StringBuilder sb = new StringBuilder(s.length()+spaceCount).append(s);
    for (int i = 0; i < spaceCount; i++) {
        sb.append(' ');
    }
    return sb;
}

static void joinLists(List<String> list1, List<String> list2) {
    int i;
    final int size = list2.size();
    for (i = 0; i < size; i++) {
        list1.set(i, withSpacesAppended(list1.get(i), 2).append(list2.get(i)).toString());
    }
}

static List<String> createTreeStrings(int index, int[] array) {
    int child1 = 2 * index + 1;
    int child2 = 2 * index + 2;
    if (child1 >= array.length) {
        return new ArrayList<>(Collections.singletonList(toText(index, array)));
    } else {
        List<String> childList1 = createTreeStrings(child1, array);
        if (child2 < array.length) {
            joinLists(childList1, createTreeStrings(child2, array));
        }
        String text = toText(index, array);
        int currentLength = childList1.get(0).length();

        if (currentLength >= text.length()) {
            text = pad(text, (currentLength - text.length()) / 2, currentLength);
        } else {
            for (int i = 0, size = childList1.size(); i < size; i++) {
                childList1.set(i, pad(childList1.get(i), (currentLength - text.length()) / 2, currentLength));
            }
        }

        childList1.add(0, text);
        return childList1;
    }
}

static String toText(int index, int[] array) {
    return Integer.toString(array[index]) + '(' + index + ')';
}

使用示例:

createTreeStrings(0, new int[]{84, 81, 41, 79, 17, 38, 33, 15, 61, 6}).forEach(System.out::println);
createTreeStrings(0, new int[]{Integer.MAX_VALUE, 6}).forEach(System.out::println);

答案 3 :(得分:1)

接受的答案创建了许多新行,并且在显示时忽略了最后一个元素。因此,共享优化的代码,

public void display() {
        // n is number of elements in the heap
        // It can be further optimised by calculating height of the heap
        // and looping i only till height of the tree
        for (int i = 0; i <= n / 2; i++) {
            for (int j = 0; j < Math.pow(2, i) && j + Math.pow(2, i) <= n; j++) { // Each row has 2^n nodes
                System.out.print(heap[j + (int) Math.pow(2, i) - 1] + " ");
            }
            System.out.println();
        }
    }

答案 4 :(得分:1)

现有的解决方案对我不起作用,因此这里的操作方式略有不同,我认为这也更易于理解。此外,它不使用任何外部库。请注意,这是假设数组的第一个点为空,因为通常基于数组的堆会跳过array [0]。这将根据输入大小自动确定级别数,该大小应该是堆中的节点数。它将在每个空白处添加--(例如,如果您有13节点堆,则最后两个节点将显示为空白)。

private void printHeap(int[] heap, size) {
    int maxDepth = (int) (Math.log(size) / Math.log(2));  // log base 2 of n

    StringBuilder hs = new StringBuilder();  // heap string builder
    for(int d = maxDepth; d >= 0; d--) {  // number of layers, we build this backwards
        int layerLength = (int) Math.pow(2, d);  // numbers per layer

        StringBuilder line = new StringBuilder();  // line string builder
        for(int i = layerLength; i < (int) Math.pow(2, d + 1); i++) {
            // before spaces only on not-last layer
            if(d != maxDepth) {
                line.append(" ".repeat((int) Math.pow(2, maxDepth - d)));
            }
            // extra spaces for long lines
            int loops = maxDepth - d;
            if(loops >= 2) {
                loops -= 2;
                while(loops >= 0) {
                    line.append(" ".repeat((int) Math.pow(2, loops)));
                    loops--;
                }
            }

            // add in the number
            if(i <= size) {
                line.append(String.format("%-2s", heap[i]));  // add leading zeros
            } else {
                line.append("--");
            }

            line.append(" ".repeat((int) Math.pow(2, maxDepth - d)));  // after spaces
            // extra spaces for long lines
            loops = maxDepth - d;
            if(loops >= 2) {
                loops -= 2;
                while(loops >= 0) {
                    line.append(" ".repeat((int) Math.pow(2, loops)));
                    loops--;
                }
            }
        }
        hs.insert(0, line.toString() + "\n");  // prepend line
    }
    System.out.println(hs.toString());
}

示例输入:

int[] heap = new int[]{0, 84, 81, 41, 79, 17, 38, 33, 15, 61, 6};
int size = heap.length-1 = 10

示例输出:

           84           
     81          41     
  79    17    38    33  
15 61 6  -- -- -- -- -- 

如果有必要,您应该能够相当容易地将其更改为toString方法。如果要使用3位数字,则必须修改间距,如果有人要求,我可以使用修改后的代码进行编辑。

答案 5 :(得分:0)

如果你想将它存储在一个列表列表中(级别顺序),只需将它的每次幂分割为一次,如1,2,4,8,16 ..

    private ArrayList<ArrayList<Integer>> heapParse(int[] arr) {

    ArrayList<ArrayList<Integer>> heap = new ArrayList<ArrayList<Integer>>();
    int j=-1;
    for (int i=0; i< arr.length;i++){
        if(isPowerOfTwo(i+1)){                
            heap.add(new ArrayList<>());
            j++;
        }
        heap.get(j).add(arr[i]);
    }       
    return heap;
    }

    private boolean isPowerOfTwo(int i){
       return (i&(i-1))==0;
    }