链表
- 与数组存储不同,链表在内存中不是连续分布的。
- 复杂度分析:
- 查询: O(n)
- 增删:O(1)
- 特别注意ListNode的定义:(python)
class ListNode():
def __init__(self, val, next = None):
self.val = val
self.next = next
203.移除链表元素
利用dummy_head来统一之后的解法
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def removeElements(self, head, val):
"""
:type head: ListNode
:type val: int
:rtype: ListNode
"""
dummy_head = ListNode(next=head)
curr = dummy_head
while curr.next != None:
if curr.next.val == val:
curr.next = curr.next.next
else:
curr = curr.next
#这里二刷时出错,对于head = [7,7,7,7], val = 7 这种情况来讲,curr就暂时不能向后
#移动,因为更新后的curr会是7 == val,此时出错。
return dummy_head.next
707.设计链表 (medium)
二刷依旧没能自己写出来,继续。。。
- 要熟悉ListNode的定义方式,由于dummyHead只用作索引,所以其值val初始化为多少都可以。
- 不只是这道题,所有的题目都应该最先考虑极端情况,比如本题的index是否合理。
- 边界问题。这是一个很常见的问题,最好的处理方式就是考虑一个小的数(loop次数)来用笔来画一下或是“运行”一下,这样就很清晰什么时候需要停止。
- 可能第一次写的时候不会想到复用的情况,比如addAtHead(), addAtTail()是可以通过更通用的addAtIndex()来实现的,这是可以的。其原理也是一样的。
- 题解:(单向链表,双向链表待做...)
class Node():
def __init__(self, val, next=None):
self.val = val
self.next = next
class MyLinkedList(object):
def __init__(self):
self.dummyHead = Node(0)
self.size = 0
def get(self, index):
"""
:type index: int
:rtype: int
"""
if index < 0 or index >= self.size:
return -1
curr = self.dummyHead
while index > 0:
curr = curr.next
index -= 1
return curr.next.val
def addAtHead(self, val):
"""
:type val: int
:rtype: None
"""
self.addAtIndex(0, val)
def addAtTail(self, val):
"""
:type val: int
:rtype: None
"""
self.addAtIndex(self.size, val)
def addAtIndex(self, index, val):
"""
:type index: int
:type val: int
:rtype: None
"""
if index < 0 or index > self.size:
return
newNode = Node(val)
curr = self.dummyHead
while index > 0:
curr = curr.next
index -= 1
temp = curr.next
curr.next = newNode
newNode.next = temp
self.size += 1
def deleteAtIndex(self, index):
"""
:type index: int
:rtype: None
"""
if index < 0 or index >= self.size:
return
curr = self.dummyHead
while index > 0:
curr = curr.next
index -= 1
curr.next = curr.next.next
self.size -= 1
# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
206.反转链表
二刷依旧是下面的问题,继续。。。
- 对于解题的逻辑没有问题,问题在于对于“虚拟头节点”的思想没有灵活运用:这里虽然也要定义一个节点来使得head有指向,但是这里定义的并不是dummyHead,而是一个Null,是一个链表最终的收尾。
- 节点和指针的概念:head是头节点,prev是指针,因此最后返回prev,而不是head(head返回的是节点的值)。
- 对于为什么返回prev而不是head,即使二刷时对于这种解释我也不是很舒服,经过仔细研究整理了自己的理解(不一定严谨,但是现阶段可以这么理解):
- 首先动手画图一步一步运行到最后会发现,while循环停止时prev指向head(此时链表的头节点),但注意的是head是节点,prev是指针!
- (最开始没有画图的理解)大部分的链表题目都会定义cur = head来操作链表,此时cur就是指向head节点的指针(此时head是节点),然后经多while cur:来进行一些列的操作,如果配合dummyHead的话最终会返回dummyHead.next,这样就能解释为什么是返回prev了。如果是这样的话,像这道题并没有定义cur = head,但是实际的做法就是将head当作指针来操作(while head:),但是要明确head本身是头节点,最终返回的依旧是prev。
- 题解:(代码上半部分是错的,下半部分是正确的)
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
prev = None
while head.next:
temp = head.next
head.next = prev
prev = head
head = temp
return head
# 上面的问题在于返回的是节点的值
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
prev = None
while head: # why not head.next? Because even though head.next = None, it's still ok for reversing.
temp = head.next
head.next = prev
prev = head
head = temp
return prev
总结:
配合画图来帮助理顺处理逻辑。
链表一定要分清节点和指针的概念 new ListNode()是真实存在的一个节点, head = new ListNode() 相当于 head指针指向了一个真实的节点, node = head, 相当于node和head同时指向了这个真实的节点。(day3 知识点)