朱有鹏C语言高级---4.9.7--单链表--删除节点(7)
(1)链表到底用来干嘛的?
链表是用来存数据的,链表和数组是一类东西!!
(2)为什么要删除节点?
有时候链表节点中的数据不想要了,因此要删除这个节点。
(3)删除节点的2个步骤
第一步:找到要删除的节点
第二步:删除这个节点。
(4)如何找到待删除的节点
通过遍历来查找节点。从头指针+头结点开始,顺着链表依次将各个节点拿出来,按照一定的方法比对,找到我们要删除的那个节点。
(5)如何删除一个节点
1、待删除的节点不是尾节点的情况:
首先把待删除的节点的前一个节点的pNext指针指向待删除的节点的后一个节点的首地址(这样就把这个节点从链表
中摘出来了),然后再将这个摘出来的节点free掉接口。
2、待删除的节点是尾节点的情况:
首先把待删除的尾节点的前一个节点的pNext指针指向NULL(这时候就相当于原来尾节点前面的一个节点变成了新的
尾节点),然后将摘出来的节点free掉。
(6)注意堆内存的释放
当程序都结束了的情况下,那些没有free的堆内存也被释放了。
有时候我们的程序运行时间很久,这时候malloc的内存没有free,会一直被占用直到你free释放它或者整个程序终止。
完整代码:(核心函数:delete_node())
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
//构建一个链表节点
struct node
{
int data; //有效数据
struct node *pNext; //指向下一个节点的指针
};
//作用:创建一个链表的节点
//返回:指针,指针指向我们本函数新创建的一个节点的首地址
struct node * create_node(int data)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
if (NULL == p)
{
printf("malloc error.\n");
return NULL;
}
//清理申请到的堆内存
bzero(p, sizeof(struct node));
//填充节点
p->data = data;
p->pNext = NULL;//将来要指向下一个节点的首地址
//实际操作时将下一个节点malloc返回的指针复制给这个
return p;
}
//思路:由头指针向后遍历,直到走到原来的最后一个节点。原来最后一个节点里面的pNext是NULL,
//现在我们将它改成new就可以了。添加了之后新节点就变成了最后一个。
//pH:头指针,有链表的头指针才能找到链表。new是一个新的节点
//计算添加了新的节点后共有多少个节点,然后把这个数写进头结点中
void insert_tail(struct node *pH, struct node *new)
{
int cnt = 0;
//分两布来完成插入
//第一步,先找到链表中最后一个节点
struct node *p = pH;
while (NULL != p->pNext)
{
p = p->pNext;//往后走一个节点
cnt++;
}
//第二部,将新节点插入到最后一个节点尾部
p->pNext = new;
pH->data = cnt + 1;
}
//思路:
void insert_head(struct node *pH, struct node *new)
{
//第1步: 新节点的next指向原来的第一个节点
new->pNext = pH->pNext;
//第2部: 头节点的next指向新节点的地址
pH->pNext = new;
//第3步: 头节点中的计数要加1
pH->data += 1;
}
//遍历单链表,pH为指向单链表的头指针,遍历的节点数据打印出来
void bianli(struct node *pH)
{
//pH->data //头节点的数据,不是链表的常规数据,不要算进去了
//struct node *p = pH;//错误,因为头指针后面是头节点,头节点的数据域是节点个数
struct node *p = pH->pNext;//直接跨过了头节点,p直接走到第一个节点
printf("-----开始遍历-----\n");
while (NULL != p->pNext)//是不是最后一个节点
{
printf("node data: %d.\n", p->data);
p = p->pNext; //走到下一个节点,也就是循环增量
}
printf("node data: %d.\n", p->data);
printf("-----完了-----\n");
}
//遍历单链表,pH为指向单链表的头指针,遍历的节点数据打印出来
void bianli2(struct node *pH)
{
//pH->data //头节点的数据,不是链表的常规数据,不要算进去了
struct node *p = pH;//错误,因为头指针后面是头节点,头节点的数据域是节点个数
printf("-----开始遍历-----\n");
while (NULL != p->pNext)//是不是最后一个节点
{
p = p->pNext; //走到下一个节点,也就是循环增量
printf("node data: %d.\n", p->data);
}
printf("-----完了-----\n");
}
// 从链表pH中删除节点,待删除的节点的特征是数据区等于data
// 返回值:当找到并且成功删除了节点则返回0,当未找到节点时返回-1
int delete_node(struct node *pH, int data)//参数:头指针,节点的数据
{
//找到这个待删除的节点,通过遍历链表来查找
struct node *p = pH; //头指针后面是头节点,用来指向当前节点
struct node *pPrev = NULL; //用来指向当前节点的前一个节点
while (NULL != p->pNext) //是不是最后一个节点
{
pPrev = p; //在p走向下一个节点前先将其保存
p = p->pNext; //走到下一个节点,也就是循环增量
//判断这个节点是不是我们要找的那个节点
if (p->data == data)
{
// 找到了节点,处理这个节点
// 分为2种情况,一个是找到的是普通节点,另一个是找到的是尾节点
// 删除的节点的困难点在于:通过链表的遍历依次访问各个节点,找到这个节点
// 后p指向了这个节点,但是要删除这个节点关键要操作前一个节点,但是这
// 时候已经没有指针指向前一个节点了,所以没法操作。解决方案就是增加
// 一个指针指向当前节点的前一个节点
if (NULL == p->pNext)
{
//尾节点
pPrev->pNext = NULL; //原来尾节点的前一个节点变成新尾节点
free(p); //释放原来的尾节点的内存
}
else
{
//普通节点
//要删除的节点的前一个节点和它的后一个节点相连,这样就把要删除的节点给摘出来了
pPrev->pNext = p->pNext;
free(p);
}
// 处理完成之后退出程序
return 0;
}
}
// 到这里还没找到,说明链表中没有我们想要的节点
printf("没找到这个节点.\n");
return -1;
}
int main(void)
{
//不能指向NULL,因为尾插法时首先会判断头指针的值p->pNext
//struct node *pHeader = NULL;
//定义头指针,创建头节点。头节点的数据域传的是节点的个数
struct node *pHeader = create_node(0);//数据定义0表示没放数据
insert_head(pHeader, create_node(11));
insert_head(pHeader, create_node(12));
insert_head(pHeader, create_node(13));
insert_head(pHeader, create_node(12));
bianli2(pHeader);
delete_node(pHeader, 12);
printf("-----删除后-----\n");
bianli2(pHeader);
return 0;
}