2

I wish to iterate over a sorted array in the order that a breadth first traversal would give if I put the array into a binary tree and performed a BFT on that (which is how I currently achieve this). Obviously this involves additional memory overhead since you need to build and store the array again in the binary tree. I know this should be similar to a binary search but I can't quite get the ordering right.

Here's how I currently achieve this:

BTree bst = sortedArrayToBST(array);
Queue<BTree> queue = new LinkedList<BTree>();
queue.add(bst);
while(!queue.isEmpty()) {
    BTree node = queue.remove();
    System.out.println(node.data);
    if(node.left != null) queue.add(node.left);
    if(node.right != null) queue.add(node.right);
}

Here's what I have currently (but this obviously gives the wrong ordering):

public void bstIterate(int[] array, int start, int end) {
    if(array.length == 0) return;
    int med = array.length /2;
    System.out.println(array[med]);
    bstIterate(array,start,mid);
    bstIterate(array,mid+1,array.length);
}

Can this be done without extra memory overhead, or do I have to store the items in a stack, vector or queue, and if so, how and would it require less memory than a binary tree?

Jonno_FTW
  • 8,601
  • 7
  • 58
  • 90
  • You dont need to create a binary tree to do BFS but you need a queueu. – DarthVader May 27 '13 at 07:01
  • 1
    You did not say how they are supposed to be put in binary tree. Is it heap, is it in any order? (In such case just return values in any order you want) Is it binary search tree? Is it balanced. – Alpedar May 27 '13 at 07:10
  • Sorry, yes the BST produced by the `sortedArraytoBST` does produce a balanced binary tree. – Jonno_FTW May 28 '13 at 02:58

2 Answers2

2

I hope your sortedArrayToBST method is building a balanced binary tree from the given array. In that case the method you tried to implement will mimic a DFS (Depth first search) iteration over a BST. But your implementation is buggy, a correct implementation will look like this:

void bstIterate(int[] array, int start, int end) {
  if (start > end) return;
  int mid = start + (end - start) / 2; //it should not be array.lenght/2 because we are not updating the array at any point

  bstIterate(array, start, mid-1); // mid is already printed so we no longer need it
  bstIterate(array, mid+1, end);
}

//Now Call the method from you main
bstIterate(array, 0, array.length-1); //it should be array.length-1, as it is the last index of last element of the array

But from the question title I understand you are looking for BFS traversal over a sorted array by assuming the array as balanced binary tree.

Lets say our sorted array is this {1, 2, 3, 4, 5, 6, 7}. In that case a balanced BST will look like this:

    4
   / \
  2   6
 / \ / \
1  3 5  7

The BFS traversal on the above tree should output as follows: 4 2 6 1 3 5 7 I wrote a quick C++ implementation for this which will do the job in O(n) complexity (hope you can easily convert it to java):

#include <stdio.h>
#include <queue>
using namespace std;
class Node {
public:
    int start;
    int end;
};

void BFSiterate(int *arr, int start, int end) {
    queue<Node>que;
    Node n;
    n.start = start;
    n.end = end;
    que.push(n);

    while(!que.empty()) {
        n = que.front();
        que.pop();
        int mid = n.start + (n.end - n.start) / 2;
        printf("%d\n", arr[mid]);
        Node x;
        x.start = n.start;
        x.end = mid-1;
        if (x.start<=x.end)
            que.push(x); //left

        x.start = mid+1;
        x.end = n.end;
        if (x.start<=x.end)
            que.push(x); //right
    }
}

int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7};
    int len = sizeof(arr)/4;
    BFSiterate(arr, 0, len-1);
    return 0;
}
sowrov
  • 1,018
  • 10
  • 16
  • It looks to me like you might need more bounds checking before adding a range to the queue. While this code works ok with a 7 item array (since that is perfectly balanced), it generates an infinite loop more often than not for other sizes. – James Holderness May 27 '13 at 12:40
  • I think there's still something wrong. If I try with the array `1,2,3,4,5,6` it gives me back `3 1 5 1 2 4 6`, which clearly doesn't seem right (note the repeated `1`). – James Holderness May 27 '13 at 13:03
  • I would be happier if OP would find those issues (I mean it is worth bothering if it has the potentiality to help the OP)! Thank you anyway though... – sowrov May 27 '13 at 13:22
2

I'm not sure this is particularly efficient, but one possible solution is to pass a depth parameter to your bstIterate method and call it repeatedly with increasing depth until it returns no more results.

Something like this:

public static boolean bstIterate(int array[], int start, int end, int depth) {
  if (end <= start)
    return false;
  else {
    int mid = start + (end-start)/2;
    if (depth == 0) {
      System.out.println(array[mid]);
      return true;
    }
    else {
      boolean res1 = bstIterate(array, start, mid, depth-1);
      boolean res2 = bstIterate(array, mid+1, end, depth-1);
      return res1 || res2;
    }
  }
}

which you would call like this:

int depth = 0;
while (bstIterate(array, 0, array.length, depth))
  depth++;

Given this array:

int array[] = {1, 3, 4, 7, 9, 13, 18, 23, 25, 30};

that produces this tree:

                13
       4                 25
   3       9          23    30
1       7          18

and this output:

13
4
25
3
9
23
30
1
7
18

Is that what you had in mind?

James Holderness
  • 22,721
  • 2
  • 40
  • 52
  • This looks rather more like [DFS with ID](http://en.wikipedia.org/wiki/Depth-first_search_with_iterative_deepening). But I suppose it is an option. – Bernhard Barker May 27 '13 at 09:12
  • Nice to know there is a name for that. But notice this point: "IDDFS is equivalent to breadth-first search, but uses much less memory." That's exactly what the OP was asking for was it not? He said he wanted a breadth-first traversal "without extra memory overhead". Using a queue (as the other answer has done) seems like exactly the sort of thing he was trying to avoid. – James Holderness May 27 '13 at 09:59
  • @JamesHolderness: Recursive methods do take [memory in the stack](http://stackoverflow.com/questions/4816159/stack-memory-use-with-arithmetic-and-recursive-function-call), most probably it will take more memory in the stake than the method in other answer, as in each recursive call it will store at least 4 variables + function pointer and maybe local variables as well. On top of that it is an O(n^2) solution :-? – sowrov May 27 '13 at 11:42
  • @sowrov There's a big difference between memory on the stack and memory allocated from the heap, especially in Java where any allocated memory needs to be garbage collected. The issue isn't how much space is used, but the cost of allocation and recovery of that memory. Your solution may well be more efficient, but if the OP is trying to avoid memory allocation then it's not meeting his requirements. – James Holderness May 27 '13 at 12:14
  • @sowrov Actually if you read a bit about [DFS with ID](http://en.wikipedia.org/wiki/Depth-first_search_with_iterative_deepening), you realize it needs much less space than [BFS](http://en.wikipedia.org/wiki/Breadth-first_search) (`O(bd)` VS `O(b^d)`). And, assuming it's a balanced tree, the **majority** of the leaves will be at the bottom level, so it's more like O(2n) = O(n). It's slower, but generally not asymptotically. – Bernhard Barker May 27 '13 at 15:54
  • In my tests of C implementations of both algorithms, IDDFS actually turned out to be 3 to 4 times faster than the queue algorithm. The larger the array the worse the queue performed. That's just one implementation, of course, but I think it's hard to argue that the queue is unquestionably better. – James Holderness May 27 '13 at 17:20
  • I expect my array to be quite large, in the 10s of thousands of elements, and this only needs to be run once, so the main concern is with the memory limits in java. – Jonno_FTW May 28 '13 at 03:10
  • @Jonno_FTW Memory shouldn't be a problem - 100 000 elements should take less than 3 MB with a standard Java queue implementation (similar to the other answer). That can be reduced to about 380 KB using a specialized queue. – Bernhard Barker May 28 '13 at 09:06