Browse Source
This adds a generic bottom-up mergesort implementation for singly linked lists. It was inspired by Simon Tatham's webpage on the topic[1], but not so much by his implementation -- for no good reason, really, just a case of NIH. [1] http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
René Scharfe
13 years ago
committed by
Junio C Hamano
5 changed files with 138 additions and 0 deletions
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
#include "cache.h" |
||||
#include "mergesort.h" |
||||
|
||||
struct mergesort_sublist { |
||||
void *ptr; |
||||
unsigned long len; |
||||
}; |
||||
|
||||
static void *get_nth_next(void *list, unsigned long n, |
||||
void *(*get_next_fn)(const void *)) |
||||
{ |
||||
while (n-- && list) |
||||
list = get_next_fn(list); |
||||
return list; |
||||
} |
||||
|
||||
static void *pop_item(struct mergesort_sublist *l, |
||||
void *(*get_next_fn)(const void *)) |
||||
{ |
||||
void *p = l->ptr; |
||||
l->ptr = get_next_fn(l->ptr); |
||||
l->len = l->ptr ? (l->len - 1) : 0; |
||||
return p; |
||||
} |
||||
|
||||
void *mergesort(void *list, |
||||
void *(*get_next_fn)(const void *), |
||||
void (*set_next_fn)(void *, void *), |
||||
int (*compare_fn)(const void *, const void *)) |
||||
{ |
||||
unsigned long l; |
||||
|
||||
if (!list) |
||||
return NULL; |
||||
for (l = 1; ; l *= 2) { |
||||
void *curr; |
||||
struct mergesort_sublist p, q; |
||||
|
||||
p.ptr = list; |
||||
q.ptr = get_nth_next(p.ptr, l, get_next_fn); |
||||
if (!q.ptr) |
||||
break; |
||||
p.len = q.len = l; |
||||
|
||||
if (compare_fn(p.ptr, q.ptr) > 0) |
||||
list = curr = pop_item(&q, get_next_fn); |
||||
else |
||||
list = curr = pop_item(&p, get_next_fn); |
||||
|
||||
while (p.ptr) { |
||||
while (p.len || q.len) { |
||||
void *prev = curr; |
||||
|
||||
if (!p.len) |
||||
curr = pop_item(&q, get_next_fn); |
||||
else if (!q.len) |
||||
curr = pop_item(&p, get_next_fn); |
||||
else if (compare_fn(p.ptr, q.ptr) > 0) |
||||
curr = pop_item(&q, get_next_fn); |
||||
else |
||||
curr = pop_item(&p, get_next_fn); |
||||
set_next_fn(prev, curr); |
||||
} |
||||
p.ptr = q.ptr; |
||||
p.len = l; |
||||
q.ptr = get_nth_next(p.ptr, l, get_next_fn); |
||||
q.len = q.ptr ? l : 0; |
||||
|
||||
} |
||||
set_next_fn(curr, NULL); |
||||
} |
||||
return list; |
||||
} |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
#ifndef MERGESORT_H |
||||
#define MERGESORT_H |
||||
|
||||
void *mergesort(void *list, |
||||
void *(*get_next_fn)(const void *), |
||||
void (*set_next_fn)(void *, void *), |
||||
int (*compare_fn)(const void *, const void *)); |
||||
|
||||
#endif |
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
#include "cache.h" |
||||
#include "mergesort.h" |
||||
|
||||
struct line { |
||||
char *text; |
||||
struct line *next; |
||||
}; |
||||
|
||||
static void *get_next(const void *a) |
||||
{ |
||||
return ((const struct line *)a)->next; |
||||
} |
||||
|
||||
static void set_next(void *a, void *b) |
||||
{ |
||||
((struct line *)a)->next = b; |
||||
} |
||||
|
||||
static int compare_strings(const void *a, const void *b) |
||||
{ |
||||
const struct line *x = a, *y = b; |
||||
return strcmp(x->text, y->text); |
||||
} |
||||
|
||||
int main(int argc, const char **argv) |
||||
{ |
||||
struct line *line, *p = NULL, *lines = NULL; |
||||
struct strbuf sb = STRBUF_INIT; |
||||
|
||||
for (;;) { |
||||
if (strbuf_getwholeline(&sb, stdin, '\n')) |
||||
break; |
||||
line = xmalloc(sizeof(struct line)); |
||||
line->text = strbuf_detach(&sb, NULL); |
||||
if (p) { |
||||
line->next = p->next; |
||||
p->next = line; |
||||
} else { |
||||
line->next = NULL; |
||||
lines = line; |
||||
} |
||||
p = line; |
||||
} |
||||
|
||||
lines = mergesort(lines, get_next, set_next, compare_strings); |
||||
|
||||
while (lines) { |
||||
printf("%s", lines->text); |
||||
lines = lines->next; |
||||
} |
||||
return 0; |
||||
} |
Loading…
Reference in new issue