C语言数据结构之双向链表(LIST)的实现
- 与单向链表相比,双向链表数据结构中多出一个指向链表前一个节点的节点指针prev!!!
双向链表数据类型的定义
- 双向链表的创建 list_new
- 双向链表的释放 list_free
- 尾部追加 list_append
- 头部追加 list_prepend
- 逐项操作 list_foreach
- 测试函数,字符串尾部追加结果:AAA BBB CCC DDD EEE
- 字符串头部追加结果:EEE DDD CCC BBB AAA
代码如下:
c
/* filename : list.c */
#include <stdio.h>
#include <stdlib.h>
/**/
typedef void (*LSFunc) (void *data);
/**/
typedef struct _ListNode ListNode;
struct _ListNode {
void *data;
ListNode *prev, *next;
};
/**/
ListNode *
list_node_new (void *data)
{
ListNode *node = (ListNode*) malloc (sizeof(ListNode));
node->data = data;
node->prev = NULL;
node->next = NULL;
return node;
}
/**/
void
list_free (ListNode *node)
{
ListNode *tmp = node;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
ListNode *pnode = tmp->next;
free (tmp);
tmp = pnode;
}
}
/**/
ListNode *
list_append (ListNode *head, void *data)
{
ListNode *tmp = head, *node;
while (tmp->next != NULL)
tmp = tmp->next;
node = list_node_new (data);
node->prev = tmp;
tmp->next = node;
return head;
}
/**/
ListNode *
list_prepend (ListNode *head, void *data)
{
ListNode *tmp = head, *node;
while (tmp->prev != NULL)
tmp = tmp->prev;
node = list_node_new (data);
node->next = tmp;
tmp->prev = node;
return node;
}
/**/
void
list_foreach (ListNode *node, LSFunc lsfunc)
{
ListNode *tmp = node;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
lsfunc (tmp->data);
tmp = tmp->next;
}
}
/* ---------------------------------------- */
/**/
void
out_string (void *data)
{
printf ("%s ", (char*)data);
}
/**/
void
test_list_append (void)
{
ListNode *ls;
char *buf[5] = {"AAA", "BBB", "CCC", "DDD", "EEE"};
ls = list_node_new (buf[0]);
for (int i = 1; i < 5; i++)
ls = list_append (ls, buf[i]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
list_free (ls);
}
/**/
void
test_list_prepend (void)
{
ListNode *ls;
char *buf[5] = {"AAA", "BBB", "CCC", "DDD", "EEE"};
ls = list_node_new (buf[0]);
for (int i = 1; i < 5; i++)
ls = list_prepend (ls, buf[i]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
list_free (ls);
}
/**/
int
main (int argc, char *argv[])
{
test_list_append ();
test_list_prepend ();
return 0;
}
/* --[<\|/>]-- */
编译运行,检查内存,情况如下:
bash
songvm@ubuntu:~/works/xdn/loo$ gcc list.c -o list
songvm@ubuntu:~/works/xdn/loo$ ./list
[ AAA BBB CCC DDD EEE ]
[ EEE DDD CCC BBB AAA ]
songvm@ubuntu:~/works/xdn/loo$ valgrind --leak-check=yes ./list
==3688== Memcheck, a memory error detector
==3688== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3688== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==3688== Command: ./list
==3688==
[ AAA BBB CCC DDD EEE ]
[ EEE DDD CCC BBB AAA ]
==3688==
==3688== HEAP SUMMARY:
==3688== in use at exit: 0 bytes in 0 blocks
==3688== total heap usage: 11 allocs, 11 frees, 1,264 bytes allocated
==3688==
==3688== All heap blocks were freed -- no leaks are possible
==3688==
==3688== For counts of detected and suppressed errors, rerun with: -v
==3688== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
songvm@ubuntu:~/works/xdn/loo$
双向链表求长度LENGTH 插入INSERT
- 双向链表求长度 list_length
- 双向链表插入节点 list_insert
代码如下:
c
/**/
int
list_length (ListNode *head)
{
int len = 0;
ListNode *tmp = head;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
tmp = tmp->next;
len++;
}
return len;
}
/**/
ListNode *
list_insert (ListNode *head, int nth, void *data)
{
int idx = 0;
ListNode *tmp = head;//, *node;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
idx = idx + 1;
if (idx == nth)
{
ListNode *node = list_node_new (data);
node->next = tmp->next;
node->prev = tmp;
tmp->next = node;
break;
}
tmp = tmp->next;
}
return head;
}
- 测试函数,创建双向链表AAA BBB CCC DDD EEE,在第3个索引处插入XXXX,目标结果AAA BBB CCC XXXX DDD EEE
代码如下:
c
/**/
void
test_list_insert (void)
{
ListNode *ls;
char *buf[6] = {"AAA", "BBB", "CCC", "DDD", "EEE", "XXXX"};
ls = list_node_new (buf[0]);
for (int i = 1; i < 5; i++)
ls = list_append (ls, buf[i]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
printf ("List length is %d\n", list_length (ls));
ls = list_insert (ls, 3, buf[5]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
printf ("List length is %d\n", list_length (ls));
list_free (ls);
}
编译运行,结果如预期,输出如下:
bash
songvm@ubuntu:~/works/xdn/loo$ gcc list.c -o list
songvm@ubuntu:~/works/xdn/loo$ ./list
[ AAA BBB CCC DDD EEE ]
List length is 5
[ AAA BBB CCC XXXX DDD EEE ]
List length is 6
songvm@ubuntu:~/works/xdn/loo$
双向链表的连接concat
- 双向链表连接函数list_concat,将两个双向链表连接成为一个双向链表
代码如下:
c
/**/
ListNode*
list_concat (ListNode *lsa, ListNode *lsb)
{
ListNode *tmp = lsa, *node = lsb;
while (tmp->next != NULL)
tmp = tmp->next;
while (node->prev != NULL)
node = node->prev;
tmp->next = node;
node->prev = tmp;
while (tmp->prev != NULL)
tmp = tmp->prev;
return tmp;
}
- 测试将两个链表内容为:AAA BBB CCC DDD和XXX YYY ZZZ KKK,连接成为一个链表
代码如下:
c
/**/
void
test_list_concat (void)
{
char *bufa[4] = {"AAA", "BBB", "CCC", "DDD"};
char *bufb[4] = {"XXX", "YYY", "ZZZ", "KKK"};
ListNode *lna, *lnb, *lnx;
lna = list_node_new (bufa[0]);
for (int i = 1; i < 4; i++)
lna = list_append (lna, bufa[i]);
printf ("[ ");
list_foreach (lna, out_string);
printf ("]\n");
lnb = list_node_new (bufb[0]);
for (int i = 1; i < 4; i++)
lnb = list_append (lnb, bufb[i]);
printf ("[ ");
list_foreach (lnb, out_string);
printf ("]\n");
lnx = list_concat (lna, lnb);
printf ("[ ");
list_foreach (lnx, out_string);
printf ("]\n");
list_free (lnx);
}
编译运行,检查内存,结果如下:
bash
songvm@ubuntu:~/works/xdn/loo$ gcc list.c -o list
songvm@ubuntu:~/works/xdn/loo$ ./list
[ AAA BBB CCC DDD ]
[ XXX YYY ZZZ KKK ]
[ AAA BBB CCC DDD XXX YYY ZZZ KKK ]
songvm@ubuntu:~/works/xdn/loo$ valgrind --leak-check=yes ./list
==2951== Memcheck, a memory error detector
==2951== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2951== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2951== Command: ./list
==2951==
[ AAA BBB CCC DDD ]
[ XXX YYY ZZZ KKK ]
[ AAA BBB CCC DDD XXX YYY ZZZ KKK ]
==2951==
==2951== HEAP SUMMARY:
==2951== in use at exit: 0 bytes in 0 blocks
==2951== total heap usage: 9 allocs, 9 frees, 1,216 bytes allocated
==2951==
==2951== All heap blocks were freed -- no leaks are possible
==2951==
==2951== For counts of detected and suppressed errors, rerun with: -v
==2951== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
songvm@ubuntu:~/works/xdn/loo$
双向链表倒转REVERSE 取链表头节点FIRST 取链表尾节点LAST
- 双向链表倒转 list_reverse
- 取链表头节点 list_first
- 取链表尾节点 list_last
代码如下:
c
/* list reverse tail to head */
ListNode*
list_reverse (ListNode *ls)
{
ListNode *tmp = ls, *node = NULL;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
ListNode *pn = tmp;
tmp = tmp->next;
pn->next = node;
pn->prev = NULL;
if (node != NULL)
node->prev = pn;
node = pn;
}
return node;
}
/* get list first node */
ListNode*
list_first (ListNode *ls)
{
ListNode *tmp = ls;
while (tmp->prev != NULL)
tmp = tmp->prev;
return tmp;
}
/* get list last node */
ListNode*
list_last (ListNode *ls)
{
ListNode *tmp = ls;
while (tmp->next != NULL)
tmp = tmp->next;
return tmp;
}
- 测试函数,创建链表字符串AAA到HHH,倒转输出内容,再由尾部向前依次输出内容
代码如下:
c
/**/
void
test_list_reverse (void)
{
char *buf[8] = {"AAA", "BBB", "CCC", "DDD", "EEE", "FFF", "GGG", "HHH"};
ListNode *ls;
ls = list_node_new (buf[0]);
for (int i = 1; i < 8; i++)
ls = list_append (ls, buf[i]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
ls = list_reverse (ls);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
{
ListNode *tmp = ls;
while (tmp->next != NULL)
tmp = tmp->next;
printf ("[ ");
while (tmp != NULL)
{
printf ("%s ", (char*)(tmp->data));
tmp = tmp->prev;
}
printf ("]\n");
}
list_free (ls);
}
编译运行,达到预期,效果如下:
bash
songvm@ubuntu:~/works/xdn/loo$ gcc list.c -o list
songvm@ubuntu:~/works/xdn/loo$ ./list
[ AAA BBB CCC DDD EEE FFF GGG HHH ]
[ HHH GGG FFF EEE DDD CCC BBB AAA ]
[ AAA BBB CCC DDD EEE FFF GGG HHH ]
songvm@ubuntu:~/works/xdn/loo$
双向链表删除指定节点 REMOVE_NTH
- 双向链表删除指定节点 list_remove_nth
代码如下:
c
/**/
ListNode*
list_remove_nth (ListNode *ls, int nth)
{
int idx = 0;
ListNode *tmp = ls, *pn = ls;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
if (idx == nth)
{
if (idx == 0)
{
ls = tmp->next;
ls->prev = NULL;
free (tmp);
break;
}
pn->next = tmp->next;
tmp->next->prev = pn;
free (tmp);
break;
}
idx = idx + 1;
pn = tmp;
tmp = tmp->next;
}
return ls;
}
- 测试函数,创建链表,删除第1第2节点,输出链表内容
代码如下:
c
/**/
void
test_list_remove (void)
{
char *txt[5] = {"AAAA", "BBBB", "CCCC", "DDDD", "EEEE"};
ListNode *ls;
ls = list_node_new (txt[0]);
for (int i = 1; i < 5; i++)
ls = list_append (ls, txt[i]);
list_foreach (ls, out_string); printf ("\n");
ls = list_remove_nth (ls, 1);
ls = list_remove_nth (ls, 2);
list_foreach (ls, out_string); printf ("\n");
list_free (ls);
}
编译运行,达到预期,效果如下:
bash
songvm@ubuntu:~/works/xdn/loo$ gcc list.c -o list
songvm@ubuntu:~/works/xdn/loo$ ./list
AAAA BBBB CCCC DDDD EEEE
AAAA CCCC EEEE
songvm@ubuntu:~/works/xdn/loo$
判断链表是否为循环链表
判断链表是否为循环链表is_round
代码如下:
c
/* if list is a round return 1 else return 0 */
int
list_isround (ListNode *ls)
{
ListNode *tmp = ls;
while (tmp != NULL)
{
ListNode *node = tmp->next;
if (ls == node) return 1;
tmp = node;
}
return 0;
}
- 测试函数,创建链表,取尾节点,尾节点next指针指向头节点,头节点的prev指针指向尾节点,形成循环链表
代码如下:
c
/**/
void
test_list_round (void)
{
char *txt[5] = {"AAAA", "BBBB", "CCCC", "DDDD", "EEEE"};
ListNode *head, *tail;
head = list_node_new (txt[0]);
for (int i = 1; i < 5; i++)
head = list_append (head, txt[i]);
tail = list_last (head);
tail->next = head; //link round
head->prev = tail; //link round
if (list_isround (head))
printf ("Double list is round!\n");
else
printf ("Double list not round!\n");
tail->next = NULL; //cut round
head->prev = NULL; //cut round
if (list_isround (head))
printf ("Double list is round!\n");
else
printf ("Double list not round!\n");
list_free (head);
}
编译运行,达到预期效果,输出如下:
bash
songvm@ubuntu:~/works/xdn/loo$ gcc list.c -o list
songvm@ubuntu:~/works/xdn/loo$ ./list
Double list is round!
Double list not round!
songvm@ubuntu:~/works/xdn/loo$
完整代码如下:
c
/* filename : list.c */
#include <stdio.h>
#include <stdlib.h>
/* compile : gcc list.c -o list */
/* run : ./list */
/* define function pointer for list foreach */
typedef void (*LSFunc) (void *data);
/* define ListNode datatype */
typedef struct _ListNode ListNode;
struct _ListNode {
void *data;
ListNode *prev, *next;
};
/* create a ListNode with data */
ListNode *
list_node_new (void *data)
{
ListNode *node = (ListNode*) malloc (sizeof(ListNode));
node->data = data;
node->prev = NULL;
node->next = NULL;
return node;
}
/* free a list, from head to tail every node */
void
list_free (ListNode *node)
{
ListNode *tmp = node;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
ListNode *pnode = tmp->next;
free (tmp);
tmp = pnode;
}
}
/* append a ListNode with data to head */
ListNode *
list_append (ListNode *head, void *data)
{
ListNode *tmp = head, *node;
while (tmp->next != NULL)
tmp = tmp->next;
node = list_node_new (data);
node->prev = tmp;
tmp->next = node;
return head;
}
/* prev append a ListNode with data to head */
ListNode *
list_prepend (ListNode *head, void *data)
{
ListNode *tmp = head, *node;
while (tmp->prev != NULL)
tmp = tmp->prev;
node = list_node_new (data);
node->next = tmp;
tmp->prev = node;
return node;
}
/* foreach listnode run lsfunc for data */
void
list_foreach (ListNode *node, LSFunc lsfunc)
{
ListNode *tmp = node;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
lsfunc (tmp->data);
tmp = tmp->next;
}
}
/* get list length */
int
list_length (ListNode *head)
{
int len = 0;
ListNode *tmp = head;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
tmp = tmp->next;
len++;
}
return len;
}
/* insert a ListNode with data at nth in head */
ListNode *
list_insert (ListNode *head, int nth, void *data)
{
int idx = 0;
ListNode *tmp = head;//, *node;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
idx = idx + 1;
if (idx == nth)
{
ListNode *node = list_node_new (data);
node->next = tmp->next;
node->prev = tmp;
tmp->next = node;
break;
}
tmp = tmp->next;
}
return head;
}
/* concat lsa and lsb and return a new ListNode pointer */
ListNode*
list_concat (ListNode *lsa, ListNode *lsb)
{
ListNode *tmp = lsa, *node = lsb;
while (tmp->next != NULL)
tmp = tmp->next;
while (node->prev != NULL)
node = node->prev;
tmp->next = node;
node->prev = tmp;
while (tmp->prev != NULL)
tmp = tmp->prev;
return tmp;
}
/* list reverse tail to head */
ListNode*
list_reverse (ListNode *ls)
{
ListNode *tmp = ls, *node = NULL;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
ListNode *pn = tmp;
tmp = tmp->next;
pn->next = node;
pn->prev = NULL;
if (node != NULL)
node->prev = pn;
node = pn;
}
return node;
}
/* get list first node */
ListNode*
list_first (ListNode *ls)
{
ListNode *tmp = ls;
while (tmp->prev != NULL)
tmp = tmp->prev;
return tmp;
}
/* get list last node */
ListNode*
list_last (ListNode *ls)
{
ListNode *tmp = ls;
while (tmp->next != NULL)
tmp = tmp->next;
return tmp;
}
/**/
ListNode*
list_remove_nth (ListNode *ls, int nth)
{
int idx = 0;
ListNode *tmp = ls, *pn = ls;
while (tmp->prev != NULL)
tmp = tmp->prev;
while (tmp != NULL)
{
if (idx == nth)
{
if (idx == 0)
{
ls = tmp->next;
ls->prev = NULL;
free (tmp);
break;
}
pn->next = tmp->next;
tmp->next->prev = pn;
free (tmp);
break;
}
idx = idx + 1;
pn = tmp;
tmp = tmp->next;
}
return ls;
}
/* if list is a round return 1 else return 0 */
int
list_isround (ListNode *ls)
{
ListNode *tmp = ls;
while (tmp != NULL)
{
ListNode *node = tmp->next;
if (ls == node) return 1;
tmp = node;
}
return 0;
}
/* ---------------------------------------- */
/**/
void
out_string (void *data)
{
printf ("%s ", (char*)data);
}
/**/
void
test_list_append (void)
{
ListNode *ls;
char *buf[5] = {"AAA", "BBB", "CCC", "DDD", "EEE"};
ls = list_node_new (buf[0]);
for (int i = 1; i < 5; i++)
ls = list_append (ls, buf[i]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
list_free (ls);
}
/**/
void
test_list_prepend (void)
{
ListNode *ls;
char *buf[5] = {"AAA", "BBB", "CCC", "DDD", "EEE"};
ls = list_node_new (buf[0]);
for (int i = 1; i < 5; i++)
ls = list_prepend (ls, buf[i]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
list_free (ls);
}
/**/
void
test_list_insert (void)
{
ListNode *ls;
char *buf[6] = {"AAA", "BBB", "CCC", "DDD", "EEE", "XXXX"};
ls = list_node_new (buf[0]);
for (int i = 1; i < 5; i++)
ls = list_append (ls, buf[i]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
printf ("List length is %d\n", list_length (ls));
ls = list_insert (ls, 3, buf[5]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
printf ("List length is %d\n", list_length (ls));
list_free (ls);
}
/**/
void
test_list_concat (void)
{
char *bufa[4] = {"AAA", "BBB", "CCC", "DDD"};
char *bufb[4] = {"XXX", "YYY", "ZZZ", "KKK"};
ListNode *lna, *lnb, *lnx;
lna = list_node_new (bufa[0]);
for (int i = 1; i < 4; i++)
lna = list_append (lna, bufa[i]);
printf ("[ ");
list_foreach (lna, out_string);
printf ("]\n");
lnb = list_node_new (bufb[0]);
for (int i = 1; i < 4; i++)
lnb = list_append (lnb, bufb[i]);
printf ("[ ");
list_foreach (lnb, out_string);
printf ("]\n");
lnx = list_concat (lna, lnb);
printf ("[ ");
list_foreach (lnx, out_string);
printf ("]\n");
list_free (lnx);
}
/**/
void
test_list_reverse (void)
{
char *buf[8] = {"AAA", "BBB", "CCC", "DDD", "EEE", "FFF", "GGG", "HHH"};
ListNode *ls;
ls = list_node_new (buf[0]);
for (int i = 1; i < 8; i++)
ls = list_append (ls, buf[i]);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
ls = list_reverse (ls);
printf ("[ ");
list_foreach (ls, out_string);
printf ("]\n");
{
ListNode *tmp = ls;
while (tmp->next != NULL)
tmp = tmp->next;
printf ("[ ");
while (tmp != NULL)
{
printf ("%s ", (char*)(tmp->data));
tmp = tmp->prev;
}
printf ("]\n");
}
list_free (ls);
}
/**/
void
test_list_remove (void)
{
char *txt[5] = {"AAAA", "BBBB", "CCCC", "DDDD", "EEEE"};
ListNode *ls;
ls = list_node_new (txt[0]);
for (int i = 1; i < 5; i++)
ls = list_append (ls, txt[i]);
list_foreach (ls, out_string); printf ("\n");
ls = list_remove_nth (ls, 1);
ls = list_remove_nth (ls, 2);
list_foreach (ls, out_string); printf ("\n");
list_free (ls);
}
/**/
void
test_list_round (void)
{
char *txt[5] = {"AAAA", "BBBB", "CCCC", "DDDD", "EEEE"};
ListNode *head, *tail;
head = list_node_new (txt[0]);
for (int i = 1; i < 5; i++)
head = list_append (head, txt[i]);
tail = list_last (head);
tail->next = head; //link round
head->prev = tail; //link round
if (list_isround (head))
printf ("Double list is round!\n");
else
printf ("Double list not round!\n");
tail->next = NULL; //cut round
head->prev = NULL; //cut round
if (list_isround (head))
printf ("Double list is round!\n");
else
printf ("Double list not round!\n");
list_free (head);
}
/**/
int
main (int argc, char *argv[])
{
//test_list_append ();
//test_list_prepend ();
//test_list_insert ();
//test_list_concat ();
//test_list_reverse ();
//test_list_remove ();
test_list_round ();
return 0;
}
/* --[<\|/>]-- */
下一步研究栈(Stack)数据结构!!!