abrash-black-book/15-03.html

171 lines
7.6 KiB
HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="vsisbn" content="1576101746" />
<meta name="vstitle" content="Michael Abrash's Graphics Programming Black Book, Special Edition" />
<meta name="vsauthor" content="Michael Abrash" />
<meta name="vspublisher" content="The Coriolis Group" />
<meta name="vspubdate" content="07/01/97" />
<meta name="vscategory" content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development" />
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Linked Lists and Unintended Challenges</title>
<meta name="chapter" content="15" />
<meta name="pages" content="287-290" />
</head>
<body>
<center>
<table border="1">
<tr>
<td>
<a href="15-02.html">Previous</a>
</td>
<td>
<a href="index.html">Table of Contents</a>
</td>
<td>
<a href="15-04.html">Next</a>
</td>
</tr>
</table>
</center>
<p><b>LISTING 15.5 L15-5.C</b></p>
<pre>
/* Finds the first node in a value-sorted linked list that
has a Value field greater than or equal to a key value, and
returns a pointer to the node preceding that node (to facilitate
insertion and deletion), or a NULL pointer if no such value was
found. Assumes the list is terminated with a sentinel tail node
containing the largest possible Value field setting and pointing
to itself as the next node. */
#include &lt;stdio.h&gt;
#include &ldquo;llist.h&rdquo;
struct LinkNode *FindNodeBeforeValueNotLess(
struct LinkNode *HeadOfListNode, int SearchValue)
{
struct LinkNode *NodePtr = HeadOfListNode;
while (NodePtr-&gt;NextNode-&gt;Value &lt; SearchValue)
NodePtr = NodePtr-&gt;NextNode;
if (NodePtr-&gt;NextNode-&gt;NextNode == NodePtr-&gt;NextNode)
return(NULL); /* we found the sentinel; failed search */
else
return(NodePtr); /* success; return pointer to node preceding
node that was &gt;= */
}
</pre>
<p><a id="Fig4"><img src="images/15-04.jpg" /><br />
<b>Figure 15.4</b></a>&nbsp;&nbsp;<i>List terminated by a sentinel.</i></p>
<h3 id="Heading5">Circular Lists</h3>
<p>One minor but elegant refinement yet remains: Use a single node as both the head <i>and</i> the tail of the list. We can do this by connecting the last node back to the first through the head/tail node in a circular fashion, as shown in Figure 15.5. This head/tail node can also, of course, be a sentinel; when it&rsquo;s necessary to check for the end of the list explicitly, that can be done by comparing the current node pointer to the head pointer. If they&rsquo;re equal, you&rsquo;re at the head/tail node.</p>
<p>Why am I so fond of this circular list architecture? For one thing, it saves a node, and most of my linked list programming has been done in severely memory-constrained environments. Mostly, though, it&rsquo;s just so <i>neat;</i> with this setup, there&rsquo;s not a single node or inner-loop instruction wasted. Perfect economy of programming, if you ask me.</p>
<p>I must admit that I racked my brains for quite a while to come up with the circular list, simple as it may seem. Shortly after coming up with it, I happened to look in Sedgewick&rsquo;s book, only to find my nifty optimization described plain as day; and a little while after <i>that,</i> I came across a thread in the algorithms/computer.sci topic on BIX that described it in considerable detail. Folks, the information is out there. Look it up <i>before</i> turning on your optimizer afterburners!</p>
<p>Listings 15.1 and 15.6 together form a suite of C functions for maintaining a circular linked list sorted by ascending value. (Listing 15.5 requires modification before it will work with circular lists.) Listing 15.7 is an assembly language version of <b>InsertNodeSorted()</b>; note the tremendous efficiency of the scanning loop in <b>InsertNodeSorted()&mdash;</b>four instructions per node!&mdash;thanks to the dummy head/tail/sentinel node. Listing 15.8 is a simple application that illustrates the use of the linked-list functions in Listings 15.1 and 15.6.</p>
<p>Contrast Figure 15.5 with Figure 15.1, and Listings 15.1, 15.5, 15.6, and 15.7 with Listings 15.3 and 15.4. Yes, linked lists are simple, but not so simple that a little knowledge doesn&rsquo;t make a substantial difference. Make it a habit to read Knuth or Sedgewick or the like before you write a single line of code.</p>
<p><a id="Fig5"><img src="images/15-05.jpg" /><br />
<b>Figure 15.5</b></a>&nbsp;&nbsp;<i>Representing a circular list.</i></p>
<p><b>LISTING 15.6 L15-6.C</b></p>
<pre>
/* Suite of functions for maintaining a linked list sorted by
ascending order of the Value field. The list is circular; that
is,it has a dummy node as both the head and the tail of the list.
The dummy node is a sentinel, containing the largest possible
Value field setting. Tested with Borland C++ in C mode. */
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &ldquo;llist.h&rdquo;
/* Initializes an empty linked list of LinkNode structures,
consisting of a single head/tail/sentinel node, and returns a
pointer to the list. Returns NULL for failure. */
struct LinkNode *InitLinkedList()
{
struct LinkNode *Sentinel;
if ((Sentinel = malloc(sizeof(struct LinkNode))) == NULL)
return(NULL);
Sentinel-&gt;NextNode = Sentinel;
Sentinel-&gt;Value = SENTINEL;
strcpy(Sentinel-&gt;Text, &ldquo;*** sentinel ***&rdquo;);
return(Sentinel);
}
/* Finds the first node in a value-sorted linked list with a value
field equal to a key value, and returns a pointer to the node
preceding that node (to facilitate insertion and deletion), or a
NULL pointer if no value was found. Assumes list is terminated
with a sentinel node containing the largest possible value. */
struct LinkNode *FindNodeBeforeValue(struct LinkNode *HeadOfListNode,
int SearchValue)
{
struct LinkNode *NodePtr = HeadOfListNode;
while (NodePtr-&gt;NextNode-&gt;Value &lt; SearchValue)
NodePtr = NodePtr-&gt;NextNode;
if (NodePtr-&gt;NextNode-&gt;Value == SearchValue) {
/* Found the search value; success unless we found the
sentinel (can happen only if SearchValue == SENTINEL) */
if (NodePtr-&gt;NextNode == HeadOfListNode) {
return(NULL); /* failure; we found the sentinel */
} else {
return(NodePtr); /* success; return pointer to node
preceding the node that was equal */
}
} else {
return(NULL); /* No match; return failure status */
}
}
/* Inserts the specified node into a value-sorted linked list, such
that value-sorting is maintained. Returns a pointer to the node
after which the new node is inserted. */
struct LinkNode *InsertNodeSorted(struct LinkNode *HeadOfListNode,
struct LinkNode *NodeToInsert)
{
struct LinkNode *NodePtr = HeadOfListNode;
int SearchValue = NodeToInsert-&gt;Value;
while (NodePtr-&gt;NextNode-&gt;Value &lt; SearchValue)
NodePtr = NodePtr-&gt;NextNode;
NodeToInsert-&gt;NextNode = NodePtr-&gt;NextNode;
NodePtr-&gt;NextNode = NodeToInsert;
return(NodePtr);
}
</pre>
<center>
<table border="1">
<tr>
<td>
<a href="15-02.html">Previous</a>
</td>
<td>
<a href="index.html">Table of Contents</a>
</td>
<td>
<a href="15-04.html">Next</a>
</td>
</tr>
</table>
</center>
<hr width="90%" size="1" noshade="noshade" />
<div align="center">
Graphics Programming Black Book &copy; 2001 Michael Abrash
</div>
</body>
</html>