从单向链表中删除指定值的节点

输入一个单向链表和一个节点的值,从单向链表中删除等于该值的节点,删除后如果链表中无节点则返回空指针。

链表的值不能重复。

构造过程,例如输入一行数据为:

6 2 1 2 3 2 5 1 4 5 7 2 2

则第一个参数6表示输入总共6个节点,第二个参数2表示头节点值为2,剩下的2个一组表示第2个节点值后面插入第1个节点值,为以下表示:

1 2 表示为

2->1

链表为2->1

3 2表示为

2->3

链表为2->3->1

5 1表示为

1->5

链表为2->3->1->5

4 5表示为

5->4

链表为2->3->1->5->4

7 2表示为

2->7

链表为2->7->3->1->5->4

最后的链表的顺序为 2 7 3 1 5 4

最后一个参数为2,表示要删掉节点为2的值

删除 结点 2

则结果为 7 3 1 5 4

数据范围:链表长度满足 1≤�≤1000 1≤n≤1000 ,节点中的值满足 0≤���≤10000 0≤val≤10000

测试用例保证输入合法

输入描述

输入一行,有以下4个部分:

​ 1 输入链表结点个数

​ 2 输入头结点的值

​ 3 按照格式插入各个结点

​ 4 输入要删除的结点的值

输出描述

输出一行

输出删除结点后的序列,每个数后都要加空格

思路

如果这题是在LeetCode用核心代码模式做的话,是一道很常规的题目,只需要遍历单向链表,找到与目标值匹配的节点后删除即可

在ACM模式下,输入输出就成了大问题

为了理顺逻辑,最好将删除功能单独写成一个函数

那么,整体逻辑就是:从输入数据中拿到头节点,遍历输入数据,将链表构建好,然后调用删除函数将指定节点删除,最后再把链表的值打印出来

  1. #include<iostream>
  2. using namespace std;
  3. struct ListNode{
  4. };
  5. ListNode* deleteNode(ListNode* head, int val) {//删除链表中值为val的节点
  6. }
  7. int main(){
  8. }
定义结构体

先定义一个结构体作为链表节点(详见:构造链表)

  1. struct ListNode{
  2. int val;
  3. ListNode* next;
  4. ListNode(int x):val(x), next(nullptr){}
  5. };
删除函数

使用dummy进行删除(注意,这里和设计链表中的删除函数还不太一样,这里需要根据节点值进行删除而不是索引值)

  1. ListNode* deleteNode(ListNode* head, int val) {//删除链表中值为val的节点
  2. if(head == nullptr) return nullptr;//特判:如果头结点为空,返回空指针
  3. if(head->val == val) return head->next;//特判:如果头结点的值等于val,直接返回头结点的下一个节点
  4. ListNode* dummy = new ListNode(0);//创建dummy节点,接在头节点之前
  5. dummy->next = head;
  6. ListNode* cur = dummy;
  7. while(cur->next != nullptr){
  8. if(cur->next->val == val){//处理被删除的节点
  9. ListNode* tmp = cur->next;
  10. cur->next = cur->next->next;
  11. delete tmp;
  12. }else{
  13. cur = cur->next;
  14. }
  15. }
  16. return dummy->next;
  17. }
主函数

在主函数里,我们需要接受一行输入数据,例如:

6 2 1 2 3 2 5 1 4 5 7 2 2

则第一个参数6表示输入总共6个节点,第二个参数2表示头节点值为2,剩下的2个一组表示第2个节点值后面插入第1个节点值

根据题目描述,我们需要两个两个的从输入数据中取值,过程如下:

取1、2,表示2要接在1后面【链表为2->1】

接下来取2、3,表示3要接在2后面【链表为2->3->1】

然后取5、1,表示5要接在1后面【链表为2->3->1->5】

取4、5,表示5要接在4后面【链表为2->3->1->5->4】...

实际上题目除了要我们从单向链表中删除指定值的节点,还需要我们根据输入数据按照规则来先构建链表

  1. int main(){
  2. //定义几个变量分别接收:链表节点数量(例子中的6)、头节点的值(例子中的2)、待删除节点的值
  3. int n, head_val, val;
  4. cin >> n >> head_val;
  5. ListNode* head = new ListNode(head_val); //创建头结点
  6. for(int i = 0; i < n-1; i++){ //循环插入剩余的n-1个节点
  7. int pre_val, cur_val;//取两个值(如例子中的1、2)
  8. cin >> cur_val >> pre_val; //输入当前节点的值和需要插入的位置的节点的值
  9. ListNode* pre = head;
  10. while(pre != NULL && pre->val != pre_val) //遍历链表,找到需要插入的节点
  11. pre = pre->next;
  12. ListNode* cur = new ListNode(cur_val); //创建当前节点
  13. cur->next = pre->next; //将当前节点插入至链表中
  14. pre->next = cur;
  15. }
  16. }

然后再从输入中获取待删除值,调用删除函数,之后遍历打印链表即可

  1. cin >> val; //输入需要删除的节点的值
  2. head = deleteNode(head, val); //删除节点
  3. while(head != NULL){ //遍历打印链表
  4. cout << head->val << " ";
  5. head = head->next;
  6. }
代码
  1. #include<iostream>
  2. using namespace std;
  3. struct ListNode{
  4. int val;
  5. ListNode* next;
  6. ListNode(int x) : val(x), next(NULL) {}
  7. };
  8. ListNode* deleteNode(ListNode* head, int val) {//删除链表中值为val的节点
  9. if(head == nullptr) return nullptr;//特判:如果头结点为空,返回空指针
  10. if(head->val == val) return head->next;//特判:如果头结点的值等于val,直接返回头结点的下一个节点
  11. ListNode* dummy = new ListNode(0);//创建dummy节点,接在头节点之前
  12. dummy->next = head;
  13. ListNode* cur = dummy;
  14. while(cur->next != nullptr){
  15. if(cur->next->val == val){//处理被删除的节点
  16. ListNode* tmp = cur->next;
  17. cur->next = cur->next->next;
  18. delete tmp;
  19. }else{
  20. cur = cur->next;
  21. }
  22. }
  23. return dummy->next;
  24. }
  25. int main(){
  26. int n, head_val, val;
  27. cin >> n >> head_val;
  28. ListNode* head = new ListNode(head_val); //创建头结点
  29. for(int i = 0; i < n-1; i++){ //循环插入剩余的n-1个节点
  30. int cur_val, pre_val;//取两个值(如例子中的1、2)
  31. cin >> cur_val >> pre_val; //输入当前节点的值和需要插入的位置的节点的值
  32. ListNode* pre = head;
  33. while(pre != NULL && pre->val != pre_val) //遍历链表,找到需要插入的节点
  34. pre = pre->next;
  35. ListNode* cur = new ListNode(cur_val); //创建当前节点
  36. cur->next = pre->next; //将当前节点插入至链表中
  37. pre->next = cur;
  38. }
  39. cin >> val; //输入需要删除的节点的值
  40. head = deleteNode(head, val); //删除节点
  41. while(head != NULL){ //遍历打印链表
  42. cout << head->val << " ";
  43. head = head->next;
  44. }
  45. return 0;
  46. }
输入输出总结

其实也没啥好总结的这题,本来以为可以用来当做ACM下链表输入的参考的,结果使用的还是cin

但是观察这题可以发现:ACM下,题目要求的输入有可能不是一次性输入完成的

例如本题,题目举的例子是6 2 1 2 3 2 5 1 4 5 7 2 2,如果一开始没明白题意的话很容易以为输入数据的形式就是这个,导致后续处理很困难

还有就是复习了一下删除指定节点值的操作,练习了链表的构造

输出单向链表中倒数第k个结点(构造链表、输入tips)

输入一个单向链表,输出该链表中倒数第k个结点,链表的倒数第1个结点为链表的尾指针。

链表结点定义如下:

  1. struct ListNode
  2. {
  3. int m_nKey;
  4. ListNode* m_pNext;
  5. };

正常返回倒数第k个结点指针,异常返回空指针.

要求:

(1)正序构建链表;

(2)构建后要忘记链表长度。

数据范围:链表长度满足 1≤�≤1000 1≤n≤1000 , �≤� kn ,链表中数据满足 0≤���≤10000 0≤val≤10000

本题有多组样例输入。

输入描述:

输入说明

1 输入链表结点个数

2 输入链表的值

3 输入k的值

输出说明:输出一个整数

  1. 输入:
  2. 8
  3. 1 2 3 4 5 6 7 8
  4. 4
  5. 输出:5
思路

参考删除链表倒数第N个节点,使用快慢双指针即可找出本题中倒数第K个节点

但是要注意,本题不是要删除该节点,因此要和上述题目作区分

在本题中,快指针只需要比慢指针先走k步就行,不用k+1

剩下的问题还有两个:数据输入、正向链表构造

数据输入

虽然还是用cin来获取用户的输入数据,但是这里有个使用技巧,即cin与while配合使用

形式如下:

  1. int main() {
  2. int nodeNums;//节点个数
  3. while(cin >> nodeNums){
  4. }
  5. return 0;
  6. }

由题意,我们需要先输入链表的节点个数,此时可以将接收的代码写在while里面

这样当用户(这里其实是OJ)输入8,循环开始,我们可以在循环内处理其他逻辑

当本次循环结束,若用户没有输入,则退出循环,程序结束。

OJ会不断输入测试用例直到测试完毕才会停止输入,那时候正好跳出循环,程序结束

链表构造

和上题不同,本题是要求我们按规定的顺序构建链表,不涉及穿插节点,因此适用性更广

  1. int main() {
  2. int nodeNums;//节点个数
  3. while(cin >> nodeNums){
  4. int nodeVal;//接收用户输入的节点值
  5. ListNode* dummy = new ListNode(0);
  6. ListNode* cur = dummy;
  7. for(int i = 1; i <= nodeNums; ++i){//正序构建链表
  8. cin >> nodeVal;
  9. ListNode* node4add = new ListNode(nodeVal);
  10. cur->next = node4add;
  11. cur = cur->next;
  12. }
  13. }
  14. return 0;
  15. }

进入循环后,继续接收用户输入的值作为节点的值

创建dummy和cur指针,在遍历过程中不断接收节点值,用于构造临时节点node4add,该节点被连接到cur后

随着cur的移动,链表构造完毕

后面只需要根据输入的k值,找到对应节点并返回其值即可

注意!!!使用快慢指针时,快指针只需要提前k步

注意!!!使用快慢指针时,快指针只需要提前k步

注意!!!使用快慢指针时,快指针只需要提前k步

代码
  1. #include <iostream>
  2. using namespace std;
  3. struct ListNode{
  4. int val;
  5. ListNode* next;
  6. ListNode(int x): val(x), next(nullptr){}
  7. };
  8. int main() {
  9. int nodeNums;//节点个数
  10. while(cin >> nodeNums){
  11. int nodeVal;
  12. ListNode* dummy = new ListNode(0);
  13. ListNode* cur = dummy;
  14. for(int i = 1; i <= nodeNums; ++i){//正序构建链表
  15. cin >> nodeVal;
  16. ListNode* node4add = new ListNode(nodeVal);
  17. cur->next = node4add;
  18. cur = cur->next;
  19. }
  20. int k;
  21. cin >> k;
  22. //使用双指针寻找倒数第k个节点
  23. ListNode* fast = dummy;
  24. ListNode* slow = dummy;
  25. // k--;//fast指针不用从k+1开始走,因为本题不是要删除倒数第k个节点,而是要返回其值
  26. for(int i = k; i > 0; --i){//fast指针先走k步
  27. if(fast == nullptr) break;
  28. fast = fast->next;
  29. }
  30. while(fast != nullptr){//同时走,直到fast为nullptr
  31. fast = fast->next;
  32. slow = slow->next;
  33. }
  34. cout << slow->val << endl;
  35. }
  36. return 0;
  37. }

【华为机试ACM基础#02】从单向链表中删除指定值的节点、输出单向链表中倒数第k个节点(熟悉链表的输入方式)的更多相关文章

  1. 华为机试ACM(字符组合问题)

    今晚做了华为的机试,3道ACM题,最后一道是实现从M个不同字符中任取N个字符的所有组合. eg: input:ABC 2 output:AB AC BC 第一个输入为字符串,第二个输入为组合的字符个数 ...

  2. java笔试之从单向链表中删除指定值的节点

    输入一个单向链表和一个节点的值,从单向链表中删除等于该值的节点,删除后如果链表中无节点则返回空指针. 链表的值不能重复 构造过程,例如 1 -> 2 3 -> 2 5 -> 1 4  ...

  3. 2014华为机试西安地区A组试题

    2014华为机试西安地区A组试题 题目一.分苹果 M个同样苹果放到N个同样篮子里有多少种放法,同意有篮子不放. 1<=M<=10.1<=N<=10 比如5个苹果三个篮子,3,1 ...

  4. 2014华为机试西安地区B组试题

    2014华为机试西安地区B组试题 题目一.亮着点灯的盏数 一条长廊里依次装有n(1≤n≤65535)盏电灯,从头到尾编号1.2.3.-n-1.n.每盏电灯由一个拉线开关控制.開始,电灯所有关着. 有n ...

  5. 华为机试001:字符串最后一个单词的长度(华为OJ001)

    华为机试 字符串最后一个单词的长度 计算字符串最后一个单词的长度,单词以空格隔开. 提交网址: http://www.nowcoder.com/practice/8c949ea5f36f422594b ...

  6. 寻找单向链表的倒数第k个节点

    题目: 输入一个单向链表,输出这个单向链表的倒数第k个节点 template<class T> class ListNode { public: T Data; ListNode<T ...

  7. 数据结构——求单向链表的倒数第K个节点

    首先,对于链表来说,我们不能像数组一样直接访问,所以我们想到要求倒数第K个节点首先要知道最后一个节点. 然后从最后一个节点往前数K个. 最后得到想要的值. 但是这是不对的,为什么呢?因为题目给出的是单 ...

  8. 数据结构和算法之单向链表二:获取倒数第K个节点

    我们在做算法的时候或多或少都会遇到这样的问题,那就是我们需要获取某一个数据集的倒数或者正数第几个数据.那么今天我们来看一下这个问题,怎么去获取倒数第K个节点.我们拿到这个问题的时候自然而然会想到我们让 ...

  9. LeetCode 面试题 02.02. 返回倒数第 k 个节点

    题目链接:https://leetcode-cn.com/problems/kth-node-from-end-of-list-lcci/ 实现一种算法,找出单向链表中倒数第 k 个节点.返回该节点的 ...

  10. 面试题 02.02. [链表][双指针]返回倒数第 k 个节点

    面试题 02.02. 返回倒数第 k 个节点 方法一:使用外部空间 // 执行用时: 1 ms , 在所有 Java 提交中击败了 16.75% 的用户 // 内存消耗: 36.8 MB , 在所有 ...

随机推荐

  1. VM PowerCli的简单安装和使用学习

    1. Win10 上面安装 下载 zip包并且进行安装 win10 已经带了powershell 安装比较简单, 只不过安装时会提示 powershell的权限有问题需要打开powershell 执行 ...

  2. 查看 Oracle 数据库内 没有Primary key 类型主键的表信息

    查看 Oracle 数据库内 没有Primary key 类型主键的表信息 SELECT * FROM user_tables A WHERE NOT EXISTS ( SELECT * FROM u ...

  3. 一个神奇的golang技巧:扩大heap内存来降低gc频率

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu 公众号:一本正经的瞎扯 具体的文章请看:https://web.archive.org/web/ ...

  4. 【K哥爬虫普法】辛苦钱被中间商抽走八成,还因此锒铛入狱

    我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K 哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识, ...

  5. 自动化部署实例(donetcore GitLab CICD )

    主要简单的介绍了一下 GitLab CI 的持续集成以及持续部署,这篇将通过 GitLab CI 发布一个 .net core 项目,来带小伙伴们感受一下自动化的魅力,从此告别手动发布. 准备工作 创 ...

  6. EXPLAIN分析pgsql的性能

    EXPLAIN分析pgsql的性能 前言 EXPLAIN命令 EXPLAIN -- 显示一个语句的执行计划 命令详解 EXPLAIN输出结果展示 analyze buffers 全表扫描 索引扫描 位 ...

  7. 26岁的超经典音乐播放器Winamp归来!UI彻底重构:支持iOS/安卓

    快科技4月18日讯,还记得Winamp吗? 这款1997年首发的媒体播放器,已经走过了26年的历史.它凭借高度简洁.大量的皮肤.丰富的定制性.多元的格式支持等成为有史以来最好的音乐播放器之一. 当年的 ...

  8. Android 相册

  9. NC224933 漂亮数

    题目链接 题目 题目描述 小红定义一个数满足以下条件为"漂亮数": 该数不是素数. 该数可以分解为2个素数的乘积. 4 是漂亮数,因为 4=2*2 21 是漂亮数,因为 21=3* ...

  10. RocketMQ—RocketMQ消息重复消费问题

    RocketMQ-RocketMQ消息重复消费问题 重复消费问题的描述 什么情况下会发生重复消费的问题: 生产者多次投递消息:如果生产者发送消息时,连接有延迟,MQ还没收到消息,生产者又发送了一次消息 ...