linked Lists

advertisement
Linked Lists
Linked List ADT
A linked list is a series of connected nodes (or links)
where each node is a data structure.
Dynamically allocated data structures can be linked
together to form a chain.
A linked list can grow or shrink in size as the program
runs.This is possible because the nodes in a linked list are
dynamically allocated.
Advantages of Linked Lists over
Arrays
Linked lists are more complex to code and manage than
arrays, but they have some distinct advantages.
a) A linked list can easily grow and shrink in size The programmer doesn’t need to know how many
nodes will be in the list. They are created in memory
as needed.
b) Speed of insertion or deletion from the list Inserting and deleting elements into and out of arrays
requires moving elements of the array. When a node
is inserted, or deleted from a linked list, none of the
other nodes have to be moved.
Composition of a Linked List
Each node in the linked list contains -
a) One or more members that represent data (e.g. inventory
records, customer names, addresses, telephone numbers,
etc).
b) A pointer, that can point to another node.
Data Members
Pointer
Composition of a Linked List
A linked list is called “linked” because each node in the series
(i.e. the chain) has a pointer to the next node in the list, e.g.
a) The list head is a pointer to the first node in the list.
b) Each node in the list points to the next node in the list.
c) The last node points to NULL (the usual way to signify the end).
pHead
struct
char
Node
};
pTail
Node {
element;
*next;
*next;
struct
char
Node
};
Node {
element;
*next;
*next;
struct
char
Node
};
Node {
element;
*next;
*next;
NULL
Composition of a Linked List
The pointers play a big role in maintaining
the linked list. The nodes in a linked list can
be spread out over memory. Therefore, it is
not possible to calculate the address of the
next node, like it is possible to calculate the
next element of an array. If a pointer from
one node to another is lost, the list from that
point is lost forever. Therefore, extreme care
should be taken when assigning or reassigning a pointer in a linked list.
Composition of a Linked List
D
A
E
B
pHead
C
If the pointer between any nodes (say B and C) is re-assigned erroneously to
null or anything else, the access to the rest of the nodes ( C, D and E ) is then
lost.
If you loose the head pointer, you loose the entire list.
Creating a Linked List
Just like any other data type, the information about the node
has to be first be declared.
Step 1)
Declare a data structure for the nodes.
struct Node
{
Object element;
Node *next;
};
Creating a Linked List
a) In this example, the first member of the Node struct is a
object called element. It holds the node’s data. This could
just as well be just an integer x, or a structure of student
records.
b) The second member is a pointer called next. It holds the
address of any object that is a structure of type Node.
Hence each Node struct can point to the next node in the
list.
The Node struct contains a pointer to an object of the same
type as that being declared. It is called a self-referential data
structure. This makes it possible to create nodes that point to
other nodes of the same type.
Creating a Linked List
Next, since there is no physical relationship between nodes, a pointer
needs to be created to point to the first logical node in the list.
Step 2) Declare a pointer to serve as the head of the list:
Node *head = NULL;
Once you have done these 2 steps (i.e. declared a node data structure, and
created a NULL head pointer, you have an empty linked list.
head
null
Declaring a Linked List in a
Class
class LinkedList{
public:
struct Node
{
Object element;
Node *next;
Node (Object e, Node* n = NULL ) {
element = e, next = n }
};
…
private:
Node* pHead;
};
Linked List Operations
There are 5 basic linked list operations:
–
–
–
–
–
Appending a node (to the head or to the tail)
Traversing a list
Inserting a node (into a sorted list)
Deleting a node (from the head, tail or middle)
Destroying a list
Appending a Node
When appending a node, there are several things
that need to be taken into consideration. For
instance:
–
–
–
–
–
Memory has to be allocated for a new node, and the data
stored in the memory
The insertion point has to be determined – this could be to
the head of the list, to the tail of the list or somewhere in the
middle
Once the insertion point is determined, the new node’s
logical predecessor has to be identified
Then, the new node has to point to its successor
Finally, the predecessor has to point to the new node
Appending a Node
newNode
F
D
A
E
B
pHead
C
InsertAfterThis
Once the logical predecessor of the new node (InsertAfterThis) is
identified, if it is equal to NULL, this signifies that the list is empty or that
the insertion is always done to the head of the list. In this instance, the code
for inserting the node is similar to:
newNode->next = pHead;
pHead = newNode;
Appending a Node
newNode
F
D
A
pHead
E
B
C
InsertAfterThis
InsertAfterThis
However, if the logical predecessor of the new node (InsertAfterThis)
is identified and it points to a node, this could mean that the node is
appended to the middle of the list or to the end. Regardless of where the
insertion of the new node is going to be, the code for inserting the node is
similar to:
newNode->next = InsertAfterThis->next;
InsertAfterThis->next = newNode;
Appending to the Tail
When appending always to the end of the list, here is one
algorithm that could be used.
a) Create a new node.
b) Store data in the new node.
c) if there are no nodes in the list
Make the new node the first node.
else
Traverse the List to find the last node.
Add the new node to the end of the list.
endif.
Add a Node to an Empty List
New Node
h
next
pHead
NULL
NULL
pHead
h
next
NULL
Appending a Node to the end of a List
New Node
h
NULL
next
pHead
e
next
f
next
g
next
NULL
f
next
g
next
h
pHead
e
next
next
NULL
Appending a Node to the Tail
void LinkedList::appendNode(object e)
{
Node *newNode, *Walker;
newNode = new Node;
newNode->element = e;
newNode->next = NULL;
if ( pHead==NULL)
pHead = newNode;
else
// create a new node
// if the list is empty add to head
{
Walker = pHead;
while (Walker->next != NULL)
Walker = Walker->next;
Walker->next = newNode;
}
// Initialize Walker to head of list
// Find the last node in the list
// Insert newNode as the last node
Appending a Node to the Tail
In the previous algorithm, the list had to be
traversed all the way to the end to find the
last node in the list.
One way to make this simpler would be to
have a pointer that always pointed to the last
node. In this method, traversing the list would
then not be required.
Appending a Node to the Tail
without Traversing the List
void LinkedList::appendNode(object e)
{
Node *newNode;
newNode = new Node;
newNode->element = e;
newNode->next = NULL;
if ( pHead==NULL)
{
pHead = newNode;
pTail = newNode;
}
else
{
pTail->next = newNode;
pTail = newNode;
}
}
// create a new node
// if the list is empty add to head
// Insert newNode as the last node
Appending a Node to the Head
When appending always to the head of the list, here
is one algorithm that could be used.
a) Create a new node.
b) Store data in the new node.
c) if the list is empty
Make the new node the head of the list.
else
Add the new node to the top of the list
Make the new node the head of the list
endif.
Appending a Node to the Head
pHead
newNode
f
g
next
e
next
NEXT
h
next
NULL
Appending a Node to the Head
void LinkedList::appendNode(object e)
{
Node *newNode;
newNode = new Node;
// create a new node
newNode->element = e;
newNode->next = NULL;
if ( pHead==NULL)
// appending to an empty list
{
pHead = newNode;
}
else
// appending to an existing list
{
newNode->next = pHead;
pHead = newNode;
}
}
Appending a Node to the Head
In the previous algorithm, when appending a node to
an existing list, the order in which the statements are
written is extremely important.
What would happen if the statement were written in
reverse order, in this manner?
pHead = newNode;
newNode->next = pHead;
Appending a Node to the Head
This would result with the new node pointing to itself, which would
make the program go in a never-ending loop when accessing the
list. This would also result in loosing access to all the nodes that
were currently on the linked list.
pHead
newNode
f
g
next
e
next
NEXT
h
next
NULL
Traversing a Linked List
When traversing a list, it is important to remember NOT to
move the head pointer from node to node. This would
result in loosing access to nodes in the list. Instead,
always assign another pointer to the head of the list, and
use that pointer to traverse it.
Assign list head to walking pointer
While walking pointer is not NULL
Display the info pointed to by walking pointer
Assign walking pointer to its own next member
end While
Traversing a Linked List
void LinkedList::displayList()
{
Node *Walker;
Walker = pHead;
while(Walker != NULL)
{
printInfo(Walker->element) ;
Walker = Walker->next;
}
}
Inserting a Node into an Ordered
List
Create a new node.
Store data in the new node.
if there are no nodes in the list
Make the new node the first node.
else
Find the first node whose value is greater than or equal
the new value, or the end of the list (whichever is first).
Insert the new node before the found node, or at end of
the list if no node was found.
endif
Inserting a Node
7
pHead
2
next
next
12
newNode
9
null
null
Inserting a Node
nodePtr
7
pHead
2
next
next
12
previousNode
newNode
9
null
null
Inserting a Node
previousNode
7
pHead
2
next
next
12
nodePtr
newNode
9
null
null
Inserting a Node
Once the position in which to insert is found, the node needs to
be inserted. The order in which the statements are written is
important when inserting the node.
newNode->next = nodePtr;
previousNode->next = newNode;
OR
newNode->next = previousNode->next;
previousNode->next = newNode;
Inserting a Node
The segment of code from the previous slide will result in the following list:
previousNode
7
pHead
2
next
next
12
nodePtr
9
newNode
next
null
Inserting a Node
However, if the order in which the statements were
written were reversed in this manner:
previousNode->next = newNode;
newNode->next = previousNode->next;
This would result with the new node pointing to itself,
which would make the program go in a never-ending
loop when accessing the list.
Inserting a Node
The segment of code from the previous slide will result in the following list,
where the nodes after the new node are lost, and the list becomes a never
ending loop, when traversed.
previousNode
7
pHead
2
next
next
12
nodePtr
9
newNode
next
null
Inserting a Node into an Ordered
List
void LinkedList::insertNode(Object e)
{
Node *newNode, *nodePtr, *previousNode;
newNode = new Node;
// Allocate a new node & store the new object
newNode->element = e;
newNode->next = NULL;
if (pHead==NULL)
// empty list – add to head
pHead = newNode;
else
Walker = pHead;
// assign Walker to head and use Walker to find where to insert
while ( Walker != NULL && Walker->element.value < newNode->element.value )
{
previousNode = Walker;
Walker = Walker->next;
}
if (previousNode == NULL)
// new node has the smallest value, insert at head
{
head = newNode;
newNode->next = Walker;
}
else
{
previousNode->next = newNode;
newNode->next = Walker;
}
}
Deleting a Node
Deleting a node is similar to adding a node.
The node to be deleted needs to be identified,
along with its predecessor. Then,
a) Remove the node from the list without
breaking the links created by the next
pointers.
b) Delete the node from memory.
It is important to remember to delete the node
so that resources are released.
Deleting a Node
Assume that we need to delete node that contains the value 7.
nodePtr
7
pHead
2
next
next
12
previousNode
The bypassed node is destroyed with the statement
delete nodePtr;
null
Deleting a Node
void LinkedList::deleteNode(int num)
{
ListNode *Walker, *previousNode;
if (!pHead)
// If the list is empty, do nothing.
return;
if (pHead->element.value == num)
// If it is the first node in the list (special case)
{
Walker = pHead->next;
// set the new head to walker
delete pHead;
// delete node
pHead = Walker;
// reassign new head
}
else
{
Walker = pHead;
// Initialize Walker to head of list
while (Walker != NULL && Walker->element.value != num)
// skip all nodes whose value is not num
{
previousNode = Walker;
Walker = Walker->next;
}
previousNode->next = Walker->next;
// Link the previous node to the node after Walker
delete Walker;
// delete Walker
}
}
Download