删除指定节点
//删除某个结点
int list_delete(linklist H,int pos){
if(H==NULL){
printf("删除结点失败,链表为空!\n");
return -1;
}
//获取要删除结点的上一个结点(前驱)
listnode *lastnode = list_get(H,pos-1);
//当没有前驱,或者该节点不存在的情况下
if(lastnode == NULL || lastnode->next == NULL){
printf("删除结点失败,结点不存在!\n");
return -1;
}
//记录后继结点的地址
listnode *nextnode = lastnode->next->next;
//释放该节点
free(lastnode->next);
//链接前驱结点和后继结点
lastnode->next = nextnode;
return 0;
}
删除指定节点的思路是非常清晰的
先上个图:
以上图为例,删除B结点,为保证删除指定节点后链表的完整性首先应该找到B结点的前驱,也就是A结点,从代码里面可以看到,找到前驱结点后进行了两个条件的判断,
1、前驱结点不存在 2、前驱结点的后继(也就是要删除的位置结点)不存在
满足这两个条件的其中一个就证明该节点不存在,所以函数直接结束!
反之,则满足进而进行下一步,要删除的结点是B,而B结点中保存了后继(C结点)的地址,所以不要一上来就把B给干掉了,程序是以前驱(A结点)为出发点的,所以A的后继的后继就是C结点,程序创建了一个结点指针并把C结点的地址保存下来,然后就可以放心大胆的删除B了,删除B之后,A就是C的前驱,C也就是A的后继,至于A的前驱以及C的后继是不受影响的,所以直接将A的后继赋值为C的地址就好了。
当然可能还会有一些特殊情况,端点情况:要么是头结点H,要么是尾结点。
1、先说下头结点:
头结点的位置定义是-1,所以当运行到如下程序是非法的。
listnode *lastnode = list_get(H,pos-1);
再看看list_get是如何写的:
所以删除结点函数最小的位置是0,头结点位置-1是无法被删除的。因为头结点前再无结点。
再说下删除尾结点:
假如现在有三个有效结点H->A->B->C->NULL
那要删除的位置肯定就是2了,
C有直接前驱B,这个很好理解。但是说到后继,C并没有有效的后继结点,但是这并不代表C结点没有next指针,只不过next为NULL,所以在删除C后拼接链表的时候,B的后继就是NULL,也是完全可以的。
释放列表
//删除并释放表
listnode* list_free(linklist H){
int i=-1;
if(H==NULL){
printf("删除链表失败,链表不存在!\n");
return NULL;
}
listnode *p = H;
while(p!=NULL){
H = H->next;
free(p);
printf("释放第%d个结点的空间成功!\n",i++);
p=H;
}
return NULL;
}
释放列表的思路就是,定义一个节点指针指向头结点,保存当前头结点的地址,当地址有效的时候,将头指针后移因为移动之前的结点(也就是头结点)即将被干掉, 所以用头结点的下一个结点继位成新的头结点。新的头结点继位成功后,老的就把空间释放掉,依次循环下去。
这里有一个点需要注意,当循环结束后,头结点指针H是有值的,这里不能直接写成:
H = NULL
因为H只函数传进来的形参,虽然H本身是指针,但是作为形参后,形参改变,原数据还是不会变的。这里我使用的方法是调用该函数的时候需要真正的H去接收这个NULL。如下:
H = list_free(H);
当然还有一种方式,那就是指针再取地址,也就是指针的指针,这样才能改变原本指针的值。,如下:
void list_free(linklist *H){
/*中间代码略*/
*H = NULL;
}
main测试代码:
int main(void){
//创建一个空链表
linklist H = list_creat();
//初始化插入几个数据
for(int i=0;i<10;i++){
list_tail_insert(H,i);
}
printf("初始链表数据为:");
list_show(H);
printf("\n***********删除链表中指定位置的元素测试*************\n");
while(1){
printf("\n请输入一个要删除的结点位置,输入-1为结束测试!\n");
int pos;
int res;
scanf("%d",&pos);
if(pos == -1) break;
res = list_delete(H,pos);
if(res == 0){
printf("删除指定结点后的链表为:");
list_show(H);
}
}
printf("\n**********删除链表测试************\n");
H = list_free(H);
list_show(H);
return 0;
}
测试效果
By Urien 2021年2月3日 00:27:40