Tree and graphs problems in Julia using struct data types, pointers and this - algorithm

I want to write solve some graph/trees problems using Julia language.
Here is some good example. In C it was done this way:
Recursive C program for level order traversal of Binary Tree
#include <stdio.h>
#include <stdlib.h>
/* A binary tree node has data, pointer to left child
and a pointer to right child */
struct node
{
int data;
struct node *left, *right;
};
/* Function prototypes */
void printGivenLevel(struct node* root, int level);
int height(struct node* node);
struct node* newNode(int data);
/* Function to print level order traversal a tree*/
void printLevelOrder(struct node* root)
{
int h = height(root);
int i;
for (i=1; i<=h; i++)
printGivenLevel(root, i);
}
/* Print nodes at a given level */
void printGivenLevel(struct node* root, int level)
{
if (root == NULL)
return;
if (level == 1)
printf("%d ", root->data);
else if (level > 1)
{
printGivenLevel(root->left, level-1);
printGivenLevel(root->right, level-1);
}
}
/* Compute the "height" of a tree -- the number of
nodes along the longest path from the root node
down to the farthest leaf node.*/
int height(struct node* node)
{
if (node==NULL)
return 0;
else
{
/* compute the height of each subtree */
int lheight = height(node->left);
int rheight = height(node->right);
/* use the larger one */
if (lheight > rheight)
return(lheight+1);
else return(rheight+1);
}
}
/* Helper function that allocates a new node with the
given data and NULL left and right pointers. */
struct node* newNode(int data)
{
struct node* node = (struct node*)
malloc(sizeof(struct node));
node->data = data;
node->left = NULL;
node->right = NULL;
return(node);
}
/* Driver program to test above functions*/
int main()
{
struct node *root = newNode(1);
root->left = newNode(2);
root->right = newNode(3);
root->left->left = newNode(4);
root->left->right = newNode(5);
printf("Level Order traversal of binary tree is \n");
printLevelOrder(root);
return 0;
}
I have tried to do it in Julia in similar way, but there are some problems, especially with accessing to struct elements.(like node->right and node->left).
Or some way to create Self-referential struct and function to allocate nodes.
struct node
data::Int
left::Ptr{node}
right::Ptr{node}
end
# Compute the "height" of a tree -- the number of
# nodes along the longest path from the root node
# down to the farthest leaf node.
function height = (node::Ptr{node})
if node === nothing
return 0
else
# compute the height of each subtree
lheight = height(node->left)
rheight = height(node->right)
# use the larger one
if lheight > rheight
return lheight+1
else return rheight+1
end
end
end
From what I've seen trying to recreate a problem solution in C way isn't the way, however this struct type should be useful. I just have to know how to create self-referent struct, how to allocate elements in this node and how to get them.

First of all, I would strongly suggest reading through https://docs.julialang.org/en/v1/, since Julia is different from C in quite a few ways. There are multiple things to point out here:
Per default structs in Julia are immutable, which means you cannot modify fields after it is created. It is also always passed by copy instead of by reference, since it doesn't have a specific memory address and usually gets allocated on the stack. This actually has multiple benefits for the compiler and is part of the reason why Julia can be so fast. In your example you probably want a mutable struct, which is more similar a struct in C.
In Julia, you should never have to use pointers (Ptr) directly, unless you are calling C code. Since Julia uses a garbage collector, raw pointers have a lot of gotchas when it comes to memory management and should generally just be avoided. You usually just work with objects directly or, if you want to pass immutable objects by reference, you can wrap them in Ref.
In Julia, fields are always accessed either just with a dot x.field (equivalent to getproperty(x, :field)), or in some cases getfield(x, :field). (The latter can't be overloaded by the user, which is sometimes useful). -> actually creates an anonymous function.
For your example the following should work instead:
mutable struct Node
data::Int
left::Node
right::Node
Node(data::Int) = new(data)
end
function height(node::Node, field::Symbol)
isdefined(node, field) || return 0
return height(getproperty(node, field))
end
function height(node::Node)
lheight = height(node, :left)
rheight = height(node, :right)
# use the larger one
if lheight > rheight
return lheight+1
else
return rheight+1
end
end
What the first part is doing is creating a mutable struct Node, with self-referential fields like your C example. The line Node(data::Int) = new(data) is actually an inner constructor taking just the data and if you call new directly in a mutable struct, you can leave trailing fields undefined. You can define these fields afterwards with x.field = y. If these fields are themselves mutable, you can also check if they are undefined with isdefined(x, :field). Here, I am adding another method to height, which also takes a field name, which returns the height of the field if it's defined and 0 otherwise.
You would then construct nodes and calculate their height like this:
julia> n = Node(1)
Node(1, #undef, #undef)
julia> n.left=Node(2)
Node(2, #undef, #undef)
julia> n
Node(1, Node(2, #undef, #undef), #undef)
julia> height(n)
2
Hope that helps! If you want to learn more, the documentation I linked above is usually quite good.

Related

Why my Preoder, Inorder and Postorder functions are not working

#include <stdio.h>
#include <stdlib.h>
Node Creation
This structure creates the struct node data type
struct node
{
int data;
struct node *left, *right;
} * newnode;
Create Function
create() - It first allocates the memory required for the node. When user enters the data, it recursively calls itself to create its child node, and this process goes on. When the user enters -1, it terminates the recursion and goes back from where it is called.
struct node *create()
{
int x;
newnode = (struct node *)malloc(sizeof(struct node));
newnode->left = 0;
newnode->right = 0;
printf("Enter data(-1 for no node)\n");
scanf("%d", &x);
if (x == -1)
return 0;
newnode->data = x;
printf("Enter left child of %d\n", x);
newnode->left = create();
printf("Enter right child of %d\n", x);
newnode->right = create();
return newnode;
}
Preorder
preorder(struct node *root) - This function displays the data of the tree in preorder manner
void preorder(struct node *root)
{
if (root == 0)
return;
printf("%d\n", root->data);
preorder(root->left);
preorder(root->right);
}
Inorder
inorder(struct node *root) - This function displays the data of the tree in inorder manner
void inorder(struct node *root)
{
if (root == 0)
return;
inorder(root->left);
printf("%d\n", root->data);
inorder(root->right);
}
Postorder
Postorder(struct node *root) - This function displays the data of the tree in postorder manner
void postorder(struct node *root)
{
if (root == 0)
return;
postorder(root->left);
postorder(root->right);
printf("%d\n", root->data);
}
Main Function
Main function asks the user to create a tree and then traverse it according to the choice entered by the user. The problem is that preorder, inorder and postorder are not giving the required output, and result in an infinite loop after execution.
void main()
{
struct node *root;
root = 0;
int choice = 3, opt = 1;
while (opt)
{
printf("Select\n 1-for creation\n 2-for preorder\n 3-for inorder\n 4-for postorder\n");
scanf("%d", &choice);
switch (choice)
{
case 1:
root = create();
break;
case 2:
printf("Preorder is: ");
preorder(root);
break;
case 3:
printf("Inorder is: ");
inorder(root);
break;
case 4:
printf("Postorder is: ");
postorder(root);
break;
default:
printf("Invalid choice");
break;
}
printf("Wanna continue \n1-for yes\n0-for no\n");
scanf("%d", &opt);
}
}
There is no bug with any of the traversal functions you've provided.
No design or implementation problem with create () function either.
The trouble lies in the global struct node pointer newnode declared with the structure's definition.
Because each recursive call to create () is basically using the "same" newnode pointer, the tree is never really built in the way we want it to.
Let's try to dry run the create () function.
Let's say we want a tree like this:
1
/ \
2 3
create () is first called from main.
The memory is allocated using malloc () function and the address of the memory is stored in newnode.
Set it's attributes, left and right.
Ask for data and put it into data attribute, if data == -1 is true, return 0.
Up until this point, this is the state:
newnode -> 1
/ \
Null Null
create () is recursively called to build the left subtree.
The memory is allocated for newnode using malloc () and the address of the memory is stored in newnode. Note that this operation has basically "over-wrote" the address previously stored in newnode (because newnode is a global variable)
Then again, the user will be prompted for the data and its attributes will be set.
Therefore, the tree has now become:
newnode -> 2
/ \
Null Null
The struct node to which newnode was previously pointing is now lost (because of loss of its address)
Similarly, when the recursive call for the right subtree is made, then, the following will be observed:
newnode -> 3
/ \
Null Null
Considering the same scenario for the rest of the recursive calls made, it is clear that in the end, the tree we were expecting wasn't built and the reason is the global variable newnode, the constant allocation of memory and over-writing the address in newnode led to memory leakage only.
The reason infinite recursion was found is that, during multiple recursive calls, the left or right pointer of newnode was made to point to newnode itself, leading to a cycle. This node can be found by closely tracking the data of newnode during the recursive calls.
Hence, remove the declaration of newnode pointer from the structure declaration and modify the following statement in create () function:
newnode = (struct node *)malloc(sizeof(struct node));
to this:
struct node * newnode = (struct node *)malloc(sizeof(struct node));
And
struct node
{
int data;
struct node *left, *right;
};
is all what's needed.
In this way, each recursive call to create () function will have its own local pointer variable newnode and there will be no overwriting, no leakage, no cycle, no infinite recursion.

level order tree traversal without using additional memory

I know about algorithm to level order traversal of a tree. (I think everybody knows about that) That algorithm uses queue to store the nodes of the tree. Is there a algorithm that do not uses additional memory? That algorithm must not use recursion (in that way we are using stack). Note, that tree is given in left-child right-sibling representation. No additional pointers are allowed.
The structures in C, for the tree are:
struct node {
int data;
struct node *left-child;
struct node *right-sibling;
}
Tree is represented with a pointer to the root node. Of course, root cannot have right-sibling.
One way could be to use the right-sibling pointers which are null, to make all nodes siblings of each other (temporarily).
You could use a slow and fast pointer. The fast one would always be at the last sibling (that has a null pointer as right-sibling). The left-child pointer of the slow node would then be copied into that right-sibling, after which the fast pointer runs further to the end again. The slow pointer goes one step to the right and the same repeats. When the slow pointer also reaches the end, all nodes will be siblings. Either the slow or fast pointer can be used to output the values in the level-order. This will do the job, but the tree will have been destroyed as a consequence.
To restore the tree, I would suggest that during the above process the direction of all sibling edges is reversed. This means you need to have another pointer that lags behind the slow pointer. This will allow the reversal to be performed between those two. This is a bit obscure, because the right-sibling will then in fact point to something that is mostly a left sibling.
After the above process, the pointers will be at the end of the node list, but because we have reversed the sibling edges, we can also walk back and reverse the edges again. One difficulty is to know which sibling pointers should become null again (for when a node was originally a right most child). This can be done by having again a fast pointer moving ahead (in the left direction) to find nodes that have child. If the pointer that lags behind the slow pointer hits such a child, we know that the slow pointer's node should get a null pointer as right-sibling. When this fix is applied, the fast pointer should again run ahead to find yet another parent node, ...etc.
Note that the left-child pointers are not altered by this algorithm.
So, in total this solution uses three pointers and the structure of the tree itself.
Here is a sample tree I have used in an implementation below:
1
/
2 ------------ 3 ---------4
/ / /
5 -- 6 -- 7 8 -- 9 10 -- 11 -- 12 -- 13
/ /
14 -- 15 -- 16 17 -- 18 -- 19
Implementation in JavaScript -- runnable snippet:
function * traverse(node) {
let lead = node; // ...walks somewhere ahead of node
let lag = null; // ... always follows one step behind node
while (node) {
yield node.data; // output
lead.rightSibling = node.leftChild;
while (lead.rightSibling) lead = lead.rightSibling;
// rotate: point node to next right-sibling, and reverse direction of sibling edge
[node.rightSibling, lag, node] = [lag, node, node.rightSibling]
}
// Restore tree
lead = node = lag.rightSibling; // backwards
lag.rightSibling = null;
while (lead !== null && lead.leftChild === null) lead = lead.rightSibling; // actually going left!
while (node) {
if (lead !== null && lead.leftChild === lag) {
// When lag is the leftChild of some node (lead), then lag should not be the target of a rightSibling
[node.rightSibling, lag, node] = [null, node, node.rightSibling];
// Find previous parent
lead = lead.rightSibling;
while (lead !== null && lead.leftChild === null) lead = lead.rightSibling; // actually going left!
} else {
// walk back and restore sibling pointers
[node.rightSibling, lag, node] = [lag, node, node.rightSibling];
}
}
}
// Create node, given its data and child nodes
function Node(data, ...children) {
// Link the children as siblings
if (children.length > 1) children.reduceRight((a, b) => (b.rightSibling = a, b))
// Create the node itself. For now, without any siblings
return {
data,
leftChild: children.length ? children[0] : null,
rightSibling: null
};
}
// Example tree
let tree = Node(1,
Node(2,
Node(5), Node(6,
Node(14), Node(15), Node(16)
), Node(7)
), Node(3,
Node(8), Node(9)
), Node(4,
Node(10), Node(11,
Node(17), Node(18), Node(19)
), Node(12), Node(13)
)
);
// Apply the algorithm and output the yielded values
console.log(...traverse(tree));
Version in C
I am not so fluent in C, but I think this should do it:
#include <stdio.h>
#include <stdlib.h>
// define Node as a pointer to a node struct
typedef struct node {
int data;
struct node *leftChild;
struct node *rightSibling;
} * Node;
// Some helper functions to ease the creation of a tree:
Node sibling(Node leftSibling, Node rightSibling) {
leftSibling->rightSibling = rightSibling;
return leftSibling;
}
Node parent(Node parent, Node child) {
parent->leftChild = child;
return parent;
}
Node create(int data) {
Node node = malloc(sizeof(struct node));
node->data = data;
return node;
}
// end - helper functions
void traverse(Node node) {
Node lead = node; // ...walks somewhere ahead of node
Node lag = NULL; // ... always follows one step behind node
while (node) {
printf("%d\n", node->data); // output
lead->rightSibling = node->leftChild;
while (lead->rightSibling) lead = lead->rightSibling;
// rotate: point node to next right-sibling, and reverse direction of sibling edge
Node temp = node->rightSibling;
node->rightSibling = lag;
lag = node;
node = temp;
}
// Restore tree
lead = node = lag->rightSibling; // backwards
lag->rightSibling = NULL;
while (lead != NULL && lead->leftChild == NULL) lead = lead->rightSibling; // actually going left!
while (node != NULL) {
if (lead != NULL && lead->leftChild == lag) {
// When lag is the leftChild of some node (lead), then lag should not be the target of a rightSibling
lag = node;
node = node->rightSibling;
lag->rightSibling = NULL;
// Find previous parent
lead = lead->rightSibling;
while (lead != NULL && lead->leftChild == NULL) lead = lead->rightSibling; // actually going left!
} else {
// walk back and restore sibling pointers
Node temp = node->rightSibling;
node->rightSibling = lag;
lag = node;
node = temp;
}
}
}
int main(void) {
// Create the example tree
Node root = parent(create(1),
sibling(parent(create(2),
sibling(create(5), sibling(parent(create(6),
sibling(create(14), sibling(create(15), create(16)))
), create(7)))
), sibling(parent(create(3),
sibling(create(8), create(9))
), parent(create(4),
sibling(create(10), sibling(parent(create(11),
sibling(create(17), sibling(create(18), create(19)))
), sibling(create(12), create(13))))
)))
);
traverse(root);
return 0;
}
To print the tree in a very basic format, you can use this function:
void printTree(Node node, int indent) {
if (!node) return;
for (int i = 0; i < indent; i++) printf(" ");
printf("%d\n", node->data);
printTree(node->leftChild, indent+1);
printTree(node->rightSibling, indent);
}
This will help to verify that indeed the tree is the same before and after the traversal.
If you can store an extra next pointer in each node of the tree which points to the next node in level order for each level, then you can do the level order traversal in constant space.
You can apply Morris level order traversal if you want to traverse your tree in constant space.You can refer here and here.

Singly linked list Tail

if you want create a singly linked list like this:
struct Node {
int data;
Node *next;
};
struct List{
Node *head;
// Node *tail; --> necessary?
Node *last;
};
And this list has the methods "append", "remove", "printList" and "findElement".
Is it necessary to have a tail? Because with "last" you can address the last node.
So when it is necessary to have all three Nodes "head", "tail" and "last"? When you want to insert the node sorted into the list for example?
No, it's not necessary. The tail is equal to head->next and thus it would be redundant and add bookkeeping overhead to keep this field updated.
Also note that the field last is kind of unusual. In most use cases, you add elements to the head of a singly linked list and use a different data structure when you really need to add to the end.
Actually, you can implement enqueue (append at tail), push (prepend at head), dequeue (remove from head), and of course find and print with with a one-pointer header. The trick is to make the list circular and have the header point to the tail. Then tail->next is the head.
#include <stdio.h>
#include <stdlib.h>
typedef struct node_s {
struct node_s *next;
int data;
} Node;
typedef struct list_s {
Node *tail;
} List;
Node *new_node(int data) {
Node *node = malloc(sizeof *node);
node->data = data;
node->next = node;
return node;
}
void init_list(List *list) {
list->tail = NULL;
}
int is_empty(List *list) {
return list->tail == NULL;
}
void enqueue(List *list, Node *node) {
if (list->tail) {
Node *head = list->tail->next;
node->next = head;
list->tail->next = node;
list->tail = node;
} else list->tail = node->next = node;
}
void push(List *list, Node *node) {
if (list->tail) {
Node *head = list->tail->next;
node->next = head;
list->tail->next = node;
} else list->tail = node->next = node;
}
Node *dequeue(List *list) {
Node *head = list->tail->next;
if (head == list->tail)
list->tail = NULL;
else
list->tail->next = head->next;
return head;
}
void print_list(List *list) {
printf("The list:\n");
if (list->tail) {
Node *head = list->tail->next;
Node *p = head;
do {
printf("%d\n", p->data);
p = p->next;
} while (p != head);
}
}
int main(int argc, char *argv[]) {
List list[1];
init_list(list);
// Build the list in order and print it.
for (int i = 0; i < 4; i++) enqueue(list, new_node(i));
print_list(list);
// Remove elements from head until empty.
printf("Dequeueing:\n");
while (!is_empty(list)) {
Node *node = dequeue(list);
printf("%d\n", node->data);
free(node);
}
// Build the list in reverse order and print it.
for (int i = 0; i < 4; i++) push(list, new_node(i));
print_list(list);
return 0;
}
I think it depends on what operations you want to use.
Assuming you want to insert and delete nodes at the tail of a list, it is certainly a wise choice to keep a last node in your list.
Otherwise, if you want to do operations at the beginning of the list, a last node is unnecessary.
It's not necessary but a tail can be useful if you're working with the linked list in a queue-like FIFO fashion rather than a stack-like LIFO fashion or want to be able to transfer entire lists of elements from one head to another's tail without disrupting the relative order of the elements.
Note that I'm referring to 'tail' as a reference to the last node in the list which I believe is safe to assume that the question is about.
A lot of very micro-optimized SLL implementations often are tail-less and work like a stack while backed by an efficient fixed allocator for locality of reference (cache-friendliness) and faster node allocations/deallocations. There the primary benefit of the SLL over a variable-sized array-based sequence is the ability to start moving things around by just changing the value of the next pointer/reference and the lack of invalidation on inserting/removing elements if you're working in native, lower-level languages that involve pointers. The lack of a tail can boost performance quite a bit by reducing the amount of branching instructions required in operations to push and pop from the stack.
For the needs you listed, whether the tail is going to help or just add unnecessary complexity and overhead is if your append and remove operations can work strictly from the front in a LIFO stack fashion or if you want to be able to append to the back but remove from the front in a FIFO fashion without any iteration involved, e.g. If you don't have a tail in the latter case, one of these operations are going to go from constant-time complexity to linear-time complexity, and you might improve your use cases by exchanging that linear-time algorithmic complexity for the relatively smaller micro-level overhead of maintaining a tail.

how to find lowest common ancestor of a nary tree?

Is there a way without using extra space to find LCA of nary tree.
I did it using a string saving the preorder of both the nodes and finding common prefix
If nodes "know" their depth - or you're willing to allow the space to compute the depth of your nodes, you can back up from the lower node to the same depth of the higher node, and then go up one level at a time until they meet.
Depends on what "extra space" means in this context. You can do it with one integer - the difference in depths of the two nodes. Is that too much space?
Another possibility is given you don't have a parent pointer, you can use pointer reversal - every time you traverse a pointer, remember the location from which you came, remember the pointer you will next traverse, and then just before the next pointer traversal, replace that pointer with the back pointer. You have to reverse this when going up the tree to restore it. This takes the space of one pointer as a temporary. And another integer to keep the depth as you work your way down and up. Do this synchronously for the two nodes you seek, so that you can work your way back up from the lower one until you're at the same height in both traversals, and then work back up from both until you're at the common node. This takes three extra pieces of memory - one for each of the current depths, one for the temporary used during a pointer reversal. Very space efficient. Is it worth it?
Go back and do it for a binary tree. If you can do it for a binary tree you can do it for an n-ary tree.
Here's a link to LCA in a binary tree:
And here's how it looks after converting it to a n-ary tree LCA:
public class LCA {
public static <V> Node<V>
lowestCommonAncestor(Node<V> argRoot, Node<V> a, Node<V> b) {
if (argRoot == null) {
return null;
}
if (argRoot.equals(a) || argRoot.equals(b)) {
// if at least one matched, no need to continue
// this is the LCA for this root
return argRoot;
}
Iterator<Node<V>> it = argRoot.childIterator();
// nr of branches that a or b are on,
// could be max 2 (considering unique nodes)
int i = 0;
Node<V> lastFoundLCA = null;
while (it.hasNext()) {
Node<V> node = lowestCommonAncestor(it.next(), a, b);
if (node != null) {
lastFoundLCA = node;
i++ ;
}
if (i >= 2) {
return argRoot;
}
}
return lastFoundLCA;
}
}
Do a synchronous walk to both the nodes.
Start with LCA=root;
loop:
find the step to take for A and the step for B
if these are equal { LCA= the step; decend A; descend B; goto loop; }
done: LCA now contains the lca for A and B
Pseudocode in C:
struct node {
struct node *offspring[1234];
int payload;
};
/* compare function returning the slot in which this should be found/placed */
int find_index (struct node *par, struct node *this);
struct node *lca(struct node *root, struct node *one, struct node *two)
{
struct node *lca;
int idx1,idx2;
for (lca=root; lca; lca=lca->offspring[idx1] ) {
idx1 = find_index(lca, one);
idx2 = find_index(lca, two);
if (idx1 != idx2 || idx1 < 0) break;
if (lca->offspring[idx1] == NULL) break;
}
return lca;
}

Linked List Merge Sort Exaplanation

Can someone explain to me please, how this code works :
http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c
I don't understand the algorithm used in this post. Thanks
Merge sort is often preferred for sorting a linked list. The slow random-access performance of a linked list makes some other algorithms (such as quicksort) perform poorly, and others (such as heapsort) completely impossible.
Let head be the first node of the linked list to be sorted and headRef be the pointer to head. Note that we need a reference to head in MergeSort() as the below implementation changes next links to sort the linked lists (not data at the nodes), so head node has to be changed if the data at original head is not the smallest value in linked list.
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);
/* Link list node */
struct node
{
int data;
struct node* next;
};
/* function prototypes */
struct node* SortedMerge(struct node* a, struct node* b);
void FrontBackSplit(struct node* source,
struct node** frontRef, struct node** backRef);
/* sorts the linked list by changing next pointers (not data) */
void MergeSort(struct node** headRef)
{
struct node* head = *headRef;
struct node* a;
struct node* b;
/* Base case -- length 0 or 1 */
if ((head == NULL) || (head->next == NULL))
{
return;
}
/* Split head into 'a' and 'b' sublists */
FrontBackSplit(head, &a, &b);
/* Recursively sort the sublists */
MergeSort(&a);
MergeSort(&b);
/* answer = merge the two sorted lists together */
*headRef = SortedMerge(a, b);
}
/* See http://geeksforgeeks.org/?p=3622 for details of this
function */
struct node* SortedMerge(struct node* a, struct node* b)
{
struct node* result = NULL;
/* Base cases */
if (a == NULL)
return(b);
else if (b==NULL)
return(a);
/* Pick either a or b, and recur */
if (a->data data)
{
result = a;
result->next = SortedMerge(a->next, b);
}
else
{
result = b;
result->next = SortedMerge(a, b->next);
}
return(result);
}
/* UTILITY FUNCTIONS */
/* Split the nodes of the given list into front and back halves,
and return the two lists using the reference parameters.
If the length is odd, the extra node should go in the front list.
Uses the fast/slow pointer strategy. */
void FrontBackSplit(struct node* source,
struct node** frontRef, struct node** backRef)
{
struct node* fast;
struct node* slow;
if (source==NULL || source->next==NULL)
{
/* length next;
/* Advance 'fast' two nodes, and advance 'slow' one node */
while (fast != NULL)
{
fast = fast->next;
if (fast != NULL)
{
slow = slow->next;
fast = fast->next;
}
}
/* 'slow' is before the midpoint in the list, so split it in two
at that point. */
*frontRef = source;
*backRef = slow->next;
slow->next = NULL;
}
}
/* Function to print nodes in a given linked list */
void printList(struct node *node)
{
while(node!=NULL)
{
printf("%d ", node->data);
node = node->next;
}
}
/* Function to insert a node at the beginging of the 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;
/* link the old list off the new node */
new_node->next = (*head_ref);
/* move the head to point to the new node */
(*head_ref) = new_node;
}
/* Drier program to test above functions*/
int main()
{
/* Start with the empty list */
struct node* res = NULL;
struct node* a = NULL;
struct node* b = NULL;
/* Let us create a unsorted linked lists to test the functions
Created lists shall be a: 2->3->20->5->10->15 */
push(&a, 15);
push(&a, 10);
push(&a, 5);
push(&a, 20);
push(&a, 3);
push(&a, 2);
/* Remove duplicates from linked list */
MergeSort(&a);
printf("\n Sorted Linked List is: \n");
printList(a);
getchar();
return 0;
}
Try imaging all the merges that are performed in a normal merge sort on an array: first, elements are paired up and merged into sorted subarray of length two, then these subarray of length two are paired up and merged into sorted subarray of length four and so on. Notice the length of the subarray: 1, 2, 4, and so on, let's call this instep, which doubles in each iteration.
At any point, p points to a list of length instep, q points to a list of length instep or smaller (we may hit the end of the list), and q immediately follows p. They form a pair of subarray as mentioned above. We do a merge on p and q to get a sorted list of length psize + qsize starting from p. We than move p and q to the next pair, and so on. Once we are done with the whole list, we double instep and start merging longer sorted list.

Resources