简体   繁体   中英

Find heap depth faster than O(n^2)

Help me optimize the algorithm. I have a heap in the array. Each number in the array indicates a parent. Root is -1. I need to find the depth of heap. Example:

Array is 4 -1 4 1 1

heap_structure

The answer is 3.

It's my code

static int findMax(int[] mas) {
    int a[] = new int[mas.length];
    a[pos] = 1;
    int max = 0;

    for (int j = 0; j < mas.length; j++) {
        for (int i = 0; i < a.length; i++) {
            if (a[i] == 0 && a[mas[i]] != 0) {
                a[i] = a[mas[i]] + 1;
                if (a[i] > max)
                    max = a[i];
            }
        }
    }
    return max;
}

Where pos - position of root.

I also solved this problem with recursion. But tests give me "Time limit exceeded" also.

static class Node {
        static int nodesCount = 0;

        int val;
        int deep;
        List<Node> childrens = new ArrayList<>();
        static Set<Integer> deeps = new HashSet<>();

        public Node(int val, int deep) {
            this.val = val;
            this.deep = deep;
            deeps.add(deep);
            nodesCount++;
        }

        public List<Node> getChildrens() {
            return childrens;
        }

        public int getDeep() {
            return deep;
        }
    }
static int findMax(int [] mas){
    Node head = null;
    for (int i = 0; i < mas.length; i++) {
        if (mas[i] == -1)
            head = new Node(i, 1);
    }
    fillChildren(head, mas);
    return Node.deeps.stream().max(Comparator.naturalOrder()).get();
}

private static void fillChildren(Node head, int[] mas) {
    for (int i = 0; i < mas.length; i++) {
        if (mas[i] == head.val) {
            Node child = new Node(i, head.getDeep() + 1);
            head.getChildrens().add(child);
            fillChildren(child, mas);
        }
    }
}

To substantiate Matej's answer, here is pseudocode.

  • associate a D field to every node,

  • initialize all D to -1,

  • from every node, follow the parent chain until your reach a node with non-negative D,

  • if the root is reached, set its D to 0,

  • tracing the chain backward, update the D's increasingly.

A chain traversal stops on the first non-negative node met, and all intermediate nodes become non-negative. So the negative nodes are only visited once and this justifies the O(n) behavior.

Updating all nodes in a chain is crucial, otherwise the same nodes can be visited several times. In the worst case, that could lead to O(n²) operations.

It is worth to note that the algorithm requires a stack to make the backward traversal possible. In the worst case, the stack depth can reach n, adding extra O(n) storage space (or the risk of a stack overflow is no care is taken).

A better option might be to use the D field of the traversed nodes to store the "return index" and form a temporary backward chain.

Remember depths of visited nodes in an array. Start traversing from first input element to its root and on backtrack store depths to visited nodes array. On each jump from child to parent check if the depth is already calculated. If yes, you don't have to go that route for second time and can use the pre-calculated value directly. Will be O(n).

All we need is a map that indicates where the children are, then perform a breadth first search. As we can see in the output below, the complexity is O(n).

 function f(A){ let map = {}; A.map((parentIdx, childIdx) => { if (map[parentIdx]) map[parentIdx].push(childIdx); else map[parentIdx] = [childIdx]; }); let maxDepth = 0; let queue = [[-1, 0]]; while (queue.length){ const [node, depth] = queue.shift(); console.log( `node: ${node}, children: [${map[node] || ''}], ` + `current depth: ${depth}`); maxDepth = Math.max(maxDepth, depth); if (map[node]) for (let child of map[node]) queue.push([child, depth + 1]); } return maxDepth; } var arr = [4, -1, 4, 1, 1]; console.log(f(arr)); 

While you can find the max depth in O(n) based on your array of parents, as described by Matej, it may be worthwile to transform that array to a tree structure that's more easily navigable in the direction from parent to child node. You already do this with your Node class, but your fillChildren method has complexity O(n²) as you have to scan the entire array again and again to find the childrens of the current node.

Instead, you could create a Map<Integer, Set<Integer>> , mapping nodes to their child nodes. This way, you can create the entire tree in a single loop over the array, in O(n).

static Map<Integer, Set<Integer>> makeTree(int[] parents) {
    Map<Integer, Set<Integer>> tree = new HashMap<>();
    for (int i = 0; i < parents.length; i++) {
        tree.computeIfAbsent(parents[i], x -> new HashSet<>()).add(i);
    }
    return tree;
}

Then, you can easily write a recursive function for getting the maximum depth of the tree:

static int depth(Map<Integer, Set<Integer>> tree, int node) {
    return tree.containsKey(node) 
            ? 1 + tree.get(node).stream().mapToInt(n -> depth(tree, n)).max().getAsInt()
            : 0;
}

The complexity for both steps is O(n) if the array represents a tree, as each node is visited exactly once. If the array could also show a directed graph, you should keep track of nodes already visited before. For your example, use it like this:

int[] array = {4, -1, 4, 1, 1};
Map<Integer, Set<Integer>> tree = makeTree(array);
System.out.println(depth(tree, -1)); // 3

As with any recursive algorithm, this may hit the maximum recursion depth if the tree is very, very deep. It can be rewritten in an iterative fashion using a Stack , but won't be quite as concise then.

Stack<Integer> stack = new Stack<>();
stack.add(-1);
int[] depth = new int[array.length];
while (! stack.isEmpty()) {
    int node = stack.pop();
    for (Integer child : tree.getOrDefault(node, Collections.emptySet())) {
        depth[child] = node == -1 ? 1 : depth[node] + 1;
        stack.add(child);
    }
}
System.out.println(IntStream.of(depth).max().getAsInt());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM