Skip to content

Commit 5f31577

Browse files
authored
linked list cleanup (#26)
* linkedlist example; better insert when too large position provided * fix another edge case for doubly linked lists; add example file for doubly linked list * better handling -1 insert when no elements present * simplify llist insert node; fix edge case * clean-up linked lists code for maintainability and readability; fix edge case * add non-alloc check * remove unnecessary parens
1 parent aa09e1a commit 5f31577

9 files changed

Lines changed: 323 additions & 149 deletions

File tree

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ examples: libraries
4141
$(CC) $(STD) $(DISTDIR)/bitarray-lib.o $(EXAMPLEDIR)/bitarray_example.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/ex_bitarray
4242
$(CC) $(STD) $(DISTDIR)/fileutils-lib.o $(EXAMPLEDIR)/fileutils_example.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/ex_fileutils
4343
$(CC) $(STD) $(DISTDIR)/stringlib.o $(EXAMPLEDIR)/stringlib_example.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/ex_stringlib
44+
$(CC) $(STD) $(DISTDIR)/llist-lib.o $(EXAMPLEDIR)/linkedlist_example.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/ex_linkedlist
45+
$(CC) $(STD) $(DISTDIR)/dllist-lib.o $(EXAMPLEDIR)/doublylinkedlist_example.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/ex_doublylinkedlist
4446

4547
clean:
4648
rm -rf ./$(DISTDIR)/*
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*******************************************************************************
2+
* Demonstrate some of the uses of the doubly linked list datastructure by
3+
* adding people into a queue! We will make a person struct, to show it being
4+
* used with a non-standard datatype.
5+
*
6+
* NOTE: One can make a queue (FIFO) using the data structure by making a
7+
* macro for pop and push to ensure that one always pushes to the tail (-1)
8+
* and pops from the front (0). A stack (LIFO) is best implemented using a
9+
* doubly linked list pushes to the front and pops from the tail.
10+
*
11+
* Use -v to include more debugging information
12+
*******************************************************************************/
13+
14+
15+
#include <stdio.h>
16+
#include <stdlib.h>
17+
#include <stdbool.h>
18+
#include <string.h>
19+
#include <time.h>
20+
#include <assert.h>
21+
#include "../src/dllist.h"
22+
23+
24+
typedef struct person {
25+
int age;
26+
char gender;
27+
int height; /* feet */
28+
int id; /* order of getting here; for testing */
29+
} _person;
30+
31+
32+
#define NUM_ELEMENTS 1000000
33+
34+
int main(int argc, char const *argv[]) {
35+
bool verbose = false;
36+
37+
if (argc == 2 && strcmp(argv[1], "-v") == 0) {
38+
verbose = true;
39+
}
40+
int i;
41+
time_t t;
42+
srand((unsigned) time(&t));
43+
44+
dllist_t l = dll_init();
45+
46+
for (i = 0; i < NUM_ELEMENTS; i++) {
47+
_person* p = malloc(sizeof(_person));
48+
p->age = rand() % 110 + 1;
49+
p->gender = rand() % 2 == 0 ? 'm' : 'f';
50+
p->height = rand() % 7 + 1;
51+
p->id = i;
52+
if (dll_insert(l, p, -1) == DLL_FAILURE) { /* we want to append to the end each time */
53+
printf("Failed to allocate memory!\n");
54+
}
55+
56+
assert(dll_num_elements(l) == (unsigned int)(i + 1));
57+
}
58+
59+
int cnt = 0;
60+
dll_node* n;
61+
dll_reverse_traverse(l, n) { /* traverse for testing in reverse (from tail) */
62+
_person* q = (_person*) n->data;
63+
assert(q->id == NUM_ELEMENTS - ++cnt);
64+
}
65+
cnt = 0;
66+
dll_traverse(l, n) { /* traverse for testing from the head node */
67+
_person* q = (_person*) n->data;
68+
assert(q->id == cnt++);
69+
}
70+
71+
for (i = 1; i <= NUM_ELEMENTS; i++) {
72+
_person* w = (_person*) dll_remove(l, 0);
73+
assert((unsigned int)(NUM_ELEMENTS - i) == dll_num_elements(l));
74+
if (verbose) {
75+
printf("person: %d\tage: %d \tgender: %c\theight (feet): %d\n", w->id, w->age, w->gender, w->height);
76+
}
77+
free(w); /* it was pop'd but not yet free'd */
78+
}
79+
80+
dll_free(l);
81+
82+
printf("Completed successfully!\n");
83+
return 0;
84+
}

examples/linkedlist_example.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*******************************************************************************
2+
* Demonstrate some of the uses of the linked list datastructure by
3+
* adding points as a stack! We will make a point struct, to show it being
4+
* used with a non-standard datatype.
5+
*
6+
* NOTE: One can make a stake using the data structure by making a macro for
7+
* pop and push to ensure that one always pushes to the front (0) and pops
8+
* from the front. A stack (LIFO) is best implemented using a singly linked
9+
* list that always does all work from the head node.
10+
*
11+
* Use -v to include more debugging information
12+
*******************************************************************************/
13+
14+
#include <stdio.h>
15+
#include <stdlib.h>
16+
#include <stdbool.h>
17+
#include <string.h>
18+
#include <time.h>
19+
#include <assert.h>
20+
#include "../src/llist.h"
21+
22+
typedef struct point {
23+
int x;
24+
int y;
25+
int z;
26+
} _point;
27+
28+
29+
#define NUM_ELEMENTS 9000
30+
31+
32+
int main(int argc, char const *argv[]) {
33+
bool verbose = false;
34+
35+
if (argc == 2 && strcmp(argv[1], "-v") == 0) {
36+
verbose = true;
37+
}
38+
39+
time_t t;
40+
srand((unsigned) time(&t));
41+
42+
llist_t l = ll_init();
43+
44+
/* build out a list of points and add them to the stack*/
45+
int i;
46+
for (i = 0; i < NUM_ELEMENTS; i++) {
47+
_point* p = malloc(sizeof(_point));
48+
p->x = i;
49+
p->y = rand() % 50;
50+
p->z = i + 1;
51+
ll_insert(l, p, 0); /* for a stack, everything must be up front */
52+
}
53+
54+
/* traverse the list and make sure that each point's x is less than the previous */
55+
int cnt = 0;
56+
int prev = NUM_ELEMENTS + 1;
57+
ll_node* n;
58+
ll_traverse(l, n) { /* this is a macro to simplify n = n->next type loops */
59+
_point* q = (_point*) n->data;
60+
assert(prev > q->x);
61+
prev = q->x;
62+
}
63+
64+
/* for a stack, we need to "pop" or remove the first element each time */
65+
for (i = 1; i <= NUM_ELEMENTS; i++) {
66+
_point* w = (_point*) ll_remove(l, 0);
67+
assert(NUM_ELEMENTS - i == w->x);
68+
assert((unsigned int)(NUM_ELEMENTS - i) == ll_num_elements(l));
69+
if (verbose) {
70+
printf("point: %d\tx: %d\ty: %d\tz: %d\n", cnt++, w->x, w->y, w->z);
71+
}
72+
free(w); /* it was pop'd but not yet free'd */
73+
}
74+
75+
assert(0 == ll_num_elements(l));
76+
77+
ll_free_alt(l, true);
78+
printf("Completed successfully!\n");
79+
return 0;
80+
}

src/dllist.c

Lines changed: 59 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#include <stdlib.h>
22
#include <stdbool.h>
3+
#include <stdio.h>
34
#include "dllist.h"
45

56

6-
77
typedef struct __doubly_linked_list {
88
dll_node* head;
99
dll_node* tail;
@@ -16,7 +16,6 @@ dllist_t dll_init() {
1616
l->head = NULL;
1717
l->tail = NULL;
1818
l->elms = 0;
19-
2019
return l;
2120
}
2221

@@ -50,31 +49,11 @@ dll_node* dll_last_node(dllist_t l) {
5049
}
5150

5251
int dll_append(dllist_t l, void* data) {
53-
dll_node* n = calloc(1, sizeof(dll_node));
54-
if (n == NULL)
55-
return DLL_FAILURE;
56-
57-
n->data = data;
58-
n->next = NULL;
59-
n->prev = NULL;
60-
61-
if (l->elms == 0) {
62-
l->tail = n;
63-
l->head = n;
64-
++(l->elms);
65-
return DLL_SUCCESS;
66-
}
67-
68-
l->tail->next = n;
69-
n->prev = l->tail;
70-
l->tail = n;
71-
++(l->elms);
72-
73-
return DLL_SUCCESS;
52+
return dll_insert(l, data, -1);
7453
}
7554

7655
int dll_insert(dllist_t l, void * data, int idx) {
77-
if (idx < 0 && idx <= (-1 * (int)l->elms))
56+
if (idx != -1 && idx < 0 && idx <= (-1 * (int)l->elms))
7857
return DLL_FAILURE;
7958

8059
if (idx < 0)
@@ -89,38 +68,39 @@ int dll_insert(dllist_t l, void * data, int idx) {
8968
n->next = NULL;
9069
n->prev = NULL;
9170

92-
if (idx == 0) {
93-
n->next = (l->head);
71+
if (l->elms == 0) { /* first node to be added edge case */
72+
l->head = n;
73+
l->tail = n;
74+
} else if (idx == 0) { /* first node */
75+
n->next = l->head;
9476
n->next->prev = n;
9577
l->head = n;
96-
++(l->elms);
97-
return DLL_SUCCESS;
98-
} else if (idx >= (int)l->elms) {
78+
} else if (idx >= (int)l->elms) { /* last node */
9979
l->tail->next = n;
10080
n->prev = l->tail; /* we want the tail to point to the end */
10181
l->tail = n;
102-
++(l->elms);
103-
return DLL_SUCCESS;
104-
}
105-
106-
int i;
107-
dll_node* t;
108-
if (idx <= (int)l->elms / 2) {
109-
t = dll_first_node(l);
110-
for (i = 1; i < idx; i++)
111-
t = t->next;
112-
} else {
113-
/* start from the tail and go backwards! */
114-
int stop = l->elms - idx;
115-
t = dll_last_node(l);
116-
for (i = 0; i < stop; i++)
117-
t = t->prev;
82+
} else { /* mid node; determine which direction would be fastest to get to it */
83+
int i;
84+
dll_node* t;
85+
if (idx <= (int)l->elms / 2) {
86+
t = dll_first_node(l);
87+
for (i = 1; i < idx; i++)
88+
t = t->next;
89+
} else {
90+
/* start from the tail and go backwards! */
91+
int stop = l->elms - idx;
92+
t = dll_last_node(l);
93+
for (i = 0; i < stop; i++)
94+
t = t->prev;
95+
}
96+
97+
n->next = t->next;
98+
t->next = n;
99+
n->next->prev = n;
100+
n->prev = t;
118101
}
119102

120-
n->next = t->next;
121-
t->next = n;
122-
n->next->prev = n;
123-
n->prev = t;
103+
++(l->elms);
124104
return DLL_SUCCESS;
125105
}
126106

@@ -137,52 +117,39 @@ void* dll_remove(dllist_t l, int idx) {
137117
if (idx < 0)
138118
idx = l->elms + idx;
139119

140-
void* ret;
141-
dll_node* n;
142-
if (l->elms == 1) { /* this is the oddest edge case */
143-
n = l->head;
144-
ret = n->data;
145-
l->head = NULL;
146-
l->tail = NULL;
147-
--l->elms;
148-
free(n);
149-
return ret;
150-
} else if (idx == 0) { /* handle edge cases */
151-
n = l->head;
152-
ret = n->data;
120+
void* data;
121+
dll_node* ret;
122+
if (l->elms == 1) { /* remove the final node */
123+
ret = l->head;
124+
l->head = l->tail = NULL;
125+
} else if (idx == 0) { /* remove the first node */
126+
ret = l->head;
153127
l->head = l->head->next;
154128
l->head->prev = NULL;
155-
--l->elms;
156-
free(n);
157-
return ret;
158129
} else if (idx >= (int)(l->elms - 1)) {
159-
n = l->tail;
160-
ret = n->data;
161-
l->tail = n->prev;
162-
n->prev->next = NULL;
163-
--l->elms;
164-
free(n);
165-
return ret;
166-
}
167-
168-
int i;
169-
if (idx <= (int)l->elms / 2) {
170-
n = dll_first_node(l);
171-
for (i = 1; i < idx; i++)
172-
n = n->next;
130+
ret = l->tail;
131+
l->tail = ret->prev;
132+
ret->prev->next = NULL;
173133
} else {
174-
/* start from the tail and go backwards! */
175-
int stop = l->elms - idx;
176-
n = dll_last_node(l);
177-
for (i = 0; i < stop; i++)
178-
n = n->prev;
134+
int i;
135+
if (idx <= (int)l->elms / 2) {
136+
ret = dll_first_node(l);
137+
for (i = 1; i < idx; i++)
138+
ret = ret->next;
139+
} else {
140+
/* start from the tail and go backwards! */
141+
int stop = l->elms - idx;
142+
ret = dll_last_node(l);
143+
for (i = 0; i < stop; i++)
144+
ret = ret->prev;
145+
}
146+
147+
/* move the nodes before and after's pointers around */
148+
ret->prev->next = ret->next;
149+
ret->next->prev = ret->prev;
179150
}
180-
181-
/* mode the nodes before and after's pointers around */
182-
n->prev->next = n->next;
183-
n->next->prev = n->prev;
184-
ret = n->data;
185-
--l->elms;
186-
free(n);
187-
return ret;
151+
data = ret->data;
152+
--(l->elms);
153+
free(ret);
154+
return data;
188155
}

src/dllist.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*** Author: Tyler Barrus
77
*** email: barrust@gmail.com
88
***
9-
*** Version: 0.1.0
9+
*** Version: 0.1.1
1010
*** Purpose: Generic doubly linked list implementation
1111
***
1212
*** License: MIT 2019
@@ -32,11 +32,9 @@
3232
***
3333
*******************************************************************************/
3434

35-
3635
#include <stdbool.h>
3736

3837

39-
4038
typedef struct __doubly_linked_list dllist;
4139
typedef struct __doubly_linked_list *dllist_t;
4240

0 commit comments

Comments
 (0)