简体   繁体   中英

Partitioning a LinkedList Around a Value

I was reading through a coding book, and this problem came up:

Write code to partition a linked list around a value x, such that all nodes less than x come before all nodes greater than or equal to x.

The solution involved two lists.

If this were an array, we would need to be careful about how we shifted elements. Array shifts are very expensive. However, in a linked list, the situation is much easier. Rather than shifting and swapping elements, we can actually create two different linked lists: one for elements less than x, and one for elements greater than or equal to x. We iterate through the linked list, inserting elements into our before list or our after list. Once we reach the end of the linked list and have completed this splitting, we merge the two lists.

1 /* Pass in the head of the linked list and the value to partition
2 * around */
3 public LinkedListNode partition(LinkedListNode node, int x) {
4     LinkedListNode beforeStart = null;
5     LinkedListNode beforeEnd = null;
6     LinkedListNode after-Start = null;
7     LinkedListNode afterEnd = null;
8 
9    /* Partition list */
10     while (node != null) {
11         LinkedListNode next = node.next;
12         node.next = null;
13         if (node.data < x) {
14             /* Insert node into end of before list */
15             if (beforeStart == null) {
16                 beforeStart = node;
17                 beforeEnd = beforeStart;
18                 } else {
19                     beforeEnd.next = node;
20                     beforeEnd = node;
21                 }
22             } else {
23             /* Insert node into end of after list */
24             if (after-Start == null) {
25                 afterStart = node;
26                 afterEnd = afterStart;
27             } else {
28                 afterEnd.next = node;
29                 afterEnd = node;
30             }
31         }
32         node = next;
33     }
34
35     if (beforeStart == null) {
36         return afterStart;
37     }
38
39     /* Merge before list and after list */
40     beforeEnd.next = afterStart;
41     return beforeStart;
42 }

My question is this - doesn't it seem a little space-inefficient to have to make a couple of new LinkedLists for merging purposes? Couldn't everything be done in-place instead? Here's my implementation instead.

Node partition(Node head, int x) {
    Node realhead = head;
    Node iter = head;
    while(iter != null && iter.next != null) {
        if(iter.next.data < x) {
            Node temp = iter.next;
            iter.next = temp.next;
            temp.next = realhead;
            realhead = temp;
        }
        iter = iter.next;
    }
    return realhead;
} 

This should (theoretically) give O(n) performance, as good as the book solution, but with O(1) space, since it's not using any additional data structures, and only a couple of extra pointers. Unless the book is simply going for a suboptimal solution, there must be something that I'm missing in my implementation. Can anyone point out my error?

Here is my solution in Java.

/*
 * Append a new node to the end of a LinkedList
 */
private static Node appendNode(Node head, int data)
{
    // If head is null, then add the first node
    if(head == null)
        return new Node(data);      

    Node runner = head;
    while(runner.next != null)
        runner = runner.next;

    runner.next = new Node(data);

    return head;
}

private static Node concatenate(Node head, Node tail)
{
    // First, check arguments whether they are null or not
    if(head == null && tail == null)
        return null;

    if(head == null)
        return tail;

    if(tail == null)
        return head;

    Node runner = head;
    while (runner.next != null)
    {
        runner = runner.next;
    }

    runner.next = tail;

    return head;
}

/*
 * Partition a LinkedList
 */
private static Node partition(Node head, int x)
{
    if(head == null)
        return null;

    Node runner = head;
    Node left = null;
    Node right = null;

    while (runner != null)
    {
        if(runner.data < x)
        {
            if(left == null)
                left = new Node(runner.data);
            else
                left = appendNode(left, runner.data);
        }
        else
        {
            if(right == null)
                right = new Node(runner.data);
            else
                right = appendNode(right, runner.data);
        }
        runner = runner.next;
    }

    Node partitioned = concatenate(left, right);

    return partitioned;
}

The book solution also has a space complexity of O(1). A linked list in you case is just represented by the list head and both solutions you posted are just rearranging pointer and not actually creating any new nodes.

In order to use up more space you would expect to see statements like "new Node()" that would actually mark creating new nodes, but in the case of the book answer it only moves nodes with a value of less than x to the before start list and all others into the after start.

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