链接

Remove Nth Node From End of List

难度

Medium

描述

Given a linked list, remove the n -th node from the end of list and return

its head.

给定一个链表,要求移除导数第n个元素,并且返回新链表的head

样例:

Given linked list: **1- >2->3->4->5**, and **_n_ = 2**.

After removing the second node from the end, the linked list becomes **1- >2->3->5**.

注意:

Given n will always be valid.

给定的n永远有效

Follow up:

Could you do this in one pass?

你能一发通过么???官方嘲讽。

题解

题目的意思非常直白,不需要太多思考就能理解。但是上手去做的话会有一点小问题,因为如果是数组很好办,我们直接可以求到数组的长度,导数第N个元素也非常容易确定。但是如果是链表的话就不同了,因为我们并不知道链表的长度,所以没办法直接获取需要删除节点的位置。

既然直接没有办法求到,那么可以间接去求嘛,所以很自然地可以想到两次遍历的方法。

两次遍历

这个方法非常直观,基本上可以说是顾名思义。我们对这个链表遍历两次,第一次求到链表的长度,这样我们就可以推算到倒数第N个数是正数第几个数了。第二次我们移动对应的长度,找到需要删除的节点,将它移除即可。

我们来看下面这张图,完全可以脑补出算法:

虽然算法不难,但是这题当中藏着trick,隐藏了出题人深深的恶意。不相信的话,可以试着不看答案写写看,基本上一定会错上一两次。原因很简单,因为会有一些特殊情况需要考虑。

比如,特殊情况1:链表当中只有一个元素,显然这个时候根本不需要移动,也不用删除,直接return None就好了。但是如果我们使用常规方法的话,是无法删掉的,必须要特殊判断这种情况。

特殊情况2:这个要删的元素刚好是第一个head元素,这种情况也没有办法常规解决,也需要特殊判断。

把这两个特殊情况考虑到,基本上就没问题了。

# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
l = 0
pnt = head
# 计算链表长度
while pnt:
l += 1
pnt = pnt.next
# 如果长度为1,直接return None
if l == 1:
return None
# 计算删除需要移动的长度
l = l - n - 1
# 如果小于0,说明需要删除第一个元素,那么直接return head.next。
if l < 0:
return head.next
pnt = head
for i in range(l):
pnt = pnt.next
pnt.next = pnt.next.next
return head

一次遍历

上面的这种算法中规中矩,基本上没有难度。那么有没有更好的算法,比如我们能不能做到只遍历链表一次呢?这样不就优化了复杂度了吗?

当然是可以的,说来这种算法也非常巧妙。如果说我们把链表想象成一条跑道,而把移动遍历的指针想象成跑道上的运动员。我们现在不知道跑道有多长,但是我们想要知道距离终点30米的位置。我们还知道运动员的奔跑速度都一样。应该怎么做呢?

我们可以先让一个运动员先跑30米,然后再派另外一个运动员从起点出发。这样,当第一个运动员跑到终点的时候,第二个运动员所在的位置就是距离终点30米的位置。

我们把上述的运动员换成指针,跑道换成链表,就是这题的解法了。

同样,我们也有一张图可以说明:

我们根据描述,可以试着写出代码。

# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
pnt1 = head # 第一个指针先跑n步
for i in range(n):
pnt1 = pnt1.next
# 注意,如果是删除head,会出现跑过头的情况,需要判断
if pnt1 is None:
return head.next pnt2 = head
# 移动第一个指针,直到结尾
while pnt1.next:
pnt1 = pnt1.next
pnt2 = pnt2.next # 删除pnt2.next位置的节点
pnt2.next = pnt2.next.next
return head

看起来第二种方法似乎更优一些,但实际上如果分析复杂度的话,两者都是\(O(n)\)的复杂度,并没有高下之分。而且比较奇葩的是,我用CPP提交的时候是能明显感觉出来第二种方法优于第一种几毫秒的,但是当我换成了Python之后,第二种方法的耗时反而还更长了。可见,快慢都是相对的,一般情况下来说在复杂度相同的情况下,优化常数意义不大。

虽然搞了半天,并没有什么效果,但至少我们明白了这点,也算是收获。

好了,今天的文章就是这些,如果觉得有所收获,请顺手扫码点个关注,你们的举手之劳对我来说很重要。

LeetCode19 移除倒数第N个元素的更多相关文章

  1. 判断闰年的方法以及如何获得单链表的倒数第K个元素

    今天很悲催,心中向往的公司,打电话过来面试,问到我两个问题,结果竟然都没有回答上,伤心了,记录下今天失败,希望以后不要被同样的问题给PASS. 问题1.如何判断是否为闰年 所谓闰年那就是:四年一闰,百 ...

  2. [CareerCup] 2.2 Kth to Last Element of Linked List 链表的倒数第k个元素

    2.2 Implement an algorithm to find the kth to last element of a singly linked list. 这道题让我们求链表中倒数第k个元 ...

  3. jquery移除、绑定、触发元素事件使用示例详解

    这篇文章主要介绍了jquery移除.绑定.触发元素事件使用示例详解,需要的朋友可以参考下. unbind(type [,data]) //data是要移除的函数 $('#btn').unbind(&q ...

  4. 笔试题&amp;面试题:设计一个复杂度为n的算法找到单向链表倒数第m个元素

    设计一个复杂度为n的算法找到单向链表倒数第m个元素.最后一个元素假定是倒数第0个. 提示:双指针查找 相对于双向链表来说,单向链表仅仅能从头到尾依次訪问链表的各个节点,所以假设要找链表的倒数第m个元素 ...

  5. 求链表倒数第n个元素

    提示:设置一前一后两个指针,一个指针步长为1,另一个指针步长为n,当一个指针走到链表尾端时, 另一指针指向的元素即为链表倒数第n个元素. #include <stdio.h> #inclu ...

  6. 大杂烩 -- 查找单向链表倒数第m个元素

    基础大杂烩 -- 目录 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1.输入并查找 方案:头插法,正向查找第m个元素. ...

  7. 习题3.5 求链表的倒数第m个元素(20 分)浙大版《数据结构(第2版)》题目集

    请设计时间和空间上都尽可能高效的算法,在不改变链表的前提下,求链式存储的线性表的倒数第m(>0)个元素. 函数接口定义: ElementType Find( List L, int m ); 其 ...

  8. Q:链表的倒数第K个元素

    问题:如何得到链表中的倒数第k个元素?   一种简单的思路是遍历链表一遍,并统计出链表中节点的数目,然后计算出倒数第k个元素到链表头节点的元素的距离,然后得到对应的结果.但是,我们能否有一种更加简便的 ...

  9. python经典算法面试题1.5:如何找出单链表中的倒数第K个元素

    本题目摘自<Python程序员面试算法宝典>,我会每天做一道这本书上的题目,并分享出来,统一放在我博客内,收集在一个分类中. [微软笔试题] 难度系数:⭐⭐⭐ 考察频率:⭐⭐⭐⭐⭐ 题目描 ...

随机推荐

  1. Serverless 实战 —— Funcraft + OSS + ROS 进行 CI/CD

    前言 首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute):函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传.函数计算准 ...

  2. $bzoj4722$ 由乃 搜索

    正解:搜索 解题报告: 传送门$QwQ$ 首先发现长度为$len$的子集的值域为$[0,v\cdot len+len]$,数量为$2^{len}$.所以当$2^{len}\geq v\cdot len ...

  3. mumu模拟器手Q自动化配置

    { "platformName": "Android", "platformVersion": "6.0.1", &qu ...

  4. CentOS7.2 部署Ceph分布式存储

    1.1 环境准备 主机名 IP地址 ceph-admin 192.168.16.220 ceph-node1,ceph-mon 192.168.16.221 ceph-node2,ceph-mon 1 ...

  5. Oracle数据库设计省市区小区数据库建表

    省CREATE TABLE "SF_JECF_BASE"."SF_PROVINCE" ( "id" VARCHAR2(64 BYTE) NO ...

  6. JVM之GC(一)

    Java较C而言,最大的区别在于内存管理.JVM设有无用内存空间自动回收复用机制,也就是我们所说的GC. 之前说过,栈是为线程.为函数的执行分配内存的地方,用完即“销毁”,这里留待以后做深入探讨:堆是 ...

  7. C#反射与特性(五):类型成员操作

    目录 1,MemberInfo 1.1 练习-获取类型的成员以及输出信息 1.2 MemberType 枚举 1.3 MemberInfo 获取成员方法并且调用 1.4 获取继承中方法的信息(Decl ...

  8. 重拾c++第二天(4):复合类型

    1.定义:种类 数组名[元素个数] = {元素1,...,元素n} ,或者直接赋值:数组名[元素位置] = 值; 2.部分初始化,其他全为0,可以就定义一个0,这样得到0数组(或者就一个{},别的啥也 ...

  9. “土法炮制”之 OOM框架

    一.什么是OOM框架? OOM 的全拼是 Object-Object-Map,意思是对象与对象之间的映射,OOM框架要解决的问题就是对象与对象之间数据的自动映射. 举一个具体的例子:用过MVC模式开发 ...

  10. 递推预处理 + Manacher

    链接:https://www.nowcoder.com/acm/contest/131/D来源:牛客网 字符串 S 只包含小写英文字母.有四种操作,每次操作你可以选择其中一种: 删除字符串的第一个字母 ...