Sentinel node

From Wikipedia, the free encyclopedia
Jump to: navigation, search

In computer programming, a sentinel node is a specifically designated node used with linked lists and trees as a traversal path terminator. This type of node does not hold or reference any data managed by the data structure.

Benefits[edit]

Sentinels are used as an alternative over using null as the path terminator in order to get one or more of the following benefits:

  1. Increased speed of operations
  2. Reduced algorithmic complexity and code size
  3. Increased data structure robustness (arguably)

However, sentinel nodes rely on shared memory, which requires extra code to avoid data races. This causes sentinel nodes to have poor performance on concurrent systems.

Example[edit]

Below are two versions of a subroutine (implemented in the C programming language) for looking up a given search key in a singly linked list. The first one uses the sentinel value NULL, and the second one a (pointer to the) sentinel node Sentinel, as the end-of-list indicator. The declarations of the singly linked list data structure and the outcomes of both subroutines are the same.

struct sll_node {                          // one node of the singly linked list
   int key;
   struct sll_node *next;                  // end-of-list indicator or -> next node
} sll, *first;

First version using NULL as an end-of-list indicator[edit]

 1 // global initialization
 2 first = NULL;                              // before the first insertion (not shown)
 3 
 4 struct sll_node *Search(struct Node *first, int search_key) {
 5     struct sll_node *node;
 6     for (node = first; 
 7         node != NULL; 
 8         node = node->next) {
 9         if (node->key == search_key)
10             return node;                   // found
11     }
12     // not found
13     return NULL;
14 }

The for-loop contains two tests (yellow lines) per iteration:

  • node != NULL;
  • if (node->key == search_key).

Second version using a sentinel node[edit]

The globally available pointer sentinel to the deliberately prepared data structure Sentinel is used as end-of-list indicator.

 1 // global variable
 2 sll_node Sentinel, *sentinel = &Sentinel;
 3 // global initialization
 4 sentinel->next = sentinel;
 5 first = sentinel;                          // before the first insertion (not shown)
 6 // Note that the pointer  sentinel  has always to be kept at the END of the list.
 7 
 8 struct sll_node *SearchWithSentinelnode(struct Node *first, int search_key) {
 9     struct sll_node *node;
10     sentinel->key = search_key;
11     for (node = first; 
12         node->key != search_key; 
13         node = node->next) {
14     }
15     if (node != sentinel)
16         return node;                       // found
17     // not found
18     return NULL;
19 }

The for-loop contains only one test (yellow line) per iteration:

  • node->key != search_key;.
Remark

If the data structure is accessed concurrently then for a sentinel-based implementation, not only the list pointed to by first has to be protected by mutex, but also the Sentinel node has to be protected too; this extra mutex in quite a few use scenarios can cause severe performance degradation [1]. One way to avoid it is to have List structure having both Node* first, and Sentinel node (this way Sentinel won't need to be shared more than original list itself).

See also[edit]

References[edit]

  1. ^ Ignatchenko, Sergey (1998), "STL Implementations and Thread Safety", C++ Report