1.
Reverse a Doubly Linked List
2. Write a recursive function treeToList(Node root) that takes an ordered binary tree and
rearranges the internal pointers to make a circular doubly linked list out of the tree nodes.
The”previous” pointers should be stored in the “small” field and the “next” pointers should be
stored in the “large” field. The list should be arranged so that the nodes are in increasing order.
Return the head pointer to the new list.
3. QuickSort on Doubly Linked List
4. Merge Sort for Doubly Linked List
5. Find pairs with given sum in doubly linked list
6. Insert value in sorted way in a sorted doubly linked list
7. Delete a Doubly Linked List node at a given position
8. Count triplets in a sorted doubly linked list whose sum is equal to a given value x (hashing)
9. Remove duplicates from a sorted doubly linked list
10. Delete all occurrences of a given key in a doubly linked list
11. Remove duplicates from an unsorted doubly linked list(hashing)
12. Convert a given Binary Tree to Doubly Linked List
13. QuickSort on Singly Linked List
14. Iterative Quick sort
15. Merge sort for linked list
16. Why Quick Sort preferred for Arrays and Merge Sort for Linked Lists?
ans: (i) Quick Sort in its general form is an in-place sort (i.e. it doesn’t require any extra storage)
whereas merge sort requires O(N) extra storage, N denoting the array size which may be quite
expensive. Allocating and de-allocating the extra space used for merge sort increases the
running time of the algorithm.
Comparing average complexity we find that both type of sorts have O(NlogN) average
complexity but the constants differ. For arrays, merge sort loses due to the use of extra O(N)
storage space.
Most practical implementations of Quick Sort use randomized version. The randomized version
has expected time complexity of O(nLogn). The worst case is possible in randomized version also,
but worst case doesn’t occur for a particular pattern (like sorted array) and randomized Quick
Sort works well in practice.
Quick Sort is also a cache friendly sorting algorithm as it has good locality of reference when
used for arrays.
(ii) In case of linked lists the case is different mainly due to difference in memory allocation of
arrays and linked lists. Unlike arrays, linked list nodes may not be adjacent in memory.
Unlike array, in linked list, we can insert items in the middle in O(1) extra space and O(1) time.
Therefore merge operation of merge sort can be implemented without extra space for linked
lists.
In arrays, we can do random access as elements are continuous in memory. Let us say we have
an integer (4-byte) array A and let the address of A[0] be x then to access A[i], we can directly
access the memory at (x + i*4). Unlike arrays, we can not do random access in linked list.
Quick Sort requires a lot of this kind of access. In linked list to access i’th index, we have to travel
each and every node from the head to i’th node as we don’t have continuous block of memory.
Therefore, the overhead increases for quick sort. Merge sort accesses data sequentially and the
need of random access is low.
1.
/* Program to reverse a doubly linked list */
#include <stdio.h>
#include <stdlib.h>
/* a node of the doubly linked list */
struct Node
{ int data;
struct Node *next;
struct Node *prev; };
/* Function to reverse a Doubly Linked List */
void reverse(struct Node **head_ref)
{ struct Node *temp = NULL;
struct Node *current = *head_ref;
/* swap next and prev for all nodes of
doubly linked list */
while (current != NULL)
{ temp = current->prev;
current->prev = current->next;
current->next = temp;
current = current->prev; }
/* Before changing head, check for the cases like empty
list and list with only one node */
if(temp != NULL )
*head_ref = temp->prev; }
/* UTILITY FUNCTIONS */
/* Function to insert a node at the beginging of the Doubly Linked List */
void push(struct Node** head_ref, int new_data)
{ /* allocate node */
struct Node* new_node =
(struct Node*) malloc(sizeof(struct Node));
/* put in the data */
new_node->data = new_data;
/* since we are adding at the begining,
prev is always NULL */
new_node->prev = NULL;
/* link the old list off the new node */
new_node->next = (*head_ref);
/* change prev of head node to new node */
if((*head_ref) != NULL)
(*head_ref)->prev = new_node ;
/* move the head to point to the new node */
(*head_ref) = new_node; }
/* Function to print nodes in a given doubly linked list
This function is same as printList() of singly linked lsit */
void printList(struct Node *node)
{ while(node!=NULL)
{ printf("%d ", node->data);
node = node->next; } }
2.
// A function that appends rightList at the end
// of leftList.
Node *concatenate(Node *leftList, Node *rightList)
{
// If either of the list is empty
// then return the other list
if (leftList == NULL)
return rightList;
if (rightList == NULL)
return leftList;
// Store the last Node of left List
Node *leftLast = leftList->left;
// Store the last Node of right List
Node *rightLast = rightList->left;
// Connect the last node of Left List
// with the first Node of the right List
leftLast->right = rightList;
rightList->left = leftLast;
// Left of first node points to
// the last node in the list
leftList->left = rightLast;
// Right of last node refers to the first
// node of the List
rightLast->right = leftList;
return leftList;
// Function converts a tree to a circular Linked List
// and then returns the head of the Linked List
Node *bTreeToCList(Node *root)
if (root == NULL)
return NULL;
// Recursively convert left and right subtrees
Node *left = bTreeToCList(root->left);
Node *right = bTreeToCList(root->right);
// Make a circular linked list of single node
// (or root). To do so, make the right and
// left pointers of this node point to itself
root->left = root->right = root;
// Step 1 (concatenate the left list with the list
// with single node, i.e., current node)
// Step 2 (concatenate the returned list with the
// right List)
return concatenate(concatenate(left, root), right);
3.
/* Considers last element as pivot, places the pivot element at its
correct position in sorted array, and places all smaller (smaller than
pivot) to left of pivot and all greater elements to right of pivot */
Node* partition(Node *l, Node *h)
{ // set pivot as h element
int x = h->data;
// similar to i = l-1 for array implementation
Node *i = l->prev;
// Similar to "for (int j = l; j <= h- 1; j++)"
for (Node *j = l; j != h; j = j->next)
if (j->data <= x)
// Similar to i++ for array
{ i = (i == NULL)? l : i->next;
swap(&(i->data), &(j->data)); } }
i = (i == NULL)? l : i->next; // Similar to i++
swap(&(i->data), &(h->data));
return i; }
/* A recursive implementation of quicksort for linked list */
void _quickSort(struct Node* l, struct Node *h)
{ if (h != NULL && l != h && l != h->next)
{ struct Node *p = partition(l, h);
_quickSort(l, p->prev);
_quickSort(p->next, h); } }
// The main function to sort a linked list. It mainly calls _quickSort()
void quickSort(struct Node *head)
{ // Find last node
struct Node *h = lastNode(head);
// Call the recursive QuickSort
_quickSort(head, h);
4.
// Function to merge two linked lists
struct Node *merge(struct Node *first, struct Node *second)
{ // If first linked list is empty
if (!first)
return second;
// If second linked list is empty
if (!second)
return first;
// Pick the smaller value
if (first->data < second->data)
{ first->next = merge(first->next,second);
first->next->prev = first;
first->prev = NULL;
return first; }
else
{second->next = merge(first,second->next);
second->next->prev = second;
second->prev = NULL;
return second; } }
// Function to do merge sort
struct Node *mergeSort(struct Node *head)
{ if (!head || !head->next)
return head;
struct Node *second = split(head);
// Recur for left and right halves
head = mergeSort(head);
second = mergeSort(second);
// Merge the two sorted halves
return merge(head,second); }
5.
// Function to find pair whose sum equal to given value x.
void pairSum(struct Node *head, int x)
{ // Set two pointers, first to the beginning of DLL
// and second to the end of DLL.
struct Node *first = head;
struct Node *second = head;
while (second->next != NULL)
second = second->next;
// To track if we find a pair or not
bool found = false;
// The loop terminates when either of two pointers
// become NULL, or they cross each other (second->next
// == first), or they become same (first == second)
while (first != NULL && second != NULL &&
first != second && second->next != first)
{ // pair found
if ((first->data + second->data) == x)
{ found = true;
cout << "(" << first->data<< ", "
<< second->data << ")" << endl;
// move first in forward direction
first = first->next;
// move second in backward direction
second = second->prev; }
else
{ if ((first->data + second->data) < x)
first = first->next;
else
second = second->prev; } }
// if pair is not present
if (found == false)
cout << "No pair found"; }
6.
sortedInsert(head_ref, newNode)
if (head_ref == NULL)
head_ref = newNode
else if head_ref->data >= newNode->data
newNode->next = head_ref
newNode->next->prev = newNode
head_ref = newNode
else
Initialize current = head_ref
while (current->next != NULL and
current->next->data data)
current = current->next
newNode->next = current->next
if current->next != NULL
newNode->next->prev = newNode
current->next = newNode
newNode->prev = current
7.
/* Function to delete the node at the given position
in the doubly linked list */
void deleteNodeAtGivenPos(struct Node** head_ref, int n)
/* if list in NULL or invalid position is given */
if (*head_ref == NULL || n <= 0)
return;
struct Node* current = *head_ref;
int i;
/* traverse up to the node at position 'n' from
the beginning */
for (int i = 1; current != NULL && i < n; i++)
current = current->next;
/* if 'n' is greater than the number of nodes
in the doubly linked list */
if (current == NULL)
return;
/* delete the node pointed to by 'current' */
deleteNode(head_ref, current);
8.
// function to count triplets in a sorted doubly linked list
// whose sum is equal to a given value 'x'
int countTriplets(struct Node* head, int x)
{ struct Node* ptr, *ptr1, *ptr2;
int count = 0;
// unordered_map 'um' implemented as hash table
unordered_map<int, Node*> um;
// insert the <node data, node pointer> tuple in 'um'
for (ptr = head; ptr != NULL; ptr = ptr->next)
um[ptr->data] = ptr;
// generate all possible pairs
for (ptr1 = head; ptr1 != NULL; ptr1 = ptr1->next)
for (ptr2 = ptr1->next; ptr2 != NULL; ptr2 = ptr2->next) {
// p_sum - sum of elements in the current pair
int p_sum = ptr1->data + ptr2->data;
// if 'x-p_sum' is present in 'um' and either of the two nodes
// are not equal to the 'um[x-p_sum]' node
if (um.find(x - p_sum) != um.end() && um[x - p_sum] != ptr1
&& um[x - p_sum] != ptr2)
// increment count
count++; }
// required count of triplets
// division by 3 as each triplet is counted 3 times
return (count / 3); }
9.
removeDuplicates(head_ref, x)
if head_ref == NULL
return
Initialize current = head_ref
while current->next != NULL
if current->data == current->next->data
deleteNode(head_ref, current->next)
else
current = current->next
10.
delAllOccurOfGivenKey(head_ref, x)
if head_ref == NULL
return
Initialize current = head_ref
Declare next
while current != NULL
if current->data == x
next = current->next
deleteNode(head_ref, current)
current = next
else
current = current->next
11.
// function to remove duplicates from
// an unsorted doubly linked list
void removeDuplicates(struct Node** head_ref)
{ // if doubly linked list is empty
if ((*head_ref) == NULL)
return;
// unordered_set 'us' implemented as hash table
unordered_set<int> us;
struct Node* current = *head_ref, *next;
// traverse up to the end of the list
while (current != NULL) {
// if current data is seen before
if (us.find(current->data) != us.end()) {
// store pointer to the node next to
// 'current' node
next = current->next;
// delete the node pointed to by 'current'
deleteNode(head_ref, current);
// update 'current'
current = next; }
else {
// insert the current data in 'us'
us.insert(current->data);
// move to the next node
current = current->next; } } }
12.
// A simple recursive function to convert a given
// Binary tree to Doubly Linked List
// root --> Root of Binary Tree
// head_ref --> Pointer to head node of created
// doubly linked list
void BToDLL(Node* root, Node** head_ref)
{ // Base cases
if (root == NULL)
return;
// Recursively convert right subtree
BToDLL(root->right, head_ref);
// insert root into DLL
root->right = *head_ref;
// Change left pointer of previous head
if (*head_ref != NULL)
(*head_ref)->left = root;
// Change head of Doubly linked list
*head_ref = root;
// Recursively convert left subtree
BToDLL(root->left, head_ref); }
13.
// Partitions the list taking the last element as the pivot
struct Node *partition(struct Node *head, struct Node *end,
struct Node **newHead, struct Node **newEnd)
{ struct Node *pivot = end;
struct Node *prev = NULL, *cur = head, *tail = pivot;
// During partition, both the head and end of the list might change
// which is updated in the newHead and newEnd variables
while (cur != pivot)
{ if (cur->data < pivot->data)
{ // First node that has a value less than the pivot - becomes
// the new head
if ((*newHead) == NULL)
(*newHead) = cur;
prev = cur;
cur = cur->next; }
else // If cur node is greater than pivot
{ // Move cur node to next of tail, and change tail
if (prev)
prev->next = cur->next;
struct Node *tmp = cur->next;
cur->next = NULL;
tail->next = cur;
tail = cur;
cur = tmp; } }
// If the pivot data is the smallest element in the current list,
// pivot becomes the head
if ((*newHead) == NULL)
(*newHead) = pivot;
// Update newEnd to the current last node
(*newEnd) = tail;
// Return the pivot node
return pivot; }
//here the sorting happens exclusive of the end node
struct Node *quickSortRecur(struct Node *head, struct Node *end)
{ // base condition
if (!head || head == end)
return head;
Node *newHead = NULL, *newEnd = NULL;
// Partition the list, newHead and newEnd will be updated
// by the partition function
struct Node *pivot = partition(head, end, &newHead, &newEnd);
// If pivot is the smallest element - no need to recur for
// the left part.
if (newHead != pivot)
{ // Set the node before the pivot node as NULL
struct Node *tmp = newHead;
while (tmp->next != pivot)
tmp = tmp->next;
tmp->next = NULL;
// Recur for the list before pivot
newHead = quickSortRecur(newHead, tmp);
// Change next of last node of the left half to pivot
tmp = getTail(newHead);
tmp->next = pivot; }
// Recur for the list after the pivot element
pivot->next = quickSortRecur(pivot->next, newEnd);
return newHead; }
// The main function for quick sort. This is a wrapper over recursive
// function quickSortRecur()
void quickSort(struct Node **headRef)
{ (*headRef) = quickSortRecur(*headRef, getTail(*headRef));
return; }
14.
/* A[] --> Array to be sorted,
l --> Starting index,
h --> Ending index */
void quickSort(int A[], int l, int h)
{ if (l < h)
{ /* Partitioning index */
int p = partition(A, l, h);
quickSort(A, l, p - 1);
quickSort(A, p + 1, h); } }
15. ALGO:
MergeSort(headRef)
1) If head is NULL or there is only one element in the Linked List
then return.
2) Else divide the linked list into two halves.
FrontBackSplit(head, &a, &b); /* a and b are two halves */
3) Sort the two halves a and b.
MergeSort(a);
MergeSort(b);
4) Merge the sorted a and b (using SortedMerge() discussed here)
and update the head pointer using headRef.
*headRef = SortedMerge(a, b);