Friday, August 2, 2019

UINT 2

Stack

  1. Stack is an ordered list in which, insertion and deletion can be performed only at one end that is called top.
  2. Stack is a recursive data structure having pointer to its top element.
  3. Stacks are sometimes called as Last-In-First-Out (LIFO) lists i.e. the element which is inserted first in the stack, will be deleted last from the stack.
  4. Applications of Stack

    1. Recursion
    2. Expression evaluations and conversions
    3. Parsing
    4. Browsers
    5. Editors
    6. Tree Traversals

    Operations on Stack

    There are various operations which can be performed on stack.

    DS Stack Introduction
    1. Push : Adding an element onto the stack

    DS Stack Introduction

    Stack

    1. Stack is an ordered list in which, insertion and deletion can be performed only at one end that is called top.
    2. Stack is a recursive data structure having pointer to its top element.
    3. Stacks are sometimes called as Last-In-First-Out (LIFO) lists i.e. the element which is inserted first in the stack, will be deleted last from the stack.

    Applications of Stack

    1. Recursion
    2. Expression evaluations and conversions
    3. Parsing
    4. Browsers
    5. Editors
    6. Tree Traversals

    Operations on Stack

    There are various operations which can be performed on stack.

    DS Stack Introduction
    1. Push : Adding an element onto the stack

    DS Stack Introduction
    2. Pop : Removing an element from the stack

    DS Stack Introduction
    3. Peek : Look all the elements of stack without removing them.

    How the stack grows?

    Scenario 1 : Stack is empty
    The stack is called empty if it doesn't contain any element inside it. At this stage, the value of variable top is -1.

    DS Stack Introduction
    Scenario 2 : Stack is not empty
    Value of top will get increased by 1 every time when we add any element to the stack. In the following stack, After adding first element, top = 2.

    DS Stack Introduction
    Scenario 3 : Deletion of an element
    Value of top will get decreased by 1 whenever an element is deleted from the stack.
    In the following stack, after deleting 10 from the stack, top = 1.

    DS Stack Introduction
    Top and its value :
    Top positionStatus of stack
    -1Empty
    0Only one element in the stack
    N-1Stack is full
    NOverflow
  5. Array implementation of Stack

    In array implementation, the stack is formed by using the array. All the operations regarding the stack are performed using arrays. Lets see how each operation can be implemented on the stack using array data structure.

    Adding an element onto the stack (push operation)

    Adding an element into the top of the stack is referred to as push operation. Push operation involves following two steps.
    1. Increment the variable Top so that it can now refere to the next memory location.
    2. Add element at the position of incremented top. This is referred to as adding new element at the top of the stack.
    Stack is overflown when we try to insert an element into a completely filled stack therefore, our main function must always avoid stack overflow condition.
    Algorithm:
    1. begin   
    2.     if top = n then stack full   
    3.     top = top + 1  
    4.     stack (top) : = item;  
    5. end   
    Time Complexity : o(1)

    implementation of push algorithm in C language

    1. void push (int val,int n) //n is size of the stack   
    2. {  
    3.     if (top == n )   
    4.     printf("\n Overflow");   
    5.     else   
    6.     {  
    7.     top = top +1;   
    8.     stack[top] = val;   
    9.     }   
    10. }   

    Deletion of an element from a stack (Pop operation)

    Deletion of an element from the top of the stack is called pop operation. The value of the variable top will be incremented by 1 whenever an item is deleted from the stack. The top most element of the stack is stored in an another variable and then the top is decremented by 1. the operation returns the deleted value that was stored in another variable as the result.
    The underflow condition occurs when we try to delete an element from an already empty stack.
    Algorithm :
    1. begin   
    2.     if top = 0 then stack empty;   
    3.     item := stack(top);  
    4.     top = top - 1;  
    5. end;    
    Time Complexity : o(1)

    Implementation of POP algorithm using C language

    1. int pop ()  
    2. {   
    3.     if(top == -1)   
    4.     {  
    5.         printf("Underflow");  
    6.         return 0;  
    7.     }  
    8.     else  
    9.     {  
    10.         return stack[top - - ];   
    11.     }    
    12. }   

    Visiting each element of the stack (Peek operation)

    Peek operation involves returning the element which is present at the top of the stack without deleting it. Underflow condition can occur if we try to return the top element in an already empty stack.
    Algorithm :
    PEEK (STACK, TOP)
    1. Begin   
    2.     if top = -1 then stack empty    
    3.     item = stack[top]   
    4.     return item  
    5. End    
    Time complexity: o(n)

    Implementation of Peek algorithm in C language

    1. int peek()  
    2. {  
    3.     if (top == -1)   
    4.     {  
    5.         printf("Underflow");  
    6.         return 0;   
    7.     }  
    8.     else  
    9.     {  
    10.         return stack [top];  
    11.     }  
    12. }  


Linked list implementation of stack

Instead of using array, we can also use linked list to implement stack. Linked list allocates the memory dynamically. However, time complexity in both the scenario is same for all the operations i.e. push, pop and peek.
In linked list implementation of stack, the nodes are maintained non-contiguously in the memory. Each node contains a pointer to its immediate successor node in the stack. Stack is said to be overflown if the space left in the memory heap is not enough to create a node.

DS Linked list implementation stack
The top most node in the stack always contains null in its address field. Lets discuss the way in which, each operation is performed in linked list implementation of stack.

Adding a node to the stack (Push operation)

Adding a node to the stack is referred to as push operation. Pushing an element to a stack in linked list implementation is different from that of an array implementation. In order to push an element onto the stack, the following steps are involved.
  1. Create a node first and allocate memory to it.
  2. If the list is empty then the item is to be pushed as the start node of the list. This includes assigning value to the data part of the node and assign null to the address part of the node.
  3. If there are some nodes in the list already, then we have to add the new element in the beginning of the list (to not violate the property of the stack). For this purpose, assign the address of the starting element to the address field of the new node and make the new node, the starting node of the list.
  4. Time Complexity : o(1)

    DS Linked list implementation stack

    Deleting a node from the stack (POP operation)

    Deleting a node from the top of stack is referred to as pop operation. Deleting a node from the linked list implementation of stack is different from that in the array implementation. In order to pop an element from the stack, we need to follow the following steps :
    1. Check for the underflow condition: The underflow condition occurs when we try to pop from an already empty stack. The stack will be empty if the head pointer of the list points to null.
    2. Adjust the head pointer accordingly: In stack, the elements are popped only from one end, therefore, the value stored in the head pointer must be deleted and the node must be freed. The next node of the head node now becomes the head node.

Queue

1. A queue can be defined as an ordered list which enables insert operations to be performed at one end called REAR and delete operations to be performed at another end called FRONT.
2. Queue is referred to be as First In First Out list.
3. For example, people waiting in line for a rail ticket form a queue.


ds Queue

Applications of Queue

Due to the fact that queue performs actions on first in first out basis which is quite fair for the ordering of actions. There are various applications of queues discussed as below.
  1. Queues are widely used as waiting lists for a single shared resource like printer, disk, CPU.
  2. Queues are used in asynchronous transfer of data (where data is not being transferred at the same rate between two processes) for eg. pipes, file IO, sockets.
  3. Queues are used as buffers in most of the applications like MP3 media player, CD player, etc.
  4. Queue are used to maintain the play list in media players in order to add and remove the songs from the play-list.
  5. Queues are used in operating systems for handling interrupts.

Array representation of Queue

We can easily represent queue by using linear arrays. There are two variables i.e. front and rear, that are implemented in the case of every queue. Front and rear variables point to the position from where insertions and deletions are performed in a queue. Initially, the value of front and queue is -1 which represents an empty queue. Array representation of a queue containing 5 elements along with the respective values of front and rear, is shown in the following figure.

Array representation of Queue
The above figure shows the queue of characters forming the English word "HELLO". Since, No deletion is performed in the queue till now, therefore the value of front remains -1 . However, the value of rear increases by one every time an insertion is performed in the queue. After inserting an element into the queue shown in the above figure, the queue will look something like following. The value of rear will become 5 while the value of front remains same.

Array representation of Queue

Algorithm to insert any element in a queue

Check if the queue is already full by comparing rear to max - 1. if so, then return an overflow error.
If the item is to be inserted as the first element in the list, in that case set the value of front and rear to 0 and insert the element at the rear end.
Otherwise keep increasing the value of rear and insert each element one by one having rear as the index.

Algorithm

  • Step 1: IF REAR = MAX - 1
    Write OVERFLOW
    Go to step
    [END OF IF]
  • Step 2: IF FRONT = -1 and REAR = -1
    SET FRONT = REAR = 0
    ELSE
    SET REAR = REAR + 1
    [END OF IF]
  • Step 3: Set QUEUE[REAR] = NUM
  • Step 4: EXIT

Linked List implementation of Queue

Due to the drawbacks discussed in the previous section of this tutorial, the array implementation can not be used for the large scale applications where the queues are implemented. One of the alternative of array implementation is linked list implementation of queue.
The storage requirement of linked representation of a queue with n elements is o(n) while the time requirement for operations is o(1).
In a linked queue, each node of the queue consists of two parts i.e. data part and the link part. Each element of the queue points to its immediate next element in the memory.

In the linked queue, there are two pointers maintained in the memory i.e. front pointer and rear pointer. The front pointer contains the address of the starting element of the queue while the rear pointer contains the address of the last element of the queue.
Insertion and deletions are performed at rear and front end respectively. If front and rear both are NULL, it indicates that the queue is empty.
The linked representation of queue is shown in the following figure.

Linked List implementation of Queue


Operation on Linked Queue

There are two basic operations which can be implemented on the linked queues. The operations are Insertion and Deletion.

Insert operation

The insert operation append the queue by adding an element to the end of the queue. The new element will be the last element of the queue.
Firstly, allocate the memory for the new node ptr by using the following statement.
  1. Ptr = (struct node *) malloc (sizeof(struct node));  
There can be the two scenario of inserting this new node ptr into the linked queue.
In the first scenario, we insert element into an empty queue. In this case, the condition front = NULL becomes true. Now, the new element will be added as the only element of the queue and the next pointer of front and rear pointer both, will point to NULL.
  1. ptr -> data = item;  
  2.         if(front == NULL)  
  3.         {  
  4.             front = ptr;  
  5.             rear = ptr;    
  6.             front -> next = NULL;  
  7.             rear -> next = NULL;  
  8.         }  
In the second case, the queue contains more than one element. The condition front = NULL becomes false. In this scenario, we need to update the end pointer rear so that the next pointer of rear will point to the new node ptr. Since, this is a linked queue, hence we also need to make the rear pointer point to the newly added node ptr. We also need to make the next pointer of rear point to NULL.
  1. rear -> next = ptr;  
  2.             rear = ptr;  
  3.             rear->next = NULL;  
In this way, the element is inserted into the queue. The algorithm and the C implementation is given as follows.

Algorithm

  • Step 1: Allocate the space for the new node PTR
  • Step 2: SET PTR -> DATA = VAL
  • Step 3: IF FRONT = NULL
    SET FRONT = REAR = PTR
    SET FRONT -> NEXT = REAR -> NEXT = NULL
    ELSE
    SET REAR -> NEXT = PTR
    SET REAR = PTR
    SET REAR -> NEXT = NULL
    [END OF IF]
  • Step 4: END

Deletion

Deletion operation removes the element that is first inserted among all the queue elements. Firstly, we need to check either the list is empty or not. The condition front == NULL becomes true if the list is empty, in this case , we simply write underflow on the console and make exit.
Otherwise, we will delete the element that is pointed by the pointer front. For this purpose, copy the node pointed by the front pointer into the pointer ptr. Now, shift the front pointer, point to its next node and free the node pointed by the node ptr. This is done by using the following statements.
  1. ptr = front;  
  2.         front = front -> next;  
  3.         free(ptr);  
The algorithm and C function is given as follows.

Algorithm

  • Step 1: IF FRONT = NULL
    Write " Underflow "
    Go to Step 5
    [END OF IF]
  • Step 2: SET PTR = FRONT
  • Step 3: SET FRONT = FRONT -> NEXT
  • Step 4: FREE PTR
  • Step 5: END

Bubble Sort

In Bubble sort, Each element of the array is compared with its adjacent element. The algorithm processes the list in passes. A list with n elements requires n-1 passes for sorting. Consider an array A of n elements whose elements are to be sorted by using Bubble sort. The algorithm processes like following.
  1. In Pass 1, A[0] is compared with A[1], A[1] is compared with A[2], A[2] is compared with A[3] and so on. At the end of pass 1, the largest element of the list is placed at the highest index of the list.
  2. In Pass 2, A[0] is compared with A[1], A[1] is compared with A[2] and so on. At the end of Pass 2 the second largest element of the list is placed at the second highest index of the list.
  3. In pass n-1, A[0] is compared with A[1], A[1] is compared with A[2] and so on. At the end of this pass. The smallest element of the list is placed at the first index of the list.

Algorithm :

  • Step 1: Repeat Step 2 For i = 0 to N-1
  • Step 2: Repeat For J = i + 1 to N - I
  • Step 3: IF A[J] > A[i]
    SWAP A[J] and A[i]
    [END OF INNER LOOP]
    [END OF OUTER LOOP
  • Step 4: EXIT

How Bubble Sort Works?

We take an unsorted array for our example. Bubble sort takes Ο(n2) time so we're keeping it short and precise.
Bubble Sort
Bubble sort starts with very first two elements, comparing them to check which one is greater.
Bubble Sort
In this case, value 33 is greater than 14, so it is already in sorted locations. Next, we compare 33 with 27.
Bubble Sort
We find that 27 is smaller than 33 and these two values must be swapped.
Bubble Sort
The new array should look like this −
Bubble Sort
Next we compare 33 and 35. We find that both are in already sorted positions.
Bubble Sort
Then we move to the next two values, 35 and 10.
Bubble Sort
We know then that 10 is smaller 35. Hence they are not sorted.
Bubble Sort
We swap these values. We find that we have reached the end of the array. After one iteration, the array should look like this −
Bubble Sort
To be precise, we are now showing how an array should look like after each iteration. After the second iteration, it should look like this −
Bubble Sort
Notice that after each iteration, at least one value moves at the end.
Bubble Sort
And when there's no swap required, bubble sorts learns that an array is completely sorted.
Bubble Sort

Friday, July 26, 2019

UINT I

Introduction
Data Structure can be defined as the group of data elements which provides an efficient way of storing and organising data in the computer so that it can be used efficiently. Some examples of Data Structures are arrays, Linked List, Stack, Queue, etc. Data Structures are widely used in almost every aspect of Computer Science i.e. Operating System, Compiler Design, Artifical intelligence, Graphics and many more.Data Structures are the main part of many computer science algorithms as they enable the programmers to handle the data in an efficient way. It plays a vitle role in enhancing the performance of a software or a program as the main function of the software is to store and retrieve the user's data as fast as possible

Need of Data Structures
As applications are getting complexed and amount of data is increasing day by day, there may arrise the following problems:
Processor speed: To handle very large amout of data, high speed processing is required, but as the data is growing day by day to the billions of files per entity, processor may fail to deal with that much amount of data.
Data Search: Consider an inventory size of 106 items in a store, If our application needs to search for a particular item, it needs to traverse 106 items every time, results in slowing down the search process.
Multiple requests: If thousands of users are searching the data simultaneously on a web server, then there are the chances that a very large server can be failed during that process
in order to solve the above problems, data structures are used. Data is organized to form a data structure in such a way that all items are not required to be searched and required data can be searched instantly.


Advantages of Data Structures
Efficiency: Efficiency of a program depends upon the choice of data structures. For example: suppose, we have some data and we need to perform the search for a perticular record. In that case, if we organize our data in an array, we will have to search sequentially element by element. hence, using array may not be very efficient here. There are better data structures which can make the search process efficient like ordered array, binary search tree or hash tables.
Reusability: Data structures are reusable, i.e. once we have implemented a particular data structure, we can use it at any other place. Implementation of data structures can be compiled into libraries which can be used by different clients.
Abstraction: Data structure is specified by the ADT which provides a level of abstraction. The client program uses the data structure through interface only, without getting into the implementation details.


Advantages of Data Structures
Efficiency: Efficiency of a program depends upon the choice of data structures. For example: suppose, we have some data and we need to perform the search for a perticular record. In that case, if we organize our data in an array, we will have to search sequentially element by element. hence, using array may not be very efficient here. There are better data structures which can make the search process efficient like ordered array, binary search tree or hash tables.
Reusability: Data structures are reusable, i.e. once we have implemented a particular data structure, we can use it at any other place. Implementation of data structures can be compiled into libraries which can be used by different clients.
Abstraction: Data structure is specified by the ADT which provides a level of abstraction. The client program uses the data structure through interface only, without getting into the implementation details.


Data Structure Classification
DS Introduction



Linear Data Structures: A data structure is called linear if all of its elements are arranged in the linear order. In linear data structures, the elements are stored in non-hierarchical way where each element has the successors and predecessors except the first and last element.
Types of Linear Data Structures are given below:
Arrays: An array is a collection of similar type of data items and each data item is called an element of the array. The data type of the element may be any valid data type like char, int, float or double.
The elements of array share the same variable name but each one carries a different index number known as subscript. The array can be one dimensional, two dimensional or multidimensional.
The individual elements of the array age are:
age[0], age[1], age[2], age[3],......... age[98], age[99].
Linked List: Linked list is a linear data structure which is used to maintain a list in the memory. It can be seen as the collection of nodes stored at non-contiguous memory locations. Each node of the list contains a pointer to its adjacent node.
Stack: Stack is a linear list in which insertion and deletions are allowed only at one end, called top.
A stack is an abstract data type (ADT), can be implemented in most of the programming languages. It is named as stack because it behaves like a real-world stack, for example: - piles of plates or deck of cards etc.
Queue: Queue is a linear list in which elements can be inserted only at one end called rear and deleted only at the other end called front.
It is an abstract data structure, similar to stack. Queue is opened at both end therefore it follows First-In-First-Out (FIFO) methodology for storing the data items.
Non Linear Data Structures: This data structure does not form a sequence i.e. each item or element is connected with two or more other items in a non-linear arrangement. The data elements are not arranged in sequential structure.
Types of Non Linear Data Structures are given below:
Trees: Trees are multilevel data structures with a hierarchical relationship among its elements known as nodes. The bottommost nodes in the herierchy are called leaf node while the topmost node is called root node. Each node contains pointers to point adjacent nodes.
Tree data structure is based on the parent-child relationship among the nodes. Each node in the tree can have more than one children except the leaf nodes whereas each node can have atmost one parent except the root node. Trees can be classfied into many categories which will be discussed later in this tutorial.
Graphs: Graphs can be defined as the pictorial representation of the set of elements (represented by vertices) connected by the links known as edges. A graph is different from tree in the sense that a graph can have cycle while the tree can not have the one.
Operations on data structure
1) Traversing: Every data structure contains the set of data elements. Traversing the data structure means visiting each element of the data structure in order to perform some specific operation like searching or sorting.
Example: If we need to calculate the average of the marks obtained by a student in 6 different subject, we need to traverse the complete array of marks and calculate the total sum, then we will devide that sum by the number of subjects i.e. 6, in order to find the average.
2) Insertion: Insertion can be defined as the process of adding the elements to the data structure at any location.
If the size of data structure is n then we can only insert n-1 data elements into it.
3) Deletion:The process of removing an element from the data structure is called Deletion. We can delete an element from the data structure at any random location.
If we try to delete an element from an empty data structure then underflow occurs.
4) Searching: The process of finding the location of an element within the data structure is called Searching. There are two algorithms to perform searching, Linear Search and Binary Search. We will discuss each one of them later in this tutorial.
5) Sorting: The process of arranging the data structure in a specific order is known as Sorting. There are many algorithms that can be used to perform sorting, for example, insertion sort, selection sort, bubble sort, etc.
6) Merging: When two lists List A and List B of size M and N respectively, of similar type of elements, clubbed or joined to produce the third list, List C of size (M+N), then this process is called merging.

Characteristics of an Algorithm

An algorithm must follow the mentioned below characteristics:
  • Input: An algorithm must have 0 or well defined inputs.
  • Output: An algorithm must have 1 or well defined outputs, and should match with the desired output.
  • Feasibility: An algorithm must be terminated after the finite number of steps.
  • Independent: An algorithm must have step-by-step directions which is independent of any programming code.
  • Unambiguous: An algorithm must be unambiguous and clear. Each of their steps and input/outputs must be clear and lead to only one meaning.

PSEUDOCODE: 

One of the tools to define algorithm.
It is an english like representation of the algorithm logic.
Partly english,partly structured code.
English part-provides a relaxed syntax that describes what must be done without showing  
unnecessary details like error messages. 
Code part-consists of an extended version of the basic algorithmic constructs-sequence, selection 
and iteration.


Statement constructs:

Sequence
One or more statements that do not alter the execution path within an algorithm.
Selection
Evaluates a condition and executes zero or more alternatives.
Looping
Iterates a block of code.
Linked List Data Structure
A linked list is a linear data structure, in which the elements are not stored at contiguous memory locations. The elements in a linked list are linked using pointers as shown in the below image:
In simple words, a linked list consists of nodes where each node contains a data field and a reference(link) to the next node in the list.

Circular Linked List 

Circular linked list is a linked list where all nodes are connected to form a circle. There is no NULL at the end. A circular linked list can be a singly circular linked list or doubly circular linked list.
Advantages of Circular Linked Lists:
1) Any node can be a starting point. We can traverse the whole list by starting from any point. We just need to stop when the first visited node is visited again.
2) Useful for implementation of queue. Unlike this implementation, we don’t need to maintain two pointers for front and rear if we use circular linked list. We can maintain a pointer to the last inserted node and front can always be obtained as next of last.
3) Circular lists are useful in applications to repeatedly go around the list. For example, when multiple applications are running on a PC, it is common for the operating system to put the running applications on a list and then to cycle through them, giving each of them a slice of time to execute, and then making them wait while the CPU is given to another application. It is convenient for the operating system to use a circular list so that when it reaches the end of the list it can cycle around to the front of the list.
4) Circular Doubly Linked Lists are used for implementation of advanced data structures like Fibonacci Heap.

Circular Singly Linked List - Insertion

In a singly linked list, for accessing any node of linked list, we start traversing from the first node. If we are at any node in the middle of the list, then it is not possible to access nodes that precede the given node. This problem can be solved by slightly altering the structure of singly linked list. In a singly linked list, next part (pointer to next node) is NULL, if we utilize this link to point to the first node then we can reach preceding nodes. Refer this for more advantages of circular linked lists.
The structure thus formed is circular singly linked list look like this:


In this post, implementation and insertion of a node in a Circular Linked List using singly linked list are explained.
Implementation
To implement a circular singly linked list, we take an external pointer that points to the last node of the list. If we have a pointer last pointing to the last node, then last -> next will point to the first node.

The ponter last points to node Z and last -> next points to node P.
Why have we taken a pointer that points to the last node instead of first node ?
For insertion of node in the beginning we need traverse the whole list. Also, for insertion and the end, the whole list has to be traversed. If instead of start pointer we take a pointer to the last node then in both the cases there won’t be any need to traverse the whole list. So insertion in the begging or at the end takes constant time irrespective of the length of the list.
Insertion
A node can be added in three ways:
  • Insertion in an empty list
  • Insertion at the beginning of the list
  • Insertion at the end of the list
  • Insertion in between the nodes

Insertion in an empty List
Initially when the list is empty, last pointer will be NULL.

After inserting a node T,

After insertion, T is the last node so pointer last points to node T. And Node T is first and last node, so T is pointing to itself.
Insertion at the beginning of the list
To Insert a node at the beginning of the list, follow these step:
1. Create a node, say T.
2. Make T -> next = last -> next.
3. last -> next = T.

After insertion,
Insertion at the end of the list
To Insert a node at the end of the list, follow these step:
1. Create a node, say T.
2. Make T -> next = last -> next;
3. last -> next = T.
4. last = T.
Insertion in between the nodes
To Insert a node at the end of the list, follow these step:
1. Create a node, say T.
2. Search the node after which T need to be insert, say that node be P.
3. Make T -> next = P -> next;
4. P -> next = T.
Suppose 12 need to be insert after node having value 10,

After searching and insertion,
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.
dll
Advantages over singly linked list
1) A DLL can be traversed in both forward and backward direction.
2) The delete operation in DLL is more efficient if pointer to the node to be deleted is given.
3) We can quickly insert a new node before a given node.
In singly linked list, to delete a node, pointer to the previous node is needed. To get this previous node, sometimes the list is traversed. In DLL, we can get the previous node using previous pointer.
Disadvantages over singly linked list
1) Every node of DLL Require extra space for an previous pointer. It is possible to implement DLL with single pointer though (See this and this).
2) All operations require an extra pointer previous to be maintained. For example, in insertion, we need to modify previous pointers together with next pointers. For example in following functions for insertions at different positions, we need 1 or 2 extra steps to set previous pointer.
Insertion
A node can be added in four ways
1) At the front of the DLL
2) After a given node.
3) At the end of the DLL
4) Before a given node.