View
214
Download
0
Category
Preview:
Citation preview
8/3/2019 More Linked Lists
1/27
More Linked ListsChapter 9
8/3/2019 More Linked Lists
2/27
In some applications, it is convenient to keep access to both
the first node and the last node in the list.
This would work nicely for a linked-list implementation of a
queue (but we'll see an alternative later.)
L
first
mySize 5
9 17 22 26 34
last
Linked List Variants (9.1)
8/3/2019 More Linked Lists
3/27
The data part of the head node might be used to store some
information about the list
e.g., the number of values in the list, or the name of a set
to which all the values in the list belong . . . (e.g., 9.2:Linked polynomials)
first 9 17 22 26 34?
Sometimes a (dummy) head node is used so that every
node has a predecessor eliminates special cases for inserting and deleting.
Traversals start at first->next
Inserts & deletes: No special case (first node)
Constructor: first = new Node; first ?
Head Nodes
8/3/2019 More Linked Lists
4/27
(If data portion of element is large, two or more lists canshare the same trailer node.)
first 9 17 22 26 34? ?
Sometimes a (dummy) trailer node is also used so thatevery node has a successor.
Not very
common
Trailer Nodes
8/3/2019 More Linked Lists
5/27
Each node in a circular linked list has a predecessor (and a successor), provided
that the list is nonempty.
insertion and deletion do not require special consideration of the first
node.
This is a good implementation for a linked queue or for any problem in
last 9 17 22 26 34
In other applications a circular linked list is used;
instead of the last node containing a null pointer, itcontains a pointer to the first node in the list.
For such lists,one can use a single pointer to the last node in the
list, because then one has direct access to it and "almost-direct"
access to the first node.
Circular Linked List
8/3/2019 More Linked Lists
6/27
Circularly Linked Lists
For example, item can be inserted as follows:
newptr = new Node(item, 0);
if (first == 0) // list is empty
{
newptr->next = newptr;
first = newptr;
}
else // nonempty list
{
newptr->next = predptr->next;
predptr->next = newptr;
}
Note that a one-element circularly linked list points to itself.
8/3/2019 More Linked Lists
7/27
Circularly Linked ListsTraversal must be modified: avoid an infinite loop by looking for the end of list as
signalled by a null pointer.
Like other methods, deletion must also be slightly modified.
Deleting the last node is signalled when the node deleted points to itself.
if (first == 0) // list is empty
// Signal that the list is empty
else
{
ptr = predptr->next; // hold node for deletion
if (ptr == predptr) // one-node list
first = 0;
else // list with 2 or more nodes
predptr->next = ptr->next;
delete ptr;}
8/3/2019 More Linked Lists
8/27
All of these lists, however, are uni-directional; we can
only move from one node to its successor.
last
prevL
firstmySize 5
9 17 22 26 34
next
In many applications, bidirectional movement is necessary. In
this case, each node has two pointers one to its successor
(null if there is none) and one to its predecessor (null if there is
none.) Such a list is commonly called a doubly-linked (or
symmetrically-linked) list.
e.g., 9.4 BigInt
Doubly Linked Lists (9.4)
8/3/2019 More Linked Lists
9/27
Linked List Variants
L
first
mySize 5
last9 17 22 26 34
prev
next
doubly-linked list => bidirectional movement!!
Each node has two pointers one to its successor (null if there is
none) and one to its precedessor (null if there is none.)
Doubly linked lists give one more flexibility (can move in either
direction)BUT at significant cost :
DOUBLEthe overhead for links
More complex code
8/3/2019 More Linked Lists
10/27
And of course, we could modify this doubly-linked list so thatboth lists are circular forming a doubly-linked ring.
L
first
mySize 5
last
9 17 22 26 34
Add a head node and we have the implementation used inSTL's list class.
Other variations: 9.5
Multiply-ordered lists
Lists of lists (LISP)
Doubly Linked Rings (9.5)
8/3/2019 More Linked Lists
11/27
TL list Class Template
L
first
mySize 5
last
17 22 26 349
prev
next
data
list is a sequential containeroptimized for insertion and erasure at arbitrary points in the
sequence
Implementation: circular doubly-linked list with head node.
Caveat:
Ease of use is a trade-off for the significant overhead of doubly-linked lists
Moral:
Be aware of what you are using and its costs/benefits
8/3/2019 More Linked Lists
12/27
Hash Tables
Linear search takes O(n)Binary search takes O(log n)
Both depend on comparisons of item sought and elements in container
Hash tablesplace data so that the location of an item is determined directly as a
function of the item itself.
With a good hash function, searching a hash table takes O(1) time
that is, it is constant and does not depend on the number of items stored.
One problem with hash tables is collisions: when more than one item maps to thesame location in the hash table.
The hash function and data set determine the number of collisions and the strategy
used to handle collisions affect the performance of searching for an arbitrary
item.
8/3/2019 More Linked Lists
13/27
Hash Tables
Given up to 25 integers in the range 0 through 999 to be stored in a hash table.
This hash table can be implemented as an integer array table in which each array
element is initialized with some dummy value, such as -1.
If we use each integeri in the set as an index, that is, if we store i in table[i], then to
determine whether a particular integer number has been stored, we need only
check iftable[number] is equal to number.
The hash function then is h(i) =i
The hash function determines the location of an item i
in the hash table.
8/3/2019 More Linked Lists
14/27
Hash TablesThe hash function in the previous example works perfectly because the time
required to search the table for a given value is constant;
only one location needs to be examined.
This hash function then is very time efficient, but it is surely notspace-efficient.
Only 25 of the 1000 available locations are used to store items, leaving 975 unusedlocations; only 2.5 percent of the available space is used, and so 97.5 percent is
wasted!
Because it is possible to store 25 values in 25 locations, we might try improving
space utilization by using an array table with capacity 25.
Modified hash function h(i) =i modulo 25 addresses the space problem
// C++ syntax,
int h(int i)
{ return i % 25;}
8/3/2019 More Linked Lists
15/27
H
ashT
ablesint h(int i){ return i % 25;}always produces an integer in the range 0 through 24.52 thus is stored in table[2], since h(52) =52 % 25 =2.129, 500, 273, and 49 are stored in locations 4, 0, 23, and 24, respectively.
INDEX VALUE
0 500
1 -1
2 52
3 -14 129
5 -1
23 273
24 49
8/3/2019 More Linked Lists
16/27
Hash Tables
But what about placing 77? h(77) =77 % 25 =2
Collision!!
Other values may collide at a given position:
for example, all integers of the form 25k+2 hash to location 2.
Some strategy is needed to resolve such collisions:
1. Need to be able to place an element when its mapped location is alreadyfull
2. Need to be able to retrieve element when it's not placed directly according
to the hash function
8/3/2019 More Linked Lists
17/27
Collision Strategy (Storage)
linear probing: linear search of the table from location of collision until an
empty slot is found in which the item can be stored.
When 77 collides with 52 at location 2, put 77 in position 3
INDEX VALUE
0 5001 -1
2 52
3 77
4 129
5 102
23 273
24 49
To insert 102, we follow the probe sequence consisting of locations 2, 3, 4,
and 5 to find the first available location and thus store 102 in table[5].
8/3/2019 More Linked Lists
18/27
Collision Strategy (Storage)
If the search reaches the bottom of the table, continue at the first location.
123 collides with 273 at location 23, and the probe sequence 23, 24, 0, 1locates the first empty slot at position 1. 123 placed in position 1
INDEX VALUE0 500
1 123
2 52
3 77
4 129
5 102
23 27324 49
8/3/2019 More Linked Lists
19/27
Cost of Linear Probing (Searching)To determine if a specified value is in this hash table,
apply the hash function to compute the location for this value
1. if location is empty, value not in the table.
2. if location contains the specified value, the search is
successful.3. if location contains a different value, must rule out collision
begin a circular linear search at this location and
continue until either item is found or empty or starting
location reached (item not in table)Items 1 & 2 take O(1) time
But Item 3 (worst case) takes O(n)!
Use a hash table capacity thats 1.5 - 2 times the # of items to be
stored (c.f., the Birthday Problem on p. 484)
8/3/2019 More Linked Lists
20/27
AnotherCollision StrategyChaining: use a hash table that is an array (or vector) of linked lists to store the
items.
For example, to store names "alphabetically", use an array table of 26 linked lists,
initially empty, and the simple hash function h(name) =name[0] - A;
that is, h(name) is 0 ifname[0] is A,
1 ifname[0] is B, . . . ,
25 ifname[0] is Z
Searching such a hash table is straightforward:apply the hash function to the item sought and then use one of the search
algorithms for linked lists.
When a collision occurs, we simply insert the new item into the appropriate linked
list.
8/3/2019 More Linked Lists
21/27
Performance ofHash function
The behavior ofthe hash function affects the frequency of collisions.
For example, the preceding hash function is not optimal because some letters occur
more frequently than others.
the linked list of names beginning with S tends to be much longer than that
containing names that begin with Z.
clustering effect results in longer search times for S-names than for Z-names.
Need a better hash function to distribute names more uniformly in hash table
The hash function must not, however, be so complex that the time required to
evaluate it makes the search time unacceptable.
8/3/2019 More Linked Lists
22/27
Random HashingAn ideal hash function is
ysimple to evaluate
yscatters items throughout the hash table
minimizing the probability of collisions.
random hashing uses a simple random number generation technique to scatter the itemsrandomly throughout the hash table.
The item to store is first transformed into a large random integer and then reduced modulo
the tables capacity to determine its location:
randomInt = ((MULTIPLIER * item) + ADDEND) % MODULUS
location = randomInt % CAPACITY;
random hashing can be used with any object first encoded as an integer
For example, a name might be encoded as the sum (or average) of the ASCII codes of
some or all of its letters.
See p. 486
8/3/2019 More Linked Lists
23/27
On the surface, listlooks quite simple. But it's allo/deallo-cation scheme is
more complex than simply using new and delete operations.
To reduce the inefficiency of using the heap manager for large numbers ofallo/deallo-cations, it does it's own memory management.
Basically, for each list of a certain type T:
When a node is needed:
1. If there is a node on the free list, allocate it.(This is maintained as a linked stack in exactly the way we
described earlier.)
2. If the free list is empty:
a. Call the systems heap manager to allocate a block of memory (called
a buffer) typical size: 4K bytes.
b. Carve it up into pieces of size required for a node of a list.When a node is deallocated:
Push it onto the free list.When alllists of this type T have been destroyed:
Return all buffers to the heap.
Allo/deallo-cation in list
8/3/2019 More Linked Lists
24/27
listiterators:
list's iterator is "weaker" than that forvector.vector: random access iterators
list: idirectionaliterators
They have the following operations in common:
++ Move iterator to next element (likeptr = ptr-> next)
-- Move iterator to preceding element (likeptr = ptr-> prev)
* dereferencing operator (likeptr-> data)
= assignment (for same type iterators)
it1 = it2 makes it1positioned at same element as it2
== and != (for same type iterators)
checks whether iterators are positioned at the same element
list Iterators
8/3/2019 More Linked Lists
25/27
Iteratorsan abstraction of a pointer, hiding some of its details and eliminating some of its hazards.
For example, a list::iterator is a class within the list class template that contains
a data member node, which is a pointer to a list_node (the struct used to describe
nodes in the list class template):
template
class list
{
public:
class iterator // ... simplified here ...
{
protected:
list_node * node; // ... and here ...
. . .
};
. . .
};
8/3/2019 More Linked Lists
26/27
IteratorsThe iterator class overloads operator*()to return the value of the data member in the
list_nodepointed to by the iterators node member:
return node->data;
Also overloads operator++ () to increment the iterator to the next node in the list
// prefix version
node = node->next;
return *this;
// postfix version
iterator tmp = node;
node = node->next;
return tmp;
and overloads operator--() similarly to decrement the iterator to the previous node in
the list.
8/3/2019 More Linked Lists
27/27
Iteratorstwo important iterator-valued functions:
begin(), which returns an iterator to the first value in the list; and
end(), which returns an iterator that points beyond the final value in the list,
respectively.
Iterators normally go from the first node to the last.
One can use a reverse_iterator to go from last to first.
list:: reverse_iterator it;
for (it = myList.rbegin(); it != myList.rend(); it++)
cout
Recommended