(图解)单链表删除结点值为x的结点算法

article/2025/8/20 11:48:14

目录

一、非递归的算法

第一种算法思路如下:

第二种算法思路如下:

二、递归的算法


一、非递归的算法

第一种算法思路如下:

  1. 先判断链表L是否为空,空链表退出程序;
  2. 用p利用while循环从头到尾扫描单链表,pre指向 *p 结点的前驱;
  3. 在while循环中,用 if 语句判断是结点是否是要删除的结点,是的话就删除此结点并且pre->next指向p结点的后驱,否则让pre、p指向同步后移个结点。

时间复杂度:O(n)                空间复杂度:O(1)

// 单链表的结点存储结构typedef struct LNode{ElemType data;           // 数据域struct LNode *next;      // 结点域
} LNode, *LinkList;

算法实现:

void Del_x(LinkList L, ElemType x){// L为带头结点的链表LinkList p = L->next, pre = L, q;if (p == NULL){printf("链表为空!\n");return;}while(p){if (p->data == x) {q = p;           // q指向要删除的结点p = p->next;     // p->指向要删除结点的后驱pre->next = p;   // q的前驱指向q的后驱free(q);         // 释放q(需要删除的结点)}else{pre = p;        // pre向下后移p = p->next;    // p与pre同步向后移,这两步不能调换顺序}}return;
}

图解算法:

先看下面这个链表,咱们要删除 x = 2 的结点

 L指向头结点

pre开始指向头结点,p指向链表的第一个结点,通过while循环中的 if 语句判断,p->data \neq 2,故pre和q同步后移。

后移后p->data = 2,因此要删除这个结点,即指行while循环里面的 if 语句,q指向要删除的结点,

p指向要删除结点的后驱,pre不要移动。

 然后通过free()函数将 q 指向的空间释放,并且pre的后驱指向 p(即pre->next = p),此时链表没有 q指向的结点。

 然后再通过 if 语句判断,p->data \neq 2,pre 和 q 同步后移。

然后p->data = 2即要删除这个结点,p的后驱是空指针NULL,故最后 p = NULL,通过free()函数释放此结点,然后pre的后驱指向 p,即 pre->next = NULL。

然后p = NULL,循环结束,链表中无结点数据域等于2的结点,任务完成。

第二种算法思路如下:

  1. 利用尾插法建立单链表;
  2. p用来扫描L的所有结点,当其值不为x时,将其链接到L之后,否则将其释放。

时间复杂度:O(n)                空间复杂度:O(1)

算法实现:

void Del_x(LinkList L, ElemType x){LinkList p = L->next, r = L, q;if ( p == NULL){printf("链表为空!\n");return;}while(p){if (p->data != x){r->next = p;  // p结点的值不为x,链接到L的尾部r = p;        // 方便下次链接p = p->next;   }else{q = p;      // 保存要删除的结点p = p->next;    // 指向要删除结点的后驱,继续扫描free(q);       // 释放结点}    }r->next = NULL;   // 插入结束后,尾结点的next域为NULLreturn;
}

图解算法:

 这个算法咱们可以这么理解,类似创建了新链表(在原链表上创建),然后往新链表插入结点的值不为x的结点,用的内存还是原来结点的内存(即指向的内存和原结点的地址一致),并不是新的分配的内存。和实际创建新链表(不在原链表上创建),通过复制结点后插入不同,复制插入的结点的地址不是原结点的地址(即指向的内存地址与原结点不一致),而是新分配的内存。

先看这个链表的初始情况

咱们为了更好的理解,可以将这个链表分为两个链表,一个为L指向头结点的链表,一个为p指向除头结点所有结点组成的链表 。

根据while循环的的 if 语句判断,p所指向的结点并不是要删除的结点,故p指向下一个节点,而刚才p指向的结点被链接到L所指向的链表上。

 r->next = p;  // p结点的值不为x,链接到L的尾部

 现在p指向的是要删除的结点,执行 if 语句的else部分,用q指向它,而p指向要删除结点的后驱。

 然后通过free()函数释放q所指向的结点。

此时p指向的结点不是要删除的结点,p后移,p之前的结点链接到L指向的链表。

 r->next = p;  // p结点的值不为x,链接到L的尾部

此时p指向的是要删除的结点,p向后移,并且q指向此结点。

通过free()函数释放q结点,此时p指向NULL,while循环结束,将L指向的链表最后结点的next指向NULL。

总结来说,就是把结点不是删除结点,就链接到L指向的链表,是要删除的结点通过free()函数释放。

二、递归的算法

算法思路如下:

  1. 找到基线条件(就是递归中止的条件),即链表为空,
  2. 找到递归条件,即每次递归离空链表更进一点
  3. 所以若链表L为空,递归函数 Del_x(L,x)什么也不做
  4. 若L->data = x, f(L,x)删除结点L,并且执行函数 Del_x(L->next, x),进行下次递归。
  5. 若L->data \neq x,执行函数 Del_x(L->next, x),进行下次递归。

算法实现:

1、C++代码实现

// C++代码 LinkList &L,是一个引用
void Del_x(LinkList &L, ElemType x){// L是不带头结点的链表LinkList p;if (L == NULL)        // 递归出口return;if (L->data == x){p = L;                // 删除L,并让L指向下一个结点L = L->next;free(p);Del_x(L, x);  // 递归调用}else            // 若L指向的结点不为xDel_x(L->next, x); // 递归调用return; 
}

2、C语言代码实现

void Del_x(LinkList* head, ElemType x)
{// head为指向不带头结点链表的第一个结点的指针的指针// head为二级指针,需要修改内存上保存的指向LinkList的地址LinkList p;   // 保存要删除的结点地址if (*head == NULL) return;        // 空链表,退出程序if ((*head)->data == x) // 判断结点的值是否为x{p = *head;  // 保存要删除的结点*head = p->next;   // 保存要删除的结点后驱地址Del_x(head, x);    // 递归函数}else{head = &((*head)->next);    // 指向下一个结点的地址的地址Del_x(head, x);}
}

这个算法要区分结构体指针和指向结构体指针的指针,可以看看我写的博客来了解结构体指针和指向结构体指针的区别。

结构体指针与指向结构体指针的区别

算法需要一个递归工作栈,深度为O(n),时间复杂度为O(n)。

 测试程序:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define FLAG  -1typedef int ElemType;
typedef struct Node
{ElemType data;struct Node* next;
}LinkNode, * LinkList;LinkList AddHead2(int flag);
void ShowLinkList(LinkList head);
int ListLength(LinkList head);
bool Insert(LinkList head, ElemType x, int i, int ListLength);
bool Delete(LinkList head, int i, int ListLength);
int ShowMeanu();
//void Del_x(ElemType x, LinkList head);
void Del_x(LinkList* head, ElemType x);int main(void)
{int choice;LinkList head = (LinkList)malloc(sizeof(LinkNode));head->next = NULL;while (true){choice = ShowMeanu();switch (choice){case 0:    exit(0);break;case 1:    head = AddHead2(FLAG);break;case 2:    ShowLinkList(head);break;case 3: {int i, x;printf("请输入你要插入结点的位置:");scanf("%d", &i);printf("请输入你要插入结点的数据域的值:");scanf("%d", &x);int length = ListLength(head);Insert(head, x, i, length);}break;case 4: {int i;printf("请输入你要插入结点的位置:");scanf("%d", &i);Delete(head, i, ListLength(head));}break;case 5: {int length;length = ListLength(head);printf("链表的长度为:%d\n");}break;case 6: {int x;printf("请输入要删除所有结点为x的值:");scanf("%d", &x);LinkList p = &(head->next);//Del_x(head, x);Del_x(p, x);}break;default:   printf("请输入恰当的值!!!!\n");}}return 0;
}int ShowMeanu()
{int choice;printf("欢迎来到链表测试功能!!!!!!\n");printf("1.创建单链表              2.显示单链表中的结点\n");printf("3.在链表i位置插入节点      4.删除链表第i个结点\n");printf("5.单链表的表长            6.删除链表为x的所有结点\n");printf("0.退出程序\n");printf("请输入你想测试的功能序号:");scanf("%d", &choice);return choice;
}LinkList AddHead2(int flag)
{printf(" 输入 -1 停止创建单链表\n");LinkList head, p, q;head = (LinkList)malloc(sizeof(LinkNode));head->next = NULL;p = head;ElemType x;scanf("%d", &x);while (x != flag){q = (LinkList)malloc(sizeof(LinkNode));q->data = x;if (head->next == NULL)head->next = q;elsep->next = q;p = q;scanf("%d", &x);}if (p != NULL)p->next = NULL;return head;
}void ShowLinkList(LinkList head)
{LinkList p = head->next;while (p != NULL){printf("%d", p->data);p = p->next;}printf("\n");
}int ListLength(LinkList head)
{int len = 0;LinkList p = head;while (p->next != NULL){len++;p = p->next;}return len;
}bool Insert(LinkList head, ElemType x, int i, int ListLength)
{LinkList p = head;if (i > ListLength || p->next == NULL)return false;while (--i)p = p->next;LinkList q = (LinkList)malloc(sizeof(LinkNode));q->data = x;q->next = p->next;p->next = q;return true;
}bool Delete(LinkList head, int i, int ListLength)
{LinkList p = head;if (i > ListLength || p->next == NULL)return false;while (--i)p = p->next;LinkList q = p->next;p->next = q->next;free(q);return false;
}/*void Del_x(ElemType x, LinkList head)
{LinkList p = head->next, pre = head, q;if (p == NULL){printf("链表为空\n");return;}while (p){if (p->data == x){q = p;p = q->next;pre->next = p;free(q);}else{pre = p;p = p->next;}}return;
}*//*void Del_x(ElemType x, LinkList L) {LinkList p = L->next, r = L, q;if (p == NULL){printf("链表为空!\n");return;}while (p) {if (p->data != x) {r->next = p;  // p结点的值不为x,链接到L的尾部r = p;        // 方便下次链接p = p->next;}else {q = p;      // 保存要删除的结点p = p->next;    // 指向要删除结点的后驱,继续扫描free(q);       // 释放结点}}r->next = NULL;   // 插入结束后,尾结点的next域为NULLreturn;
}*/void Del_x(LinkList* head, ElemType x)
{LinkList p;if (*head == NULL)return;if ((*head)->data == x){p = *head;*head = p->next;Del_x(head, x);}else{head = &((*head)->next);Del_x(head, x);}
}


http://chatgpt.dhexx.cn/article/iEU4BwU8.shtml

相关文章

单链表的基本操作-插入结点、删除结点、新建链表、查找结点位置

** C语言新手小白的学习笔记-------------目前持续更新中 ** 本人90后电气工程及其自动化大学生&#xff0c;大二开始接触C语言&#xff0c;写过前端&#xff0c;Python&#xff0c;但是都不精通&#xff0c;通过许多认识后明白了自身的许多不足&#xff0c;因此&#xff0c;…

武汉轰趴团建年会的疯狂玩法活动不一样的经历

又是一年的历程与工作&#xff0c;今年来一点新花样&#xff0c;来吧&#xff0c;让我们一起快乐冲向前&#xff0c;空气中满是不一样的欢声笑语&#xff0c;让我们一起感受不一样的年会趴&#xff0c;让我们一起去快乐的游玩吧。武汉轰趴团建年会的疯狂玩法活动不一样的经历 感…

天猫双11全球狂欢节的诞生,源于对快乐的分享

时光荏苒&#xff0c;天猫双11全球狂欢节&#xff0c;如今已经迈入了第十个年头。 相信有不少读者小伙伴都知道&#xff0c;双11最早其实源于中国的“光棍节”。那么这样一个原本应该是单身狗们黯然神伤的日子&#xff0c;究竟是如何演变成一场让无数消费者和商家都激情澎湃的购…

生成模型太强大?篡改与伪造检测越来越需要了!这篇最新综述不容错过

关注公众号&#xff0c;发现CV技术之美 最近一段时间&#xff0c;以扩散模型为代表的生成模型越来越能逼真地生成图像和视频&#xff0c;一方面是一群人的狂欢&#xff0c;这是AI的进步&#xff0c;另一方面却是另一群人的担忧&#xff0c;这是AI的危险。 AI技术可以造福人类&a…

你越来越孤独的3个原因

昨天看到一句话是这样说的&#xff1a;“交朋友都很难了&#xff0c;还交男朋友。” 我觉得有趣的同时&#xff0c;又发现好像真的是这样&#xff0c;身边好像很久没有出现过新的人了&#xff0c;也很少愿意去参加各种各样的聚会&#xff0c;也没有精力再去认识一些陌生人。 …

超1.58亿人将“血拼”,超级星期六购物节即将到来

超1.58亿人将“血拼”&#xff01;美国超级星期六购物节即将到来&#xff01;亚马逊出手整治“远仓近送”&#xff01; 据美国零售联合会的年度消费者调查结果显示&#xff0c;在今年圣诞节前的最后一个星期六&#xff08;即超级星期六&#xff09;&#xff0c;将有1.58亿人发生…

复旦女神陈果:孤独是一个人的狂欢,在你寂寞时请关注这些公众号充实自己

复旦大学哲学系博士陈果&#xff0c;对孤独做出了一种完美的诠释。她说&#xff1a;“狂欢是一群人的寂寞&#xff0c;孤独是一个人的狂欢。孤独不求外物&#xff0c;反求诸己。” 在你寂寞之时&#xff0c;请关注这些公众号。他们能给你提供有生命力的阅读&#xff0c;让你在有…

1024 程序员节狂欢盛会,等了一年终于来了!

风起岳麓&#xff0c;扶摇而上&#xff0c;约战湘江&#xff0c;谁与争锋&#xff01;以“算力新时代开源创未来”为主题的第三届 2022 长沙中国 1024 程序员节于 10 月 22 日-24 日强势来袭&#xff01;数位院士领衔、中国根技术掌门人以及海外先进开源技术掌门人齐聚&#xf…

夜夜狂欢的派对

奇怪&#xff0c;外国人的语气或思维总因为隔膜的缘故觉得很幽默&#xff0c;是那种“自己不笑却让所有人笑”的幽默。比如早上抛公发给我看的帖子中的几段&#xff1a; “北京有个夜夜狂欢的派对&#xff0c;一定很多人都去参加。因为在北京很多人每天看上去都很疲倦。我不知…

一群搞社区的人

最近Mixlab无界社区参与的电台节目有点多&#xff0c;上次是城市花样精&#xff0c;这次是#社区特辑。推荐给大家&#xff1a; opus 号外号外&#xff01;好公社“嗲声嗲气”播客和CSDC服务设计社区“月月谈”电台合作啦&#xff01; 什么&#xff1f;听起来这个合作来得太突然…

一群人的战斗

一、Bug 来了 一个平静的周日午后&#xff0c;正悠闲地在公园里遛娃。突然来了一条消息&#xff0c;打开企业微信仔细看了下&#xff0c;竟大吃一惊&#xff1a;客户成功在群内反馈了 Android A/B Testing SDK 的一个 crash&#xff0c;需要紧急解决。 得知问题后我立刻和客…

一个“后浪”的狂欢,一群中年人的孤单!

点击“技术领导力”关注∆ 每天早上8:30推送 作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) “孤单&#xff0c;是一个人的狂欢&#xff0c;狂欢&#xff0c;是一群人的孤单。” 《叶子》&#xff0c;阿桑&#xff0c;词/曲&#xff1a;陈晓娟 01 …

计算机术语hook的理解

Hooks就像一些外来的钩子&#xff0c;在源代码之间钩取&#xff08;窃听&#xff09;一些信息&#xff0c;当它捕捉到自己感兴趣的事发生&#xff0c;就拦截下来&#xff0c;让自己的代码执行一下&#xff0c;处理一下这个信息&#xff0c;然后再放出去继续之前的进程。这样就可…

计算机mips是什么,在计算机术语中,什么叫MIPS

2006-08-18 在计算机术语中,什么叫VGA 显卡所处理的信息最终都要输出到显示器上&#xff0c;显卡的输出接口就是电脑与显示器之间的桥梁&#xff0c;它负责向显示器输出相应的图像信号。CRT显示器因为设计制造上的原因&#xff0c;只能接受模拟信号输入&#xff0c;这就需要显卡…

堆 (计算机术语)

2019独角兽企业重金招聘Python工程师标准>>> 堆&#xff08;英语&#xff1a;heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质&#xff1a; 堆中某个节点的值总是不大于或不小于其父节点的值&#xff1b…

计算机术语翻译(Term.)及缩写整理(Abbr.)

Table of Contents &#x1f52e; 计算机术语翻译&#xff08;Term.&#xff09;及缩写整理&#xff08;Abbr.&#xff09;&#x1f5e1;️ DOI, URI, URL, URN&#x1f5e1;️ prompt&#x1f5e1;️ as-is, to-be&#x1f5e1;️ WYSIWYG&#x1f5e1;️ socket&#x1f5e1;…

计算机术语宏是什么意思,宏(计算机术语)

什幺是宏 所谓宏&#xff0c;就是一些命令组织在一起&#xff0c;作为一个单独命令完成一个特定任务。Microsoft Word中对宏定义为&#xff1a;“宏就是能组织到一起作为一独立的命令使用的一系列word命令&#xff0c;它能使日常工作变得更容易”。Word使用宏语言Visual Basic将…

计算机术语中 cam表示,计算机术语中,英文CAT是指_____。

计算机辅助翻译(英语&#xff1a;Computer-assisted Translation或Computer-aided Translation&#xff0c;缩写&#xff1a;CAT)。 亦称计算机辅助翻译系统&#xff0c;透过人工智能搜索及比对技术以及运用参考资料库和翻译记忆程序&#xff0c;纪录翻译人员所完成之译文&…