双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
为了练习,我这次设置了哨兵结点,哨兵结点,我个人理解就是我们一般意义上的头结点(是链表的一个附加结点,数据域不存储任何信息的),只不过是链表的两端都放了一个,所以形象的称之为“哨兵”。所以和单链表的基本操作中一样,链表的有效结点应该从第二个开始。
哨兵节点的引入的方便性在于不需要对线性表的第一个节点单独进行处理,比如在某个位置上插入一个节点,一般的方法是找到该位置上原来节点的前一个节点,然后将新节点加在后面,如果没有哨兵节点的话,当插入位置为线性表的第一个位置的时候,需要考虑该位置上原来的节点并没有前驱节点;而如果有哨兵节点的话,线性表的每个位置的节点都有前驱节点,因此可以统一处理。(注意:哨兵节点根本不出现在线性表中,所以虽然它没有前驱,但是前面的那句话并不矛盾)
#include <stdio.h>
#include <stdlib.h>#include <string.h>
typedef struct _node {//类型别名
void *data;
struct _node *prior;//前驱指针
struct _node *next;//后继指针
}NODE;
typedef struct DoubleLinkList {
NODE *head;
NODE *last;
int length;
}DLList;
DLList *CreateList(); // 创建双链表
int AppendList(DLList *l, void *data, int size); // 在链表后追加结点
int InsertList(DLList *l, int pos, void *data, int size); // 在 pos 位置的元素前插入
int DeleteNode(DLList *l, int pos, void *e, int size); // 删除位置为pos的结点,并用e返回被删除的结点数据
void PrintList(DLList *l, void (*printnode)(void *)); // 打印
void ClearList(DLList *l); // 清空链表
void DestoryList(DLList **l); // 销毁链表
double d[5] = {10.23, 34.23, 54.65, 122, 35.5};
void PrintData(void *data)
{
double *d = (double *)data;
printf("d = %lf\n",*d);
}
int main()
{
int i;
double e;
DLList *list = CreateList();
for (i = 0; i < 4; ++i){
AppendList(list, &d[i], sizeof(d[i]));
}
PrintList(list,PrintData);
printf("\n\n");
InsertList(list, 2, &d[4], sizeof(d[4]));
PrintList(list,PrintData);
printf("\n\n");
DeleteNode(list, 1, &e, sizeof(double));
printf("被删除的元素是:");
printf("%lf\n\n",e);
DestoryList(&list);
if (NULL == list)
printf("list is NULL\n");
else
printf("list not NULL\n");
return 0;
}
// 创建双链表
DLList *CreateList()
{
DLList *l = (DLList *)malloc(sizeof(DLList));
if (NULL == l)
exit(0);
l->head = (NODE *)malloc(sizeof(NODE)); // 设置哨兵结点,可方便双链表的操作
if (NULL == l->head)
exit(0);
memset(l->head, 0, sizeof(NODE));
l->last = (NODE *)malloc(sizeof(NODE));
if (NULL == l->last)
exit(0);
memset(l->last, 0, sizeof(NODE));
l->head->next = l->last;
l->last->prior = l->head;
l->length = 0;
return l;
}
// 在链表后追加结点
int AppendList(DLList *l, void *data, int size)
{
NODE *p = NULL;
if (NULL == l || NULL == data)
return 0;
p = (NODE *)malloc(sizeof(NODE));
if (NULL == p)
return 0;
p->data = malloc(size);
if (NULL == p->data){
free(p);
return 0;
}
memcpy(p->data, data, size);
p->next = l->last;
p->prior = l->last->prior;
l->last->prior->next = p;
l->last->prior = p;
l->length++;
return 1;
}
// 在 pos 位置的元素前插入
int InsertList(DLList *l, int pos, void *data, int size)
{
NODE *p = NULL, *q = NULL;
int i = 0;
if (NULL == l || NULL == data || pos < 1 || pos > l->length)
return 0;
p = (NODE *)malloc(sizeof(NODE));
if (NULL == p)
return 0;
p->data = malloc(size);
if (NULL == p->data){
free(p);
return 0;
}
memcpy(p->data, data, size);
q = l->head;
while (i < pos - 1){
q = q->next;
++i;
}
p->next = q->next;
q->next = p;
p->prior = q;
p->next->prior = p;
l->length++;
return 1;
}
// 删除位置为pos的结点,并用e返回被删除的结点数据
int DeleteNode(DLList *l, int pos, void *e, int size)
{
NODE *p = NULL, *q = NULL;
int i = 0;
if (NULL == l || pos < 1 || pos > l->length)
return 0;
p = l->head;
while (i < pos - 1){ // 这里遍历到了pos位置的前一个结点,还可以直接遍历到pos位置的结点上,通过prior操作删除
p = p->next;
}
q = p->next;
memcpy(e, q->data, size);
p->next = q->next;
q->next->prior = p;
free(q->data);
free(q);
l->length--;
return 1;
}
// 打印
void PrintList(DLList *l, void (*printnode)(void *))
{
NODE *p = NULL;
int i = 0;
if (NULL == l || NULL == printnode)
return;
p = l->head;
while (i < l->length){
p = p->next;
printnode(p->data);
++i;
}
}
// 清空链表
void ClearList(DLList *l)
{
NODE *p = NULL;
if (NULL == l->head->next)
return;
p = l->head->next;
l->head->next = p->next;
free(p);
if (l->head->next != l->last) // 如果p的下一个结点不是尾结点,防止删掉尾结点
ClearList(l); // 这里用递归
}
// 销毁链表
void DestoryList(DLList **l)
{
DLList *p = *l;
if (NULL == p)
return;
ClearList(p);
free(p->head); // 释放哨兵结点
free(p->last);
free(p);
*l = NULL;
}