题目:
注意:
剑指offer上对这道题目的描述是给定的删除节点是节点指针。这表明这道题可以用时间复杂度为O(1)的方式解决。
而leetcode上对类似本题的描述是:
给定删除节点是节点值,这决定了本题时间复杂度必然至少为O(N)。因为必定要从头遍历链表。
面试tips:
1. 注意以上两种问法的区别。若是第一种,最优的方式时复为O(1)。
2. 这道题默认了所给的删除节点就在链表上,可以跟面试官提一下,显示对此考虑的更全面。如果不在还要进行判断。
思路:
这里实现剑指offer上的问法,leetcode上的采用这里的思路一很容易解决。
1. 遍历一遍链表,找到要删除的节点的前一个节点,进行删除操作即可。
2. 如果给定的删除节点是一个节点指针(该指针指向待删除链表的某一个节点),则可不需从头遍历就已经知道了要删除的节点的位置,只是不知道其前一个位置。事实上,由下面步骤我们可知,只要给的是要删除节点的指针,则可以不需要知道其前一个位置也可进行删除。
①设置虚拟节点,使得若删除的是头节点,下面的操作都相同。
②若要删除的不是尾节点,则将要删除的节点深拷贝其下一个节点,将下一个节点进行删除(因为知道当前节点就是下一个节点的前一个节点,因此很容易删除),虽然不是真正删除所删除的节点,但因为深拷贝,也就等价于是删除掉所删除的节点的结果了。见下图的c。
③若删除的是尾节点,因为一个正常节点是不能够深拷贝为空节点的,因此②走不通,这时只能从头遍历了,找到尾节点的前一个节点进行删除。
总的平均时间复杂度:((n-1)* O(1) + 1 * O(n) ) / n -> O(1)
代码实现:
1和2的代码实现上更改的部分只有class solution部分,其余部分是根据数组构建单链表及要删除的节点指针。
1.
class ListNode:
def __init__(self, val = 0, next = None):
self.val = val
self.next = next
def arr2List(arr, val):
# 一维数组转单链表, 同时对应值要找到对应节点
# 设置虚拟头节点,使得对头节点的操作同
prev = dummy_head = ListNode()
for i in range(len(arr)):
tmp = ListNode(val = arr[i])
prev.next = tmp
prev = prev.next
if arr[i] == val:
tod_Node = tmp
return dummy_head.next, tod_Node
def List2arr(head):
# 单链表转一维数组
curr = head
arr = []
while curr:
arr.append(curr.val)
curr = curr.next
return arr
class Solution:
def deleteNode(self, head, tod_Node):
# 从头遍历找到对应的节点
prev = dummy_head = ListNode(next = head)
while prev.next:
if prev.next == tod_Node:
prev.next = prev.next.next
return head
else:
prev = prev.next
if __name__ == '__main__':
# 若给定一维数组,及要删除的节点值
arr = [4, 5, 1, 9]
val = 9 # 这里表明要删除的节点为5所对应的节点
# 先要将其转为单链表以及相应节点
head, tod_Node= arr2List(arr, val)
a = Solution()
new_head = a.deleteNode(head, tod_Node)
new_arr = List2arr(new_head)
print(new_arr)
2.
class ListNode:
def __init__(self, val = 0, next = None):
self.val = val
self.next = next
def arr2List(arr, val):
# 一维数组转单链表, 同时对应值要找到对应节点
# 设置虚拟头节点,使得对头节点的操作同
prev = dummy_head = ListNode()
for i in range(len(arr)):
tmp = ListNode(val = arr[i])
prev.next = tmp
prev = prev.next
if arr[i] == val:
tod_Node = tmp
return dummy_head.next, tod_Node
def List2arr(head):
# 单链表转一维数组
curr = head
arr = []
while curr:
arr.append(curr.val)
curr = curr.next
return arr
class Solution:
def deleteNode(self, head, tod_Node):
# 1. 为确保头节点操作与其余节点同,设置一个虚拟头节点
# 2. 若删除的节点不是最后一个节点,则将其下一个节点复制过来,删除下一个节点,这等价于删除当前节点
# 3. 若删除的节点是最后一个节点,则下一个节点为空是无法复制的,因此这是需从头节点遍历找到最后一个的前一个节点进行删除操作
dummy_head = ListNode(0,next = head)
if tod_Node.next != None:
# 则说明要删除的节点不是最后一个节点
# 深拷贝下一个节点至当前节点 并同时也将下一个节点删除了
tod_Node.val = tod_Node.next.val
tod_Node.next = tod_Node.next.next
return head
else:
# 此时要删除的节点是最后一个节点,同时其也有可能是第一个节点(但是因为有虚拟头节点在 因此不可能为真正意义上的第一个节点)
# 因为该节点不可能从不空变为空(深拷贝意义上的空) 因此只能从头节点开始遍历
prev = dummy_head
while prev.next:
if prev.next == tod_Node:
prev.next = prev.next.next
return head
else:
prev = prev.next
if __name__ == '__main__':
# 若给定一维数组,及要删除的节点值
arr = [4, 5, 1, 9]
val = 1 # 这里表明要删除的节点为5所对应的节点
# 先要将其转为单链表以及相应节点
head, tod_Node= arr2List(arr, val)
a = Solution()
new_head = a.deleteNode(head, tod_Node)
new_arr = List2arr(new_head)
print(new_arr)
参考:
1.《剑指offer》
2. 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台