文章链接:
203. 代码随想录
707. 代码随想录
206. 代码随想录
视频链接:手把手带你学会操作链表 | LeetCode:203.移除链表元素_哔哩哔哩_bilibili
这几天太忙拖欠了好多天,今天先补上day3的内容。
对链表整体不熟悉,基本上每个题都是先看视频(再夸一遍,视频里讲解手撕代码真的很清晰易懂!!!),再自己尝试用python写出来,之后有空要二刷巩固。
203. 移除链表元素
状态:看视频讲解,再自己尝试写
思路:使用虚拟头节点,并引入一个临时指针来遍历链表
总结:
1. 看过视频后,发现单链表结构并不复杂,合理使用虚拟头节点更有助于增删节点
2. cur指针要从dummyhead开始遍历(考虑特殊情况,如head的元素值等于val)
3. return dummyhead.next更准确,因为原先的head,因为有可能已经被删去了
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
dummyhead = ListNode(next = head)
cur = dummyhead
while cur.next:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return dummyhead.next
时间复杂度:O(n)
空间复杂度:O(1)
707. 设计链表
状态:先看视频,再自己做leetcode。只用单链表,中间有多次debug情况
思路:
1. 要先定义一个类ListNode!!使用虚拟头节点,在不同的function中要注意cur指针的初始化
2. 在第n个节点前插入一个节点的关键思路是:只有保证第n个节点是cur.next,才能用cur操作在cur.next前插入一个节点
3. 删去第n个节点的关键思路是:只有保证第n个节点是cur.next,才能将第n个节点的前一个节点(也就是cur)指向第n个节点的后一个节点,从而删去第n个节点
总结:
1. 在获取、增删节点时,要首先判断下标index是否有效。链表的下标是从0开始的,要注意题目要求,以决定何时用判断index > self.size,何时用index >= self.size
2. debug的问题主要出现在:
- 对cur指针的初始化,何时初始化为cur = self.dummyhead,何时为cur = self.dummyhead.next
- 何时用判断index > self.size,何时用index >= self.size
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next class MyLinkedList: def __init__(self): self.dummyhead = ListNode() self.size = 0 def get(self, index: int) -> int: if index < 0 or index >= self.size: return -1 cur = self.dummyhead.next for i in range(index): cur = cur.next return cur.val def addAtHead(self, val: int) -> None: newnode = ListNode(val=val, next=self.dummyhead.next) self.dummyhead.next = newnode self.size += 1 def addAtTail(self, val: int) -> None: cur = self.dummyhead while cur.next: cur = cur.next cur.next = ListNode(val=val,next=None) self.size += 1 def addAtIndex(self, index: int, val: int) -> None: if index < 0 or index > self.size: return cur = self.dummyhead for i in range(index): cur = cur.next cur.next = ListNode(val=val, next=cur.next) self.size += 1 def deleteAtIndex(self, index: int) -> None: if index < 0 or index >= self.size: return cur = self.dummyhead for i in range(index): cur = cur.next cur.next = cur.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)
时间复杂度:涉及 index
的相关操作为 O(index), 其余为 O(1)
空间复杂度: O(n)
206. 反转链表
状态:先看视频,再自己做leetcode。看完视频就去写代码,很顺畅哈哈。但是需要过段时间二刷巩固
思路:
1. 双指针方法:定义好cur, pre双指针,同时引入临时指针temp;注意对指针赋值时的顺序:用temp保存cur.next; 移动双指针时,先移动pre, 再移动cur。(先将当前的cur赋给pre, 再修改cur为temp)
时间复杂度:O(n)
空间复杂度:O(1)
2. 递归方法:思路与双指针一致,但要注意递归函数的传入参数赋值
时间复杂度:O(n)
空间复杂度:O(n)
总结:
1. 双指针方法有更易懂,思路更明晰,不过要注意对指针赋值的顺序
2. 递归方法的reverse()函数还可以写成class reverseList的子函数,注意子函数的最后要return reverse()
# 双指针方法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
cur = head
pre = None
while cur:
temp = cur.next
cur.next = pre
pre = cur
cur = temp
return pre
# 递归方法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
def reverse(cur, pre):
if cur == None:
return pre
temp = cur.next
cur.next = pre
return reverse(temp, cur)
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
return reverse(head, None)