【数据结构学习】单链表的指定位置元素删除、链表的释放

删除指定节点

//删除某个结点
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

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
链表是一种常见的数据结构,它由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。C语言中实现链表的基本操作包括:创建链表、插入节点、删除节点、查找节点、遍历链表等。其中,插入节点和删除节点是链表的核心操作。在插入节点时,需要先找到要插入位置的前一个节点,然后将新节点插入到该节点之后;在删除节点时,需要先找到要删除节点的前一个节点,然后将该节点从链表删除。以下是链表的基本操作代码示例: 1. 创建链表 ``` LinkList CreateList() { LinkList L = (LinkList)malloc(sizeof(LNode)); L->next = NULL; return L; } ``` 2. 插入节点 ``` bool ListInsert(LinkList L, int i, ElemType e) { if (i < 1) { return false; } LNode *p = L; int j = 0; while (p != NULL && j < i - 1) { p = p->next; j++; } if (p == NULL) { return false; } LNode *s = (LNode *)malloc(sizeof(LNode)); s->data = e; s->next = p->next; p->next = s; return true; } ``` 3. 删除节点 ``` bool ListDelete(LinkList L, int i, ElemType &e) { if (i < 1) { return false; } LNode *p = L; int j = 0; while (p != NULL && j < i - 1) { p = p->next; j++; } if (p == NULL || p->next == NULL) { return false; } LNode *q = p->next; e = q->data; p->next = q->next; free(q); return true; } ``` 4. 查找节点 ``` LNode *GetElem(LinkList L, int i) { if (i < 1) { return NULL; } LNode *p = L->next; int j = 1; while (p != NULL && j < i) { p = p->next; j++; } return p; } ``` 5. 遍历链表 ``` void TraverseList(LinkList L) { LNode *p = L->next; while (p != NULL) { printf("%d ", p->data); p = p->next; } printf("\n"); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值