Uploaded by aldeyastym

Linked Lists

advertisement
Tutorial 14
Eng. Islam Atef
Linked lists
A linked list is a data structure which is built from structures and pointers. It forms a chain of
"nodes" with pointers representing the links of the chain and holding the entire thing together, in
which the order of the nodes is determined by the address, called the link, stored in each node. A
linked list can be represented by a diagram as shown below:
The arrow in each node indicates that the address of the node to which it is pointing is stored in
that node. The down arrow in the last node indicates that this link field is NULL.
So now we have a list consisting of “nodes” and pointers, the node holds the stored data along
with a pointer to the next node in the list.
Advantages of linked lists:
• Flexible and unbounded size that grows and shrinks as needed
Disadvantages of linked lists:
• Sequential access only
This linked list has four nodes in it, each with a link to the next node in the series. The last node
has a link to the special value NULL The NULL pointer shows that it is the last link in the chain.
There is also another special pointer, called Start or head, which points to the first link in the
chain so that we can keep track of it.
We’ll also have another “current” pointer, as we did in linear lists, which represents the last node
we looked at.
Now let’s look at how to define the linked list in the code. The key part of a linked list is a
structure, which holds the data for each node (the name, address, age or whatever for the items in
the list), and, most importantly, a pointer to the next node. Here given the structure of a typical
node:
struct Node
{
char name[20];
Node *next;
};
// Name of up to 20 letters
// Pointer to next node
The important part of the structure is the line before the closing curly brackets. This gives a
pointer to the next node in the list. This is the only case in C++ where you are allowed to refer to
a data type (in this case Node) before you have even finished defining it. This is not allowed if
next was just a normal variable of type Node, because the compiler won’t know how to allocate
its memory because the structures is not even fully defined yet (we’ve mentioned this in the
structure section). However, defining a pointer to that type is okay as the pointer only store an
address, so it’s memory place is known even before fully defining the struct Node.
So to summarize, the linked list consists mainly of the following:
• The address of the first node stored in the pointer “head”
• Each node which has two components; the info to be stored, and a link to the next node
in the list (that stores the address of the next node)
• The tail which is the last node
Thus far, we’ve defined a structures which represent a single node of the linked list, now let’s
define the whole linked list as a class and introduce its members. It’s as shown below:
class linked_list {
private:
struct node;
//tells the compiler about the name "node" to be
used later until fully defined
typedef node* link;
//now we define node
struct node {
elemtype elem;
link next;
};
link head, tail, current;
public :
linked_list ();
//constructor
void insert( const elemtype &e);
bool first ( elemtype & e);
bool next ( elemtype &e);
};
Let’s now look at the implementation of the basic member functions of this class, there could be
other member functions (ie: search(), copy(), etc..).
Firstly, let’s initialize the class using the constructor to have an empty linked list, as follows:
linked_list :: linked_list ( )
{
//initialize an empty list
head = 0; tail = 0; current = 0;
}
Moreover, let’s look at the insert() function, as follows:
//const call by reference to not change the element sent to be added
void linked_list :: insert (const elemtype & e)
{
// dynamic memory allocation of a new node that'll be added to the list
link addnode (new node);
// to be sure that a node was allocated, if no space is not available, it
returns 0
assert (addnode);
// add the value of "e" in the element of the node
addnode -> elem = e;
// check if the list is empty or not, if empty, make the "head" point to
this node
if(head ==0) head = addnode;
// else, then it's the new tail, so we'll make the old tail point to it
else tail->next= addnode;
// then make this node the new tail
tail = addnode;
// with its next pointing to nothing
addnode->next=0;
}
Now let’s see the function first, as shown below:
//call by reference to return the first element of the list in e
//return type is boolean representing if the first element exists or not
bool linked_list :: first (elemtype & e)
{
// after calling first, current points to first node (head)
// if head==0, the list is empty, so we don't have a first element
if( head == 0) return false;
// else, we have a list consisting of at least 1 element
// we'll return the value of the node that "head" points to
// and we'll make the current point to this head
else
{
e = head->elem;
current = head;
return true;
}
}
Lastly, let’s look at the function next, which returns the next element relative to the current value
(of the member “current”), as shown below:
//call by reference to return the next element of the list in e
//return type is boolean representing if the next element exists or not
bool linked_list :: next ( elemtype & e)
{
//for proper use, current should be nonzero. first() must be called at
least once
assert (current);
// after each call, current always points to the item that next has just
returned
// we want to return the next node to the current node, if the next node
is NULL return false
if ( current -> next ==0) return false;
// if not, shift the current to the next node, and return its element
else
{
current = current -> next;
e = current -> elem;
return true;
}
}
Now let’s write the main function and see how we’d use such a class to create and print a linked
list:
int main ()
{
linked_list L ;
elemtype x , y;
int n,m;
cin>>n;
//inserting n elements in the list
for( int i=0; i<n ; i++)
{
cin>>x;
L.insert(x);
}
//printing the list elements
cout <<" elements of the list "<<endl;
//firstly getting the first element in y
bool notempty =L.first (y);
//as long as we keep finding elements, stay in the loop
while (notempty)
{
//print the first element that we've got
cout <<y<<" ";
//then get the next element
notempty = L.next(y);
}
return 0;
}
Doubly Linked lists
If we want to add a previous operation to the list by adding a predecessor link to every node.
A Doubly Linked List (DLL) contains an extra pointer, typically called previous pointer,
together with next pointer and data which are there in singly linked list. This greatly simplifies
the insertion and removal operations by simply adding another pointer in the node to the
previous node.
Let’s look at the class definition for the doubly linked list:
class double_list
{
private:
struct Node;
typedef Node* link;
struct Node
{
elemtype elem;
link next; // Pointer to next node in DLL
link prev; // Pointer to previous node in DLL
};
link head, current;
public:
double_list ();
//constructor
void insert(const elemtype &e);
bool first ( elemtype & e);
bool next ( elemtype &e);
bool previous(elemtype & e);
};
Function insert() will be changed as follows, inserting at beginning of list:
void double_list:: insert(const elemtype &e)
{
link add= new Node;
assert (add);
add->elem=e;
add->next = head;
if( head) // test to see if list is not empty
head->prev= add;
add->prev= 0;
head=add;
}
Now let’s implement the previous function, as shown below:
bool double_list:: previous(elemtype & e)
{
assert (current);
if( current->prev== 0) return false;
else
{
current= current-> prev;
e= current-> elem;
return true;
}
}
Problem set 8 (cont.)
5) Write a function that counts the number of the nodes in a linked list.
Solution:
We can write this function inside the class or outside, we’ll look at both solutions
Solution(1):
int linked_list::count_node()
{
if(head==0) return 0;
int n=1;
current= head;
while(current->next !=0)
{
n++;
current=current->next;
}
return n;
}
Solution(2):
//send the linked list by reference to have the "current" pointer change when
calling first() and next()
int count_2 (linked_list &A)
{
elemtype x;
int n=0;
// get the first node
bool found=A.first(x);
// if it exists (if the list is not empty basically)
if(!found)
return 0;
// empty list
// keep looping as long you find nodes in the list
while(found)
{
n++;
found= A.next(x);
}
return n;
}
6) Using the class linked list, what will be the output of the followng program:
void linked_list::get( )
int sum =0; current =head;
while( current->next != NULL)
{sum+= current->elem;
current= current ->next;}
cout<< sum;}
void solve(linked_list &L)
{ elemtype x; bool found;
found = L.first(x) ;
if(! found)
return;
while( found)
{cout<<x<<" "; found =L.next(x); found=L.next(x); }}
main( )
{ linked_list L; elemtype x ; int n;
cin>>n;
for( int i=0; i<n ; i++)
{ cin>>x; L. insert (x); }
L. get( );
solve(L); }
Solution:
Assuming this input: 5 7 5 6 3 4
The output is then as follows:
7) To the standard linked list implementaion, add the following functions:
1. append(List A) to append list A at the end of this list(L)
2. List* L.copy() makes a copy of this list L and returns a pointer to it.
Solution:
void linked_list::append (linked_list A)
{
// the tail of this link will point to the head of list A
// the new tail of this list will be the tail of A
tail->next=A.head;
tail=A.tail;
}
linked_list* linked_list::copy()
{
elemtype y;
linked_list* copy_list = new linked_list;
bool notempty =first(y);
if (!notempty) return NULL;
while (notempty)
{
copy_list->insert(y);
notempty = next(y);
}
return copy_list;
}
8) Add a reverse member function to the standard linked list. Implement the reverse
operation recursively, using the following observation: If the list is empty, do nothing. To
reverse a non-empty list, each node should point to the node that was privously its
predecessor, the header should point to the last node, and the node that was formerly first
should have a null link.
Solution:
void linked_list::reverse(link prev=NULL, link next=NULL ,bool
first_call=false)
{
// empty list
if (head == NULL)
return;
// set the current pointer in first call
else if (!first_call)
{
current=head;
prev=NULL;
next=current->next;
first_call=true;
}
// recursive calling until reaching the last node, then go back
recuresively
else if (current->next == NULL) {
tail = head;
head = current;
return;
}
else
{
prev=current;
current = next;
next=next->next;
}
reverse(prev,next,first_call);
// going backwards after the recuresive calling
current->next=prev;
current=prev;
}
9) To the standard linked list, add a function Remove to delete any item from the linked list
and returns the deleted node to the memory. Then use the class linked list to delete all odd
elements(first, third, fifth,...).
Solution:
bool linked_list::remove(int target_index)
{
//empty list
if (head==NULL) return false;
link target=head;
link prev=head;
//point with "target" to the node with the target index
for (int i=1;i<=target_index;i++)
{
//target index is outside list range
if (target->next==NULL)
return false;
prev=target;
target=target->next;
}
prev->next=target->next;
//if the list consists of 1 node only
if (target==head && target->next==NULL)
{
head=NULL;
tail=NULL;
current=NULL;
}
// check if your linked list pointers are pointing to the target
if (target==current)
current=NULL;
if (target==head)
head=target->next;
if (target==tail)
{
tail=prev;
tail->next=NULL; }
delete target;
return true;
}
int main()
{
linked_list L;
int n=8;
elemtype x[] ={3,4,5,6,8,9,7,1,9};
n=sizeof(x)/sizeof(n);
for( int i=0; i<n ; i++)
{
L.insert(x[i]);
}
L.print();
elemtype y;
bool removed=L.first(y);
//if true, we have at least one element in
the list
for (int i=0; removed; i++)
{
removed = L.remove(i);
}
L.print();
return 0;
}
10) Write a member function "insertBefore" that will insert an element before the current
node of a linked list, make the necessary correction to the list.
Solution:
void linked_list::insertBefore(const elemtype & e)
{
//for proper use, current should be nonzero. first() must be called at
least once
assert (current);
link addnode = new node;
addnode->elem=e;
addnode->next=current;
if (head==current)
{
head = addnode;
return;
}
link prev=head;
while (prev->next != current)
{
prev = prev->next;
}
prev->next=addnode;
}
11) Add the following two member functions to the double linked list:
a. function Remove to delete certain element from the double linked list and returns the
deleted node to the memory.
b. Function Append (list L1 ): to append the list L1 at the end of this list.
Solution:
bool double_list::remove(int target_index)
{
//empty list
if (head==NULL) return false;
link target=head;
//point with "target" to the node with the target index
for (int i=1;i<=target_index;i++)
{
//target index is outside list range
if (target->next==NULL)
return false;
target=target->next;
}
//if the list consists of 1 node only
if (target==head && target->next==NULL)
{
head=NULL;
current=NULL;
delete target;
return true;
}
// check if your linked list pointers are pointing to the target
if (target==current)
current=NULL;
if (target==head)
{
head=target->next;
target->next->prev=NULL;
}
else if (target->next==NULL)
{
target->prev->next=target->next;
}
else
{
target->prev->next=target->next;
target->next->prev=target->prev;
}
delete target;
return true;
}
void double_list::append(double_list & L2)
{
current= head;
while(current->next!= 0)
current= current->next;
current->next=L2.head;
L2.head->prev=current;
}
12) Modify problem (10) using class doubly linked list.
Solution:
void double_list::insertBefore(const elemtype & e)
{
//for proper use, current should be nonzero. first() must be called at
least once
assert (current);
link addnode = new Node;
addnode->elem=e;
addnode->next=current;
addnode->prev=current->prev;
current->prev=addnode;
if (head==current)
{
head = addnode;
return;
}
addnode->prev->next=addnode;
}
Download