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

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

链表的值不能重复。

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

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模式下,输入输出就成了大问题

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

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

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

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

struct ListNode{
int val;
ListNode* next;
ListNode(int x):val(x), next(nullptr){}
};
删除函数

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

ListNode* deleteNode(ListNode* head, int val) {//删除链表中值为val的节点
if(head == nullptr) return nullptr;//特判:如果头结点为空,返回空指针
if(head->val == val) return head->next;//特判:如果头结点的值等于val,直接返回头结点的下一个节点 ListNode* dummy = new ListNode(0);//创建dummy节点,接在头节点之前
dummy->next = head;
ListNode* cur = dummy;
while(cur->next != nullptr){
if(cur->next->val == val){//处理被删除的节点
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}else{
cur = cur->next;
}
}
return dummy->next;
}
主函数

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

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】...

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

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

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

    cin >> val; //输入需要删除的节点的值
head = deleteNode(head, val); //删除节点
while(head != NULL){ //遍历打印链表
cout << head->val << " ";
head = head->next;
}
代码
#include<iostream>
using namespace std; struct ListNode{
int val;
ListNode* next;
ListNode(int x) : val(x), next(NULL) {}
}; ListNode* deleteNode(ListNode* head, int val) {//删除链表中值为val的节点
if(head == nullptr) return nullptr;//特判:如果头结点为空,返回空指针
if(head->val == val) return head->next;//特判:如果头结点的值等于val,直接返回头结点的下一个节点 ListNode* dummy = new ListNode(0);//创建dummy节点,接在头节点之前
dummy->next = head;
ListNode* cur = dummy;
while(cur->next != nullptr){
if(cur->next->val == val){//处理被删除的节点
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}else{
cur = cur->next;
}
}
return dummy->next;
} int main(){
int n, head_val, val;
cin >> n >> head_val; ListNode* head = new ListNode(head_val); //创建头结点
for(int i = 0; i < n-1; i++){ //循环插入剩余的n-1个节点
int cur_val, pre_val;//取两个值(如例子中的1、2)
cin >> cur_val >> pre_val; //输入当前节点的值和需要插入的位置的节点的值 ListNode* pre = head;
while(pre != NULL && pre->val != pre_val) //遍历链表,找到需要插入的节点
pre = pre->next; ListNode* cur = new ListNode(cur_val); //创建当前节点
cur->next = pre->next; //将当前节点插入至链表中
pre->next = cur;
} cin >> val; //输入需要删除的节点的值
head = deleteNode(head, val); //删除节点
while(head != NULL){ //遍历打印链表
cout << head->val << " ";
head = head->next;
}
return 0;
}
输入输出总结

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

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

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

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

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

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

链表结点定义如下:

struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};

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

要求:

(1)正序构建链表;

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

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

本题有多组样例输入。

输入描述:

输入说明

1 输入链表结点个数

2 输入链表的值

3 输入k的值

输出说明:输出一个整数

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

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

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

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

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

数据输入

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

形式如下:

int main() {
int nodeNums;//节点个数
while(cin >> nodeNums){ }
return 0;
}

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

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

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

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

链表构造

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

int main() {
int nodeNums;//节点个数
while(cin >> nodeNums){
int nodeVal;//接收用户输入的节点值
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
for(int i = 1; i <= nodeNums; ++i){//正序构建链表
cin >> nodeVal;
ListNode* node4add = new ListNode(nodeVal);
cur->next = node4add;
cur = cur->next;
}
}
return 0;
}

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

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

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

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

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

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

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

代码
#include <iostream>
using namespace std; struct ListNode{
int val;
ListNode* next;
ListNode(int x): val(x), next(nullptr){}
}; int main() {
int nodeNums;//节点个数 while(cin >> nodeNums){
int nodeVal;
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
for(int i = 1; i <= nodeNums; ++i){//正序构建链表
cin >> nodeVal;
ListNode* node4add = new ListNode(nodeVal);
cur->next = node4add;
cur = cur->next;
}
int k;
cin >> k;
//使用双指针寻找倒数第k个节点
ListNode* fast = dummy;
ListNode* slow = dummy;
// k--;//fast指针不用从k+1开始走,因为本题不是要删除倒数第k个节点,而是要返回其值
for(int i = k; i > 0; --i){//fast指针先走k步
if(fast == nullptr) break;
fast = fast->next;
} while(fast != nullptr){//同时走,直到fast为nullptr
fast = fast->next;
slow = slow->next;
}
cout << slow->val << endl;
}
return 0;
}

【华为机试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. Python学习之十三_pip的学习

    Python学习之十三_pip的学习 pip的含义 pip: pip is the package installer for Python. You can use pip to install p ...

  2. Python学习之六_同时访问Oracle和Mysql的方法

    Python学习之六_同时访问Oracle和Mysql的方法 背景 jaydebeapi 可以访问大部分数据库. 但是他有一个问题是仅能够访问一种类型的数据库. 如果同事连接两种数据库,那么就会出现问 ...

  3. ebpf的简单学习

    ebpf的简单学习-万事开头难 前言 bpf 值得是巴克利包过滤器 他的核心思想是在内核态增加一个可编程的虚拟机. 可以在用户态定义很多规则, 然后直接在内核态进行过滤和使用. 他的效率极高. 因为避 ...

  4. Linux 开启防火墙 避免非干系人误操作的处理

    公司里面进行系统集成测试. 不想让开发能够更改我的服务器信息, 但是改密码又太麻烦了. 想了想还是用 防火墙好一些. 第一步 开启防火墙 systemctl enable firewalld syst ...

  5. 400G 光模块的价格

    400G 光模块的价格  令人惊叹... https://www.fs.com/cn/c/40g-100g-transceivers-889?pro_type=&sortby=priced&a ...

  6. elementui出现展开后子菜单宽度多出1px问题

    添加 就可以解决了 .el-menu { border-right-width: 0; } <template> <div class="compen-left-men&q ...

  7. CreateProcess函数源码分析

    CreateProcess函数源码分析 ​ 源码版本:Windows 2003 源码 ​ 源码阅读工具:Source Insight 函数功能分析 函数原型 BOOL CreateProcessA( ...

  8. Java开发中PO、VO、DAO、BO、DTO、POJO 含义

    PO(persistant object) 持久对象 可以看成是与数据库中的表相映射的java对象.使用 Mybatis 来生成 PO 是不错的选择. VO(value object) 值对象 通常用 ...

  9. C/C++ 病毒破坏手法总结

    针对注册表恶意修改: #include <stdio.h> #include <Windows.h> // 禁用系统任务管理器 void RegTaskmanagerForbi ...

  10. 基于AQS实现自定义同步类

    Mutex(互斥锁) Mutex是一个不可重入的互斥锁实现.锁资源(AQS里的state)只有两种状态:0表示未锁定,1表示锁定.下边是Mutex的核心源码: class Mutex implemen ...