1
\$\begingroup\$

A node has a next and down pointer. Merge the linked-list such that end result is sorted. Here we have a top level linked-list, followed by a down-list.

If { 10 : {20, 30} , 35 { 40, 50 } } is the input data structure, it means that 10 is connected to 35, using the next pointer. 10 is connected to 20 and 20 to 30 using the down pointer, and 35 is connected to 40 and 40 to 50 using the down pointer.

Only the top level list {10 -> 35} use the next pointer, the down level links {10 -> 20 -> 30} and {35 -> 40 -> 50} use the down pointer ONLY.

The output should be a list 10 - 20 - 30 - 35 - 40 - 50, and down ptr should be null for all.

This question, like many of my previous questions, is attributed to GeeksForGeeks. This code was previously solved here.

Looking for code-review, best practices and optimizations. Verifying complexity to be \$O(mn * logn)\$, where \$n\$ is the length of number of nodes linked by next ptr, while \$m\$ is the max length of the linked list through the down ptr.

public class FlattenLinkedList {

    private Node first;
    private Node last;
    private final PriorityQueue<Node> queue;
    private int size;

    public FlattenLinkedList(List<Integer> nodes) {
        queue = new PriorityQueue<Node>(11, new FlattenListComparator());
        for (Integer item : nodes) {
            add(item);
        }
    }

    /**
     * 
     *  10  -(next)->  20 -(next)-> 30
     *   |             |            |
     *  down          down         down
     *   |             |            |
     *   12            25          35
     *   |             |            |
     *   100          400          200
     *   
     * To represent such a list in the map,
     * keys of such a map would be 10, 20 and 30.
     * The values of such a map would be
     * key: 10, values: a list of 12, 100
     * key: 20, values: a list of 25, 400
     * key: 30, values: a list of 35, 200
     * 
     */
    public FlattenLinkedList(LinkedHashMap<Integer, List<Integer>> nodes) {
        queue = new PriorityQueue<>(11, new FlattenListComparator());
        for (Integer item : nodes.keySet()) {
            add(item);
        }

        Node head = first;
        for (List<Integer> items : nodes.values()) {
            addDown(head, items);
            head = head.next; 
        }
    }


    private void add(int item) {
        final Node node = new Node(item);
        queue.add(node);
        if (first == null) {
            first = last = node;
        } else {
            last.next = node;
            last = node;
        }
        size++;
    }

    private void addDown(Node head, List<Integer> list) {
        Node prev = null;
        for (Integer item : list) {
            Node node = new Node(item);
            if (prev == null) {
                prev = node; 
                head.down = node;
            } else {
                prev.down = node;
            }
            prev = node;
        }
        size = size + list.size();
    }


    private static class FlattenListComparator implements Comparator<Node> {
        @Override
        public int compare(Node node1, Node node2) {
            return node1.item - node2.item;
        }
    }

    private static class Node {
        private Node next;
        private Node down;
        private int item;

        Node(int item) {
            this.item = item;
        }
    }

    public void flatten() {
        Node prev = null;
        while (!queue.isEmpty()) {
            Node currNode = queue.poll();
            if (prev == null) {
                prev = first = currNode;
            } else {
                prev.next = currNode;
                prev = currNode;
            }
            if (currNode.down != null) {
                queue.add(currNode.down);
            }
        }
    }


    public int[] toArray() {
        int[] arr = new int[size];
        int count = 0;
        for (Node x = first; x != null; x = x.next) {
            arr[count++] = x.item;
        }
        return arr;
    }
}

public class FlattenLinkedListTest {

    @Test
    public void test1() {
        LinkedHashMap<Integer, List<Integer>> map = new LinkedHashMap<Integer,  List<Integer>>();
        map.put(10,  Arrays.asList(20,   30));
        map.put(40,  new ArrayList<Integer>());
        map.put(70,  Arrays.asList(80,   90));
        map.put(100, Arrays.asList(110, 120));

        FlattenLinkedList flat = new FlattenLinkedList(map);
        flat.flatten();
        int[] arr = {10, 20, 30, 40, 70, 80, 90, 100, 110, 120};
        assertArrayEquals(arr, flat.toArray());
    }

    @Test
    public void test2() {
        LinkedHashMap<Integer, List<Integer>> map1 = new LinkedHashMap<Integer,  List<Integer>>();
        map1.put(1,  Arrays.asList(80, 90));
        map1.put(2,  Arrays.asList(70, 75));
        map1.put(3,  Arrays.asList(60, 65));
        map1.put(4,  Arrays.asList(50, 55));
        map1.put(5,  Arrays.asList(40, 45));

        FlattenLinkedList flat1 = new FlattenLinkedList(map1);
        flat1.flatten();
        int[] arr1 = {1, 2, 3, 4, 5, 40, 45, 50, 55, 60, 65, 70, 75, 80, 90};
        assertArrayEquals(arr1, flat1.toArray());
    }
}
\$\endgroup\$
2
\$\begingroup\$
private void addDown(Node head, List<Integer> list) {
    Node prev = null;
    for (Integer item : list) {
        Node node = new Node(item);
        if (prev == null) {
            prev = node; 
            head.down = node;
        } else {
            prev.down = node;
        }
        prev = node;
    }
    size = size + list.size();
}

This set of statements seems weird to me:

        Node node = new Node(item);
        if (prev == null) {
            prev = node; 
            head.down = node;
        } else {
            prev.down = node;
        }
        prev = node;

if prev is null, you set it to node, do some things, then set it to node again. Seems like it's not needed.

size = size + list.size();

I'd use += for that.

\$\endgroup\$
0
\$\begingroup\$

Best practice: Declare your collection-type properties as the generic form (Queue) rather than the specific form (PriorityQueue) unless you specifically need a method only available in the implementation class.

As this is a completely internal property it's not that important, but it is a best practice.

another point: I would not use the double assigment (prev = first = node) but rather assign them separately. just like I would prefer testing

if (first == null)
  first = node;
// rather than
if (prev == null) {
  first == node;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.