# 2019q3 homework4 (quiz4) contributed by < `kksweet8845` > ```cpp typedef struct __list { int data; struct __list *next; } list; ``` ```cpp list *sort(list *start) { if (!start || !start->next) return start; list *left = start; list *right = left->next; left->next = NULL; left = sort(left); right = sort(right); for (list *merge = NULL; left || right; ) { if (!right || (left && left->data < right->data)) { if (!merge) { start = merge = left ; } else { merge->next = left; merge = merge->next; } left = left->next; } else { if (!merge) { start = merge = right; } else { merge->next = right; merge = merge->next; } right = right->next; } } return start; } ``` ## Principle Merge sort can be seen as a application of divided-and-counquer method. ![](https://i.imgur.com/S7lYwmb.png) The main concept is to split the original array into two part until the length of array becomes 1. Then, merge all the arraies of one length toggether with a descending order or ascending. That is, the merge sort has two part within this algorithm. - Divide - Counquer Dividing part is simpler but it can apply different strategy when dividing the array. The example code there is a n = 1 + (n-1) method, which will always split the array into two aprts,1 and n-1, respectively. Then, it is inefficeient because it will invoke itself at least (n-1) times until it reach the both length of array be 1. Then, it starts the merge process. Let consider what if we must use the 1-(n-1) stratgy. Then, we can optimize the code when the left node is been merge to the main array. We can simple stop the loop to concatenate the right array into main array directly. ```cpp ``` ![](https://i.imgur.com/FZrqACx.png) ## Circular doubly-linked list - Rewrite the code according to `list.h` Data structure, which `list_head` is the circular linked-list, `listitem` is the custimized node for merge sort. ```cpp struct list_head { struct list_head *prev; struct list_head *next; }; struct listitem { uint16_t i; struct list_head list; }; ``` Merge part ```cpp void merge(struct list_head *left_head, struct list_head *right_head) { struct listitem *leftitem, *rightitem, *lisafe, *risafe, *item; LIST_HEAD(merge_h); leftitem = list_first_entry(left_head, struct listitem, list); lisafe = list_entry(leftitem->list.next, struct listitem, list); rightitem = list_first_entry(right_head, struct listitem, list); risafe = list_entry(rightitem->list.next, struct listitem, list); for (; &leftitem->list != left_head && &rightitem->list != right_head;) { if (cmpint(&leftitem->i, &rightitem->i) < 0) { list_move_tail(&leftitem->list, &merge_h); leftitem = lisafe; lisafe = list_entry((lisafe->list.next), struct listitem, list); } else { list_move_tail(&rightitem->list, &merge_h); rightitem = risafe; risafe = list_entry((risafe->list.next), struct listitem, list); } } if (&leftitem->list == left_head) { list_splice_tail(right_head, &merge_h); } if (&rightitem->list == right_head) { list_splice_tail(left_head, &merge_h); } list_cut_position(left_head, &merge_h, merge_h.prev); } ``` Partition part ```cpp void partition(struct list_head *left_head, struct list_head *right_head, struct list_head *head) { struct list_head *node = NULL; struct list_head *pivot = NULL; int len = 0, mean, i; list_for_each (node, head) len++; mean = len / 2; for (node = head->next, i = 1; i < mean; i++, node = node->next); list_cut_position(left_head, head, node); list_cut_position(right_head, head, head->prev); } ``` Partition First ```cpp void partition_first(struct list_head *left_head, struct list_head *right_head, struct list_head *head) { struct list_head *node = NULL; int len = 0, mean, i; list_for_each (node, head) len++; if (len == 2) { list_cut_position(left_head, head, head->next); list_cut_position(right_head, head, head->prev); return; } list_cut_position(left_head, head, head->next->next); list_cut_position(right_head, head, head->prev); } ``` Partition Randomly ```cpp void partition_rand(struct list_head *left_head, struct list_head *right_head, struct list_head *head) { struct list_head *node = NULL; struct list_head *pivot = NULL; uint16_t rand; int len = 0, mean, i; list_for_each (node, head) len++; if (len > 2) rand = get_unsigned16() % (len - 2) + 1; else { list_cut_position(left_head, head, head->next); list_cut_position(right_head, head, head->prev); return; } for (node = head->next, i = 0; i < rand; i++, node = node->next); list_cut_position(left_head, head, node); list_cut_position(right_head, head, head->prev); } ``` ## Iterative version ```cpp void merge_sort_iter(struct list_head *head){ struct list_head *node, *prev; struct list_head *odd_special = NULL; int len = 0, original_l = 0; struct list_head sec, temp; struct listitem *item = NULL; INIT_LIST_HEAD(&sec); INIT_LIST_HEAD(&temp); /** * Determine the length */ list_for_each(node, head) original_l++; if(original_l % 2 != 0){ odd_special = head->prev; list_del_init(odd_special); len = original_l - 1; }else len = original_l; size_t i = 0, j, k, t; for(i=(len>>1),j=2;i != 0 && j!=2*len;j<<=1,i>>=1){ for(k=j;k<=len;k+=j){ for(node = head, t = 0; t<j; t++, node=node->next); list_cut_position(&sec, head, node); swap(&sec, j); list_splice_tail_init(&sec, &temp); } list_splice_init(&temp, head); } if(odd_special){ list_for_each_entry(item, head, list){ if(item->i > list_entry(odd_special, struct listitem, list)->i){ prev = item->list.prev; prev->next = odd_special; odd_special->prev = prev; item->list.prev = odd_special; odd_special->next = &item->list; } } } } ``` ## XOR sort The data structure ```cpp typedef struct xor_list { int data; unsigned long lxr; } xor_list_t; typedef struct xor_info { xor_list_t *left; xor_list_t *right; } xor_lxri_t; ``` ```cpp void merge_sort(xor_lxri_t *p_lxri, xor_list_t **start){ if (!(*start) || !p_lxri->right) return; xor_list_t *left = *start; xor_list_t *right = p_lxri->right; /** * Construct xor_info for left and right */ xor_lxri_t left_lxri = {.left = NULL, .right = NULL}; left->lxr = (unsigned long) left_lxri.left ^ (unsigned long) left_lxri.right; xor_lxri_t right_lxri = {.left = NULL, .right = NULL}; right_lxri.right = extract_address(right, left); right->lxr = (unsigned long) right_lxri.left ^ (unsigned long)right_lxri.right; merge_sort(&left_lxri, &left); merge_sort(&right_lxri, &right); xor_lxri_t merge_lxri = {.left = NULL, .right = NULL}; for (xor_list_t *merge = NULL; left || right; ) { if(!right || (left && left->data < right ->data)) { if(!merge){ *start = merge = left; merge->lxr = (unsigned long) merge_lxri.left; }else { merge = append_item(&merge_lxri, merge, left); } left_lxri.left = left; left = left_lxri.right; if(left){ left_lxri.right = extract_address(left, left_lxri.left); left->lxr = (unsigned long) NULL ^ (unsigned long) left_lxri.right; } left_lxri.left = (xor_list_t*) NULL; }else { if(!merge){ *start = merge = right; merge->lxr = (unsigned long) merge_lxri.left; }else { merge = append_item(&merge_lxri, merge, right); } right_lxri.left = right; right = right_lxri.right; if(right){ right_lxri.right = extract_address(right, right_lxri.left); right->lxr = (unsigned long) NULL ^ (unsigned long) right_lxri.right; } right_lxri.left = (xor_list_t*) NULL; } p_lxri->right = extract_address(*start, (xor_list_t*)NULL); p_lxri->left = (xor_list_t*) NULL; } } ``` With doubly linked-list ```shell ==25059== HEAP SUMMARY: ==25059== in use at exit: 0 bytes in 0 blocks ==25059== total heap usage: 10 allocs, 10 frees, 240 bytes allocated ``` With xor linked-list ```cpp ==23626== HEAP SUMMARY: ==23626== in use at exit: 0 bytes in 0 blocks ==23626== total heap usage: 10 allocs, 10 frees, 160 bytes allocated ``` We can see that the memory usage of doubly linked-list has additional 8 bytes than the xor linked-list.