双向链表与循环链表在插入和删除时有所不同,因为含有结点的前驱指向,但总体思路相似。
直接上代码。
1、双向链表的插入
int InsertList(DouList L, data_t x, int i)
{
if(i<1 || i>(LengthList(L)+1))//判断 i 的位置是否符合,最大为length+1
{
return FALSE;
}
DouList p = L;//
指向头指针
int j;
for(j = 1; j < i; j ++)
{
p = p->next;//最后指向 i 的
前一个结点
}
DouList Q = (DouList)malloc(sizeof(DNode));//创建新的结点装新的内容
if(Q != NULL)
{
Q->data = x;
Q->next = NULL;
}
if(1 == i)//当 i= 1时需要将新节点置为第一个结点,即需要将head指向这个结点
{
Q->next = L->next;//先将新结点连入链表
Q->prior = L;//并使新结点的前驱指向头指针
L->next->prior = Q;//旧链表的第一个结点将会变成第二个结点,使该结点的前驱指向新插入结点
L->next = Q;//使头指针指向新插入的结点,插入结束
}
else{ //其他情况同理,先连入新结点,再断开旧的链
Q->next = p->next;//
Q->prior = p;//
p->next->prior = Q;//
p->next = Q;//
}
return TRUE;
}
2、双向链表的删除
int DleList(DouList L, int i)
{
if(i<1 || i>LengthList(L))//判断 i 的位置是否符合,最大为length
{
return FALSE;
}
DouList p = L;//
指向头指针
int j;
for(j = 1; j < i; j ++)
{
p = p->next;//最后指向 i 的
前一个结点
}
DouList q = p->next;//将一个新的链表指针指向需要删除的结点
p->next = q->next;//将遍历链表的指针指向需要删除结点的后一个结点
q->next->prior = p;//将需要删除结点的 后一个结点的前驱 指向 遍历链表指针 现在所指向的结点
free(q);//释放新的结点,即删除该结点
q = NULL;//将新创建的指针置空,防止其变成野指针
return TRUE;
}
总结:不知道会不会是一份只有自己看的懂得笔记,但是这三张链表(都有头指针)自己还是真的理解并实现了,鼓掌。难的不会,基础的还是要搞懂。之前一想到数据结构就想到满大篇绕来绕去的指向,当我静下来慢慢将链表理清晰之后,相信后面栈和队列,以及树都难不倒我的,不要恐惧。
1、对于有结点位置的参数,需要先判断
参数是否在链表中合法。首先要约定好第一个结点为1还是为0;才好判别最终的位置。其中在插入结点的情况下比较特殊,最大可以插入的链表最后一个结点的后面,所以最大的结点位置为length+1,大于这个值则不合法。
2、对于链表的遍历,主要要清楚需要
遍历到什么位置,或者说遍历到哪一个结点更方便操作,清楚之后再判别是将 遍历链表的指针 先指向 头指针 还是 指向第一个结点,以及需要将 遍历链表的指针 指向 需要操作的结点 还是 前一个结点?
3、对于链表的插入,一定要遵循
<先连后断>,考虑清楚断了之后是否还能找回这个结点?也要考虑清楚连接关系,如果已经改变指针的指向关系,就不能再使用其改变前的指向关系,否则不只是把自己绕晕,链表也就是乱的。
4、对于链表的清零,需要一个结点一个结点去删除,释放,最后要记得将遍历链表的
指针置为NULL,否则容易变成野指针。对于结点的删除也是同样的道理,需要一个结点来保存要删除的结点,将新链表连接好,再来操作需要删除的结点,做一个负责的程序员。
5、对于循环链表和双向链表的插入,当需要把新结点插入之后变成链表的第一个结点时,需要特殊处理,将
头指针指向新的结点,否则后面用到链表的时候头指针指向的第一个结点并不是新插入的结点,想想也不合理。
6、循环链表和双向链表还需要注意的是,它们最后一个结点链接的是头而不是NULL。
7、双向链表需要考虑结点的前驱指向。
都是自己一些简单基本的理解,如有错望指出啊,Shine在此感谢。