首先 我们来看这个函数
typedef int Ele;
typedef struct list{
Ele data;
list *next;
}List,*LNode;
void dele2(LNode &l,Ele e){
if(l==NULL)return;
printf("Hello!!\n");
printf("to %d\n",l);
if(l->data==e){
LNode p = l;
l = l->next;//***
free(p);
dele2(l,e);
}
else dele2(l->next,e);
printf("back %d\n",l);
}
这个函数就是将链表中所有为x的节点全部删除的操作
但是传参的时候传入的是一个指向链表节点指针的引用类型 也就是说在其中打***的地方
不会因为没有把前面的节点链接到后面而导致断链 相反
这里的l = l->next 是把当前节点的地址指向了后面
比如对于1 3 1这个链表 删除3操作
第一次走到1 那么递归层数加1时 调用dele2(l->next,e); 相当于(*l).next 将该指针所指向的next域的值
取出来并付给更深一层的参数 也就是引用指针&l 那么在此时在删除3时
会触发***处的操作 那么这里的操作实质上是 (*l).next = (*(*l).next).next
就相当于直接在1节点的next值修改成了 最后一个节点的地址 也就是相当于整个递归过程中对引用指针的操作其实就是对原表
操作
那么引用就是这样
在递归调用的时候 可以直接对原表操作 而且无视可能的断链情况
再来看一个函数
void delehead(LNode &p,Ele x){
while(p!=NULL){
if(p->data == x){
LNode q = p;
p = p->next;
// pre->next = p;
free(q);
}
else {
// pre = p;
p = p->next;
}
}
}
相同的操作 就是把递归改成非递归了 相同的是引用 但是这种情况下 就会导致断链
因为当在递归中使用引用的过程时 递归结束时会把一层层加上的取值操作符再把取值操作符一层层摘掉
而这里却是让p不断指向next 指到最后变成空 因为这里没有递归函数中返回的操作
1 3 1中执行结束的时候 递归不断返回后 原指针指向的是首节点
而非递归中由于没有返回操作 会不断地让头指针所指向的值一直向后走 最后使头指针
指到最后的NULL处
所以应另外使用指针非指针引用 这样不会修改原表的地址 并且附加前后链接操作
如果为了防止首元素删除导致头指针找不到首节点 应该另外设置头结点 或者 用计数另外记录下修改后头指针
应指向的位置 所以应改为
void delehead(LNode &l,Ele x){
// l指向头结点
LNode p = l->next,pre = l;
// p指向头节点的后继节点 pre指向头节点
// no head node
while(p!=NULL){
if(p->data == x){
LNode q = p;
p = p->next;
pre->next = p;
free(q);
}
else {
pre = p;
p = p->next;
}
}
}
这样就处理了非递归情况下对链表中删除任意位置值为x的操作 如果这里用头指针的单链表
会可能导致由于第一个节点的释放从而头指针指向了一个释放内存的地址