19. Remove Nth Node From End of List
0 ListNode
0.1 原解决代码模板
0.2 根据 LeetCode 提供的 ListNode
实现自己动手实现一个用于 debug
可以在 Jupyter Notebook 中使用。
# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def __repr__(self):
curr_node = self
s = ''
while curr_node.next:
s += "{}->".format(curr_node.val)
curr_node = curr_node.next
s+= "{}".format(curr_node.val)
return s
使用示范:
1 Solution
1.0 分析
思路:
O(n) 的复杂度是必须的,因为这是单向链表,所以肯定是必须要遍历到最后一个才能往前数 n 个。
3 -> 8 -> 1 -> 2 -> 4 ->7 -> 5
输入为 2
3 -> 8 -> 1 -> 2 -> 4 ->7 -> 5
2 1
拆键与链接
3 -> 8 -> 1 -> 2 -> 4 -x 7 -> 5
\ /|\
`-------'
可以看见,关键就是确定要移除的 node, 同时要在内存中暂留这个 node,因为我们需要用到它的 node->next
。
另一个就是需要获得要移除的 node 的上一个 node,用来链接剩下的链表
为什么可以使用栈来实现?
因为
- 栈可以保有每一个 node 信息。
- 栈的弹出顺序我们从链表末尾开始往前数的顺序是一样的。
- 我们一直把栈一直弹出
n-1
个 node,这些都是不需要的,在弹出第n
个的时候,进行 拆键与链接 动作就结束了。
1.1 简单栈功能实现代码
class Stack:
def __init__(self, listnode=None):
self._stack = list()
if listnode:
__curr_node = listnode
while __curr_node:
self._stack.append(__curr_node)
__curr_node = __curr_node.next
def push(self, node):
self._stack.append(node)
def pop(self):
return self._stack.pop()
def pick(self):
return self._stack[-1]
def __len__(self):
return len(self._stack)
栈使用示范:
因为上面自己动手实现的 ListNode 含有
__repr__
,所以这里使用stack.pick()
能够显示有用的信息。
1.2 一些特殊情况先了解一下
1.2.1 正常的测试用例示范:
1.2.2 n
和 listnode
都是 1
显示上为 []
和 LeetCode 的机制有关系。本题为了显示,LeetCode 后台将程序运行的结果进一步转换然后显示在网页上。
1.3 使用栈解决方案的最终提交代码
class Solution:
class Stack: # 将上面的栈实现拿来用
def __init__(self, listnode=None):
self._stack = list()
if listnode:
__curr_node = listnode
while __curr_node:
self._stack.append(__curr_node)
__curr_node = __curr_node.next
def push(self, node):
self._stack.append(node)
def pop(self):
return self._stack.pop()
def pick(self):
return self._stack[-1]
def __len__(self):
return len(self._stack)
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
stack = self.Stack(head)
if n == len(stack): # 上文提到的特殊情况
if len(stack) == 1:
return None
return head.next
cnt = 0
while True: # 这段代码可以简化地很短,但是为了方便符合思路,就不进一步调整了
node = stack.pop()
if (cnt + 1) != n:
cnt += 1
else: # finish
_ = stack.pick()
_.next = node.next
break
return head
2 运行结果
3 其它特殊输入了解
3.1 空 ListNode
输入
n
为 0
n
不为 0
3.2 n
超过 listnode
的长度
随手写的特殊情况 case。条件严格来说没有经过完整地思考,放在这里只是为了在代码中需要考虑特殊输入 提个醒。
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
case_1 = "Line 22: AttributeError: 'NoneType' object has no attribute 'next'"
case_2 = "Line 14: AttributeError: 'NoneType' object has no attribute 'next'"
case_3 = "Line 20: AttributeError: 'NoneType' object has no attribute 'next'"
stack = self.Stack(head)
if n > len(stack):
return case_2
if head is None:
return case_3
if n == 0:
return case_1
因为这一题比较特殊,没有上述说的这种“不合规范”的输入,所以这部分判断可以不用。
即用于本题的 (208 个)测试用例(输入)? 没有上面提到的几种特殊情况。