Showing posts with label linked lists. Show all posts
Showing posts with label linked lists. Show all posts

reversing a linked list

Few of the most elegant solutions for reversing a linked list:
1. Stanford : Linked list problems
2. Algorithms : 4th Edition : Stacks and Queues
The code is reproduced here and is very elegant:
public Node reverse(Node first) {
    if (first == null || first.next == null) return first;
    Node second = first.next;
    Node rest = reverse(second);
    second.next = first;
    first.next  = null;
    return rest;
}

Sorting linked lists

Methods for sorting linked lists :
* Merge sort ( see Knuth Vol.3 p.164 : Alogrithm L )
* Radix sort ( see Knuth Vol.3 p.175 )

He also poses the question "What is the best list-sorting method for six-digit keys, for use on the MIX computer?"(p.309) at the end of the Chapter on sorting and gives the following answer:
For small N : list insertion, for medium N (say 64) : list merge; for large N, radix list sort. (p.701)

Knuth makes an interesting observation:

An ever-increasing number of "pipeline" or "number-crunching" computers have appeared in recent years. These machines have multiple arithmetic units and look-ahead circuitry so that memory references and computation can be highly overlapped; but their efficiency deteriorates noticeably in the presence of conditional branch instructions unless the branch almost always goes the same way. The inner loop of a radix sort is well adapted to such machines, because it is a straight iterative calculation of typical number-crunching form. Therefore radix sorting is usually more efficient than any other known method for internal sorting on such machines, provided that N is not too small and the keys are not too long. (p.175 Vol.3)

Intersection of Linked Lists

Given two sorted linked lists, L1 and L2, write a C program to compute L1 /\ L2 (L1 intersection L2).

Addition of Linked Lists

The numbers are represented in linked-list with each node representing a
digit of the number as follows:

1→2→3→ø
9→9→9→ø

Write a C program to add two numbers.

9→9→9→ø
1→ø
—————————
1→0→0→0→ø



Answer:


struct Node
{
Node* next;
int val;
};


Solution I:


/* Recursive solution */
Node* AddLinkedLists(Node* A, Node* B, size_t *carry)
{
Node *next_node = NULL;
if ( A->next )
A = A->next;
if ( B->next )
B = B->next;
if( (A->next) && (B->next) )
{
next_node = AddLinkedList(A,B);
}
Node* new_node = new Node();
int sum = A->val + B->val + *carry
new_node->val = sum%10 ;
*carry = sum/10;
new_node->next = next_node;
return new_node;
}


Complexity: Space : O(n) , Time : O(n)

Solution II:

1. Reverse the lists O(n)
2. Add the lists
3. Reverse the lists O(n)

Better solution, time : O(n), space : O(1)

merging sorted streams

Given k sorted streams where each stream could possibly be infinite in length, describe an efficient algorithm to merge the k streams into a new stream (also in sorted order).

Answer:

Solution I (Divide and Conquer) :

list1
listA /
/ . \
/ . list2
/ . .
Merged / . .
List \ . .
\
\ . listk-1
listC /
\
listk


This solution is not efficient, Complexity is 2k-1 - 1

Solution II (Using buckets)

Use technique similar to Bucket sort, maximum complexity = O(N2)

Solution III (Using Heap)

(credit : The Great Khali)

Step1: Take first element of all k streams and build a min heap of them. => O(k)
Step2: Remove the min element (elem at top of heap) from the heap and put in the new stream. => O(log k)
Step3: Put new element in heap from the stream to which the prev elem belongs (which was at heap min). => O(log k)
Step4: continue above steps till we exhaust all the streams. If all streams in combination have n elements then order is O(n log(k))

Circular linked list or Corrupt linked list

Write an algorithm to detect a corrupt node in a linked list, i.e
There is a linked list. The last node could point back to any node in
the list (including the head). Find the node in the list to which the
last node points. Or in other words at which node does the circular
linked list start.

Answer:

Solution 1 : O(n) solution, ( This is the most expected solution )

#include "iostream"
#include "stdio.h"

using namespace std;

struct Node
{
int val;
Node *next;
Node(int _val, Node* _next=0):val(_val),next(_next){}
};

Node* createLinkedListWithLoop(size_t n)
{
Node *head = 0;
Node *prev = 0;
Node *last = 0;
printf("\n ---- \n");
for ( int i = 0; i < n; ++i)
{
head = new Node(0,prev);
printf("\n New Node %#x \n", head);
prev = head;
if ( !last ) last = head;
}
head = prev;
// create the loop
last->next = head->next->next->next->next;
printf("\n The last node points to %#x \n", last->next);
return head;
}

size_t countElements(Node* list, const Node* delim_node)
{
size_t count = 0;
while(list != delim_node)
{
++count;
list = list->next;
}
return count;
}

void findLoop(Node* list)
{
// step 1 : Find a node that occurs in the loop
// this is done by taking two pointers and traversing
// the linked list such that :
// PtrA moves at steps of 1
// PtrB moves at steps of 2,
// so that they will intersect each other in the loop.
Node * listA = list;
Node * listB = list;
do
{
if ( listA )
listA = listA->next;
// increment twice
if ( listB )
listB = listB->next->next;
}while(listA != listB);


// now treat list to listA as one list
// and treat listB->next to listA as another list
// i.e. find the point of intersection of these two lists
// and during the traversal, the second node pointing to this
// intersection is the corrupt node.

// the point of intersection
const Node* delim_node = listA;
// the first sub linked list
listA = list;
// the second sub linked list
listB = listB->next;

// step 2 : count the elements
size_t countA = countElements(listA, delim_node);
size_t countB = countElements(listB, delim_node);

// step 3 : align the ptrs so that they are
// at the same distance from the end
while (countA != countB)
{
if( countA > countB )
{
--countA;
listA = listA->next;
}
else
{
--countB;
listB = listB->next;
}
}
// step 4 : move both the ptrs one at a time, and check if they
// both point to same node.
Node *corrupt_node = 0;
while( listA != listB )
{
listA = listA->next;
corrupt_node = listB;
listB = listB->next;
}
// we have found the intersection now,
printf("\n Intersection at %#x, corrupt node %#x \n",
listA, corrupt_node);
}

int main()
{
Node* list = createLinkedListWithLoop(16);
findLoop(list );
return 0;
}


Solution 2 : O(n) time, O(n) space solution, but single pass.

Take a hash_map, as we traverse the linked list,
if ( node ) is absent in hash_map, add it
else if the node is present, then we have found a loop.

Intersection of linked lists

Write an algorithm to find the intersection of linked lists.

Answer:



Solution 1 :
O(n) solution, constant space. (this is what expected most of times)


#include
#include

using namespace std;

struct Node
{
int val;
Node *next;
Node(int _val, Node* _next=0):val(_val),next(_next){}
};

Node* createLinkedList(size_t n)
{
Node *head = 0;
Node *prev = 0;
printf("\n ---- \n");
for ( int i = 0; i < n; ++i)
{
head = new Node(0,prev);
printf("\n New Node %#x \n", head);
prev = head;
}
head = prev;
return head;
}

void createIntersection(Node* listA, Node* listB)
{
while(listB->next) listB = listB->next;
listB->next = listA->next->next->next->next;
}

size_t countElements(Node* list)
{
size_t count = 0;
while(list)
{
++count;
list = list->next;
}
return count;
}

void findIntersection(Node* listA, Node* listB)
{
// step 1 : count the elements
size_t countA = countElements(listA);
size_t countB = countElements(listB);

// step 2 : align the ptrs so that they are
// at the same distance from the end
while (countA != countB)
{
if( countA > countB )
{
--countA;
listA = listA->next;
}
else
{
--countB;
listB = listB->next;
}
}
// step 3 : move both the ptrs one at a time, and check if they
// both point to same node.
while( listA != listB )
{
listA = listA->next;
listB = listB->next;
}
// we have found the intersection now,
printf("Intersection at %#x \n", listA);
}

int main()
{
Node* listA = createLinkedList(10);
Node* listB = createLinkedList(3);
createIntersection(listA, listB);
findIntersection(listA, listB);
return 0;
}


Solution 2:
O(n) space, O(n) time solution,
Use a lookup table. ( not expected most of the times )

linked list flattening

You are given a singly link-list such that each node of this list is also a
head of another link list of the same type. So, how does one flatten the
linked-list

struct node {
void *data; /* could be anything */
struct node *next;
struct node *down;
};

mth to last element in a linked list

Given a singly linked list, devise an algo to find the m-th to last
element of the list. Code it.(Ex. when m=0 the last element of the LL is
returned).

Answer:




[2] -> [3] -> [4] -> [5] -> ..... [100]
^ ^
mThToLast last --->
|----------------------|
distance = m


Node *mThToLast = head;
Node* last = head;
while ( m )
{
last = last->next;
--m;
}
while( last->next )
{
mThToLast= mThToLast->next;
last = last->next;
}


Merge unsorted linked lists

Write the algorithm to merge two unsorted linked lists.

Answer:[1]



The bucket sort will give the best performance.

Add both the linked lists to a bucket data structure and then its simple to perform the bucket sort.

The complexity depends on the input values and how well they can be uniformly distributed among the buckets.


Deleting a node in circular linked list in constant time

You are given a circular single linked list of sufficiently many number of nodes(in the millions). You need to delete a node say P and you are given a pointer to P in the circular single list. Suggest the most efficient methodology of deleting the node P from the circular single linked list without rounding about the circular single linked list.

Answer :
Copy the contents of the next node
tmp = cur->next;
cur->next = this->next->next;
delete tmp

Tree, Linked list creation problem

Imagine you have an unbalanced binary search tree. Now make a linked list of all the nodes at every depth(level) of the tree. Assume every node along with a left and a right pointer also has a next pointer. To make the linked list inside the binary tree for every level, the next pointer of the first node at every depth should point to the next node at the same depth in the tree and so on for the other nodes. What is the complexity of your algorithm.

10
/ \
5 20
/ \ /
3 6 15

~~~~ TO ~~~~
10
/ \
5 ----> 20
/ \ /
3---> 6-->15



Answer:

Here is a solution of time and spacecomplexity O(n)
Here we do a breadth first search using a queue, and maintain the previous
node and its depth to create the linked list.

struct Node
{
int val;
Node *next;
Node *left;
Node *right;
};

void LinkifyTree(Node* root)
{
Queue q;
// contains the node and the depth at which the node occurs.
typedef pair NodeInfo;
// the previous node and its depth.
Node *prev_node = NULL;
int prev_depth = -1;

q.enqueue(make_pair(root,0));
while( !q.isempty())
{
NodeInfo ni = q.dequeue();
if (prev_node && (prev_depth == ni.second) )
{
/* previous node exists and the depth of the previous
* node is equal to the current node
* */
prev_node->next = ni.first;
}
else
{
ni.first->next = NULL;
}
prev_node = ni.first;
prev_depth = ni.second;

/* add the child nodes along with the depth */
q.enqueue( make_pair(ni.first->left, ni.second+1) );
q.enqueue( make_pair(ni.first->right, ni.second+1) );
}
}

Reverse a singly linked list

Write the code to reverse a singly linked list, using recursion.

Answer:

Node* __reverse(Node *n, Node *next)
{
if (!n) return NULL;
if (!next) return n;
Node *tmp = next->next;
next->next = n;
return __reverse(next, tmp);

}

Node* ReverseLinkedList(Node* head)
{
if (!head) return NULL;
Node *rev_head = __reverse( head, head->next );
head->next = NULL;
return rev_head;
}