EDIT
Problem Definition
- For a given node, find the next node (null if tail)
- For a given node, find the previous node (null if head)
- Return a node at a specified offset from the start of the list (null if out of bounds)
- Return a node at a specified offset from the end of the list (null if out of bounds)
The constraints have been revised by the OP, and include restricting the nodes as well as external structures from storing any metadata. My initial implementation of a Circular Linked List did not meet this criteria since it required knowing the head node with a flag value.
I present now a clever workaround:
A Circular Linked List with Forced Node Address Parity
Note that lately I am a C# programmer, but I will try to do this C-style as possible.
The List Node Structure
Nothing fancy here:
struct ListNode
{
ListNode *next;
int value;
};
Adding Nodes with Even Addresses Only
Since this implementation still requires that we be able to find the start of the list, we can use the LSB of the next pointer as a flag. If the last bit of the next pointer is 1, it means we are at the end of the list. A few helper functions:
bool isPtrOdd(void * addr)
{
if((unsigned int) addr & 1)
return 1;
return 0;
}
ListNode * GetNodeAddr(ListNode * node)
{
if(isPtrOdd(node))
return node - 1;
return node;
}
The only special considerations are that we must ensure that nodes are allocated only on even addresses. There are several ways of doing this, but I will show a basic one which allocates an extra byte for each node:
ListNode * NewNode()
{
void * memBlock = malloc(4 + 4 + 1); // 32 bit address, 32 bit value, 1 padding byte
if(isPtrOdd(memBlock)) // if the block started on an odd address
memBlock++; // shift over to an even address
ListNode * newNode = memBlock;
newNode->value = 0;
newNode->next = newNode + 1; // a new node circularly points to itself
return newNode;
}
void AddNode(ListNode * list, ListNode * newNode)
{
while(!isPtrOdd(list->next))
list = list->next; // Get to the end of the list
newNode->next = list->next; // Point the new node to the list head + 1
list->next = newNode;
}
Traversing the List
And using C-style external functions (no classes) we define each of the desired functions:
ListNode * NextNode(ListNode &Node)
{
if(isPtrOdd(Node->next))
return 0;
return &(Node->next);
}
ListNode * PrevNode(ListNode &Node)
{
ListNode curNode = Node;
while(&(GetNodeAddr(curNode->next)) != &Node) // We compare addresses instead of values
curNode = GetNodeAddr(curNode->next); // since the == operator is not defined for structs
if(isPtrOdd(curNode->next)) // if the 'previous node' is actually the end of list
return 0;
return &curNode;
}
ListNode * NodeFromStart(ListNode &Node, int offset)
{
// This function returns offset from head node given
// any node in the list
ListNode Head = Node;
while(!isPtrOdd(Head->next)) // Loop completes when tail is found
Head = GetNodeAddr(Head->next);
Head = GetNodeAddr(Head->next); // Move once more and get to head
for(int n=0; n<offset; n++)
{
if(isPtrOdd(Head->next)) // if we reach the end of the list
return 0;
Head = Head->next;
}
return &Head;
}
ListNode * NodeFromEnd(ListNode &Node, int offset)
{
// This function returns offset from tail node given
// any node in the list
ListNode Head = Node;
while(!isPtrOdd(Head->next)) // Loop completes when tail is found
Head = GetNodeAddr(Head->next);
Head = GetNodeAddr(Head->next); // Move once more and get to head
// Determine number of nodes in List
int count = 1;
while(!isPtrOdd(Head->next))
{
Head = GetNodeAddr(Head->next);
count++;
}
if(count<=offset)
return 0;
return NodeFromStart(Head,count - offset);
}
code-golfor acode-challenge? You currently have it tagged as both. – Paul R Sep 6 '12 at 15:13