Download Notes on Linked Lists

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Quadtree wikipedia , lookup

Lattice model (finance) wikipedia , lookup

Red–black tree wikipedia , lookup

Interval tree wikipedia , lookup

Binary tree wikipedia , lookup

Binary search tree wikipedia , lookup

B-tree wikipedia , lookup

Linked list wikipedia , lookup

Transcript
CSCI 3333 Data Structures
Linked Lists
Acknowledgement
•
•
•
•
•
Ms. Krishani Abeysekera
Dr. Bun Yue
Mr. Charles Moen
Dr. Wei Ding
Dr. Michael Goodrich
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.
Similarities and Differences Between Arrays and
Linked Lists
Similarities
– Both store data in memory
– Both store data in a sequence
Differences
– Array is one block of contiguous memory
– List is a collection of scattered memory
cells
Array
Linked
List
4
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.
Some Disadvantages
Linked List may not be randomly accessible.
Programmers needs to manage memory.
Automatic garbage collection may bring performance issues.
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 re-assigning 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;
// create a new node
newNode->element = e;
newNode->next = NULL;
if ( pHead==NULL)
// if the list is empty add to
head
{
pHead = newNode;
pTail = newNode;
}
else
// Insert newNode as the last
node
{
pTail->next = newNode;
pTail = newNode;
}
}
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
}
}