Skip to content

Commit 0db71e0

Browse files
René Scharfegitster
authored andcommitted
add mergesort() for linked lists
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 828ea97 commit 0db71e0

File tree

5 files changed

+138
-0
lines changed

5 files changed

+138
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@
180180
/test-index-version
181181
/test-line-buffer
182182
/test-match-trees
183+
/test-mergesort
183184
/test-mktemp
184185
/test-obj-pool
185186
/test-parse-options

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ TEST_PROGRAMS_NEED_X += test-genrandom
465465
TEST_PROGRAMS_NEED_X += test-index-version
466466
TEST_PROGRAMS_NEED_X += test-line-buffer
467467
TEST_PROGRAMS_NEED_X += test-match-trees
468+
TEST_PROGRAMS_NEED_X += test-mergesort
468469
TEST_PROGRAMS_NEED_X += test-mktemp
469470
TEST_PROGRAMS_NEED_X += test-obj-pool
470471
TEST_PROGRAMS_NEED_X += test-parse-options
@@ -578,6 +579,7 @@ LIB_H += log-tree.h
578579
LIB_H += mailmap.h
579580
LIB_H += merge-file.h
580581
LIB_H += merge-recursive.h
582+
LIB_H += mergesort.h
581583
LIB_H += notes.h
582584
LIB_H += notes-cache.h
583585
LIB_H += notes-merge.h
@@ -681,6 +683,7 @@ LIB_OBJS += mailmap.o
681683
LIB_OBJS += match-trees.o
682684
LIB_OBJS += merge-file.o
683685
LIB_OBJS += merge-recursive.o
686+
LIB_OBJS += mergesort.o
684687
LIB_OBJS += name-hash.o
685688
LIB_OBJS += notes.o
686689
LIB_OBJS += notes-cache.o

mergesort.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include "cache.h"
2+
#include "mergesort.h"
3+
4+
struct mergesort_sublist {
5+
void *ptr;
6+
unsigned long len;
7+
};
8+
9+
static void *get_nth_next(void *list, unsigned long n,
10+
void *(*get_next_fn)(const void *))
11+
{
12+
while (n-- && list)
13+
list = get_next_fn(list);
14+
return list;
15+
}
16+
17+
static void *pop_item(struct mergesort_sublist *l,
18+
void *(*get_next_fn)(const void *))
19+
{
20+
void *p = l->ptr;
21+
l->ptr = get_next_fn(l->ptr);
22+
l->len = l->ptr ? (l->len - 1) : 0;
23+
return p;
24+
}
25+
26+
void *mergesort(void *list,
27+
void *(*get_next_fn)(const void *),
28+
void (*set_next_fn)(void *, void *),
29+
int (*compare_fn)(const void *, const void *))
30+
{
31+
unsigned long l;
32+
33+
if (!list)
34+
return NULL;
35+
for (l = 1; ; l *= 2) {
36+
void *curr;
37+
struct mergesort_sublist p, q;
38+
39+
p.ptr = list;
40+
q.ptr = get_nth_next(p.ptr, l, get_next_fn);
41+
if (!q.ptr)
42+
break;
43+
p.len = q.len = l;
44+
45+
if (compare_fn(p.ptr, q.ptr) > 0)
46+
list = curr = pop_item(&q, get_next_fn);
47+
else
48+
list = curr = pop_item(&p, get_next_fn);
49+
50+
while (p.ptr) {
51+
while (p.len || q.len) {
52+
void *prev = curr;
53+
54+
if (!p.len)
55+
curr = pop_item(&q, get_next_fn);
56+
else if (!q.len)
57+
curr = pop_item(&p, get_next_fn);
58+
else if (compare_fn(p.ptr, q.ptr) > 0)
59+
curr = pop_item(&q, get_next_fn);
60+
else
61+
curr = pop_item(&p, get_next_fn);
62+
set_next_fn(prev, curr);
63+
}
64+
p.ptr = q.ptr;
65+
p.len = l;
66+
q.ptr = get_nth_next(p.ptr, l, get_next_fn);
67+
q.len = q.ptr ? l : 0;
68+
69+
}
70+
set_next_fn(curr, NULL);
71+
}
72+
return list;
73+
}

mergesort.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef MERGESORT_H
2+
#define MERGESORT_H
3+
4+
void *mergesort(void *list,
5+
void *(*get_next_fn)(const void *),
6+
void (*set_next_fn)(void *, void *),
7+
int (*compare_fn)(const void *, const void *));
8+
9+
#endif

test-mergesort.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include "cache.h"
2+
#include "mergesort.h"
3+
4+
struct line {
5+
char *text;
6+
struct line *next;
7+
};
8+
9+
static void *get_next(const void *a)
10+
{
11+
return ((const struct line *)a)->next;
12+
}
13+
14+
static void set_next(void *a, void *b)
15+
{
16+
((struct line *)a)->next = b;
17+
}
18+
19+
static int compare_strings(const void *a, const void *b)
20+
{
21+
const struct line *x = a, *y = b;
22+
return strcmp(x->text, y->text);
23+
}
24+
25+
int main(int argc, const char **argv)
26+
{
27+
struct line *line, *p = NULL, *lines = NULL;
28+
struct strbuf sb = STRBUF_INIT;
29+
30+
for (;;) {
31+
if (strbuf_getwholeline(&sb, stdin, '\n'))
32+
break;
33+
line = xmalloc(sizeof(struct line));
34+
line->text = strbuf_detach(&sb, NULL);
35+
if (p) {
36+
line->next = p->next;
37+
p->next = line;
38+
} else {
39+
line->next = NULL;
40+
lines = line;
41+
}
42+
p = line;
43+
}
44+
45+
lines = mergesort(lines, get_next, set_next, compare_strings);
46+
47+
while (lines) {
48+
printf("%s", lines->text);
49+
lines = lines->next;
50+
}
51+
return 0;
52+
}

0 commit comments

Comments
 (0)