Python算法题集_删除链表的倒数第 N 个结点
- 题19:删除链表的倒数第 N 个结点
- 1. 示例说明
- 2. 题目解析
- - 题意分解
- - 优化思路
- - 测量工具
- 3. 代码展开
- 1) 标准求解【二次遍历】
- 2) 改进版一【快慢指针】
- 3) 改进版二【列表裁剪】
- 4. 最优算法
本文为Python算法题集之一的代码示例
题19:删除链表的倒数第 N 个结点
1. 示例说明
-
-
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
**示例 2:**
输入:head = [1], n = 1
输出:[]**示例 3:**
输入:head = [1,2], n = 1
输出:[1]**提示:** - 链表中结点的数目为 `sz` - `1 <= sz <= 30` - `0 <= Node.val <= 100` - `1 <= n <= sz` **进阶:**你能尝试使用一趟扫描实现吗?
-
2. 题目解析
- 题意分解
- 本题为删除链表中间的节点
- 本题的主要计算是链表遍历
- 基本的解法是循环,链表1读一遍,链表2读一遍,所以基本的时间算法复杂度为O(m)
- 优化思路
-
通常优化:减少循环层次
-
通常优化:增加分支,减少计算集
-
通常优化:采用内置算法来提升计算速度
-
分析题目特点,分析最优解
-
标准发放是遍历一遍求链表长度,在走到要删除的节点执行删除操作
-
使用快慢指针的方式,可以一次扫描完成
-
可以用列表结构进行节点删除【注意如果要删除多个节点,列表是最优选择】
-
- 测量工具
- 本地化测试说明:LeetCode网站测试运行时数据波动很大,因此需要本地化测试解决这个问题
CheckFuncPerf
(本地化函数用时和内存占用测试模块)已上传到CSDN,地址:Python算法题集_检测函数用时和内存占用的模块- 本题很难超时,本地化超时测试用例自己生成,详见【最优算法章节】
3. 代码展开
1) 标准求解【二次遍历】
第一次遍历获得链表长度,第二次遍历完成删除
半生不熟,超过50%
import CheckFuncPerf as cfp
class Solution:
@staticmethod
def removeNthFromEnd_base(head, n):
ilength, tmpNode = 0, head
while tmpNode:
ilength += 1
tmpNode = tmpNode.next
iPos, target = 0, ilength - n - 1
if target == -1:
return head.next
tmpNode = head
while iPos != target:
iPos += 1
tmpNode = tmpNode.next
tmpNode.next = tmpNode.next.next
return head
result = cfp.getTimeMemoryStr(Solution.removeNthFromEnd_base, ahead, 100000)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))
# 运行结果
函数 removeNthFromEnd_base 的运行时间为 18.01 ms;内存使用量为 4.00 KB 执行结果 = 0
2) 改进版一【快慢指针】
将快慢指针距离调整为n,然后一次遍历完成
出类拔萃,超过89%
import CheckFuncPerf as cfp
class Solution:
@staticmethod
def removeNthFromEnd_ext1(head, n):
slownode, fastnode = head, head
for iIdx in range(n):
fastnode = fastnode.next
if not fastnode:
return head.next
while fastnode.next:
slownode = slownode.next
fastnode = fastnode.next
slownode.next = slownode.next.next
return head
result = cfp.getTimeMemoryStr(Solution.removeNthFromEnd_ext1, ahead, 100000)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))
# 运行结果
函数 removeNthFromEnd_ext1 的运行时间为 10.00 ms;内存使用量为 0.00 KB 执行结果 = 0
3) 改进版二【列表裁剪】
将链表转换为数组,再裁剪链表
出类拔萃,超过89%
import CheckFuncPerf as cfp
class Solution:
@staticmethod
def removeNthFromEnd_ext2(head, n):
list_Node = []
tmpNode = head
while tmpNode:
list_Node.append(tmpNode)
tmpNode = tmpNode.next
if len(list_Node) < 1:
return head
if len(list_Node) == 1:
return None
if n == 1:
list_Node[-n - 1].next = None
elif n == len(list_Node):
head = head.next
else:
list_Node[-n-1].next = list_Node[-n+1]
return head
result = cfp.getTimeMemoryStr(Solution.removeNthFromEnd_ext2, ahead, 100000)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))
# 运行结果
函数 removeNthFromEnd_ext2 的运行时间为 17.01 ms;内存使用量为 904.00 KB 执行结果 = 0
4. 最优算法
根据本地日志分析,最优算法为第2种removeNthFromEnd_ext1
nums = [ x for x in range(200000)]
def generateOneLinkedList(data):
head = ListNode()
current_node = head
for num in data:
new_node = ListNode(num)
current_node.next = new_node
current_node = new_node
return head.next
ahead = generateOneLinkedList(nums)
result = cfp.getTimeMemoryStr(Solution.removeNthFromEnd_base, ahead, 100000)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))
# 算法本地速度实测比较
函数 removeNthFromEnd_base 的运行时间为 18.01 ms;内存使用量为 4.00 KB 执行结果 = 0
函数 removeNthFromEnd_ext1 的运行时间为 10.00 ms;内存使用量为 0.00 KB 执行结果 = 0
函数 removeNthFromEnd_ext2 的运行时间为 17.01 ms;内存使用量为 904.00 KB 执行结果 = 0
一日练,一日功,一日不练十日空
may the odds be ever in your favor ~