Sunday, October 25, 2009

A better solution to a programming book's linked list problem

As astute readers of my blog know, I left my job at Sun last month. After taking some well-deserved time off, I have been looking for something new.
Despite 14 years professional experience, 20 years coding experience, and a lengthy résumé, I was stumped by some coding puzzles at a couple of large companies.
So, last monday, I picked up a book at Barnes & Noble titled "Programming Interviews Exposed, 2nd edition", to help get past the torture tests I have to endure in this tight job market. I have been reading it as a leisurely pace, trying to find the problems first on my own, before checking the solution.

I am writing this article and reprinting the problem with the author's permission.

On page 31, the author defines a typical linked-list structure :
typedef struct Element {
    struct Element *next;
    void *data;
} Element;

After some fairly basic coding discussion of linked lists, a slightly more complicated problem appears on page 35.

head and tail are global pointers to the first and last element, respectively, of a singly-linked list of integers. Implement a C function for the following prototype :

bool remove(Element *elem);
The argument to remove is the element to be deleted.


The authors spend much of pages 36-38 discussing that the problem is mostly about
"special cases", in particular when deleting elements at the beginning or the end of the list.

The author's final solution reads as follows :
 
bool remove(Element* elem) { 
    Element* curPos = &head;

    if (!elem)
        return false;

    if (elem == head) {
        head = elem->next;
            delete elem;
        /* special case for 1 element list */
        if (!head)
            tail = NULL;
        return true;
    }
    while (curPos)  {
        if (curPos->next == elem) {
            curPos->next = elem->next;
            delete elem;
            if (curPos->next == NULL)
                tail = curPos;
            return true;
        }
        curPos = curPos->next;
    }
    return false;
}
 
While my first inclination was to also have a special case, I quickly stroke that part out on paper. I think the code really does not have to be so long if you use a double pointer to refer to the
head. This eliminates the first special case for updating the head pointer when removing the first element. And therefore, there is no longer a need to update the tail pointer in two different places, which further reduces code size.

bool remove(Element* elem) {
    Element** curPos = &head;

    if (!elem)
        return false;

    while (*curPos) {
        if (elem == *curPos) {
            *curPos = elem->next;
            if (elem == tail)
                tail = elem->next;
            delete(elem);
            return true;
        }
        curPos = &((*curPos)->next);
    }
    return false;
}
The only thing I changed from my piece of paper was the name of the temporary variable, and the formatting, to match that of the book's authors.

No comments:

Post a Comment