首先不管是单链表还是双链表或者循环双链表,都是为了提高访问效率,比如同样的插入操作,顺序表需要用数组移动元素,访问效率比较差,相反链表只需要移动指针,而且顺序表是有最大空间的,而链表没有。
双链表格式:
struct node //双向链表
{
ElemType data; //数据域
struct node *next; //指针域 指向下一个后继方向
struct node *prior; //指向前一个前驱方向
};
结点指向 p == p->prior->next == p->next->prior
双向链表和单链表各个功能使用的区别:
1.初始化;
int LinkInit(Node **l)
{
*l = (Node *)malloc(sizeof(Node) * 1); //分配头结点 l 就是头指针
if (NULL == *l)
{
return FAILURE;
}
(*l)->next = NULL; //头结点指针域为空
(*l)->prior = NULL; //双向链表,头结点prior指针为空。可以看出相比单链表多分配一个指针
return SUCCESS;
}
2.插入函数:
int LinkInsert(Node *l, int n, ElemType e) //n 插入的位置
{
Node *p = l;
int k = 1; //k 移动的次数
if (NULL == l)
{
return FAILURE;
}
while (k < n && p != NULL)
{
p = p->next;
k++;
}
if (k > n || p == NULL)
{
return FAILURE;
}
Node *q = (Node *)malloc(sizeof(Node) * 1);
if (NULL == q)
{
return FAILURE;
}
q->data = e;
q->prior = p;//多了一步指向p头指针的前继
q->next = p->next;
p->next = q;
if (NULL != q->next) //多了一步如果q不是最后一个结点(画图理解会发现,后面p后面结点如果没有这一步会少一个前驱)
{
q->next->prior = q;
}
return SUCCESS;
}
3.浏览函数和单链表一样
int LinkTraverse(Node *l, void (*p)(ElemType))//比较函数判断是否相等
{
if (NULL == l)
{
return FAILURE;
}
Node *q = l;
while (q->next)
{
q = q->next;
p(q->data);
}
return SUCCESS;
}
4,长度函数一样
int LinkLength(Node *l)
{
if (NULL == l)
{
return FAILURE;
}
int len = 0;
Node *p = l->next;
while (p)
{
len++;
p = p->next;
}
return len;
}
5.判空函数一样
int LinkEmpty(Node *l)
{
return (l->next == NULL) ? TRUE : FALSE;
}
6删除函数:
int LinkDelete(Node *l, int p, ElemType *e)//删除位置p
{
int k = 1;
Node *q = l;
if (l == NULL)
{
return FAILURE;
}
while (k < p && q != NULL)//移动被删元素前一个
{
q = q->next;
k++;
}
if (k > p || q == NULL)
{
return FAILURE;
}
Node *n = q->next;//让n指向q后面要删的元素
*e = n->data;//将被删元素放入e中
q->next = n->next; //指向被删元素的后面一个 ,如果n是最后一个指向nuLL
if (n->next != NULL) //如果不是最后一个进入循环
{
n->next->prior = q;//让n后面的元素的前继指向q
}
free(n);释放删除元素
return SUCCESS;
}
清空函数的区别:
int LinkClear(Node *l)
{
if (NULL == l)
{
return FAILURE;
}
Node *p = l->next;
while (p)
{
l->next = p->next;
//p->next->prior = l;//可有可无,目的是让后面结点的前驱指向头结点
free(p);
p = l->next;//防止p为野指针,free(P)会让变成NULL,并将p往后指向,依次释放,直到最后一个结点
}
return SUCCESS;
}
注意:双链表没有倒叙的说法
二:双链循环链表
循环链表,和单链表最大的区别,单链表最后指向NULL;而循环链表指向头结点或者第一个结点
如图:可以看出首尾的链表,在空表的时候都是头尾都是指向自己的。
所以初始化也与单链表有一定区别:
int LinkInit(Node **l)
{
*l = (Node *)malloc(sizeof(Node) * 1); //分配头结点 l 就是头指针
if (NULL == *l)
{
return FAILURE;
}
(*l)->next = (*l); //后继指向自己
(*l)->prior = (*l); //前驱指向自己
return SUCCESS;
}
插入函数:
int LinkInsert(Node *l, int n, ElemType e) //n 插入的位置
{
Node *p = l;
int k = 1; //k 移动的次数
if (NULL == l) //入参判断
{
return FAILURE;
}
if (n > LinkLength(l) + 1) //超过最大长度报错,由于是循环链表,最后会回到头结点
{
return FAILURE;
}
while (k < n) //***
{
p = p->next;
k++;
}
if (k > n) //循环次数大于插入位置,会让数据变得不准确,比如n=0,k=1,不进入while循环,数据被送入头结点
{
return FAILURE;
}
Node *q = (Node *)malloc(sizeof(Node) * 1);
if (NULL == q)
{
return FAILURE;
}
q->data = e;
q->prior = p;
q->next = p->next;
p->next = q;
if (l != q->next) //如果q不是最后一个结点 **
{
q->next->prior = q;
}
return SUCCESS;
}
浏览函数:
int LinkTraverse(Node *l, void (*p)(ElemType))
{
if (NULL == l)
{
return FAILURE;
}
Node *q = l;
while (q->next != l) //不等于本身
{
q = q->next;
p(q->data);
}
return SUCCESS;
}
测试长度函数:
int LinkLength(Node *l)
{
if (NULL == l)
{
return FAILURE;
}
int len = 0;
Node *p = l->next;
while (p != l) //不等于本身 **
{
len++;
p = p->next;
}
return len;
}
判断是否为空函数
int LinkEmpty(Node *l)
{
return (l->next == l && l->prior == l) ? TRUE : FALSE; //前驱和后继指向头结点,判断为空表
}
元素获取函数
int GetElem(Node *l, int p, ElemType *e) //p 位置
{
if (NULL == l || p < 1) //入参判断 p不能小于0
{
return FAILURE;
}
Node *q = l->next; //双向循环链表,q指向第一个结点 //**
int i;
for (i = 1; i < p && q != l; i++) //循环p次,同时满足q不为空 双向循环链表 i = 1**
{
q = q->next;
}
if (q == l) //如果q为空,说明p(位置)大于长度 **这里要用q=L来判断插入位置是否大于长度
{
return FAILURE;
}
*e = q->data; //q已经指向第p个结点
return SUCCESS;
}
查询函数:
int LocateElem(Node *l, ElemType e, int (*p)(ElemType, ElemType))
{
if (NULL == l)
{
return FAILURE;
}
Node *q = l->next;
int len = 1;
while (q != l) //双向循环链表 最后指向头结点
{
if (p(e, q->data) == TRUE)
{
return len;
}
q = q->next;
len++;
}
return FAILURE;
}
删除函数:
int LinkDelete(Node *l, int p, ElemType *e)
{
int k = 1;
Node *q = l;
if (l == NULL)
{
return FAILURE;
}
if (p > LinkLength(l) + 1) //判断位置p是否合法 ,因为相对于普通链表没有p=nuLL末尾判断条件
{
return FAILURE;
}
while (k < p) //**
{
q = q->next;
k++;
}
if (k > p) //**
{
return FAILURE;
}
Node *n = q->next;
*e = n->data;
q->next = n->next;
//如果不是最后一个 **
n->next->prior = q;
free(n);
return SUCCESS;
}
清空函数:
int LinkClear(Node *l)
{
if (NULL == l)
{
return FAILURE;
}
Node *p = l->next;
while (p != l) //不回到头结点时
{
l->next = p->next;
//p->next->prior = l;
free(p);
p = l->next;
}
l->prior = l; //**
return SUCCESS;
}