代码随想录刷题第三天
链表基础知识
移除链表元素 (LC 203)
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
移除某个链表元素的时候只需要将前一个节点的next指针指向下一个元素即可。但一定要注意的是前一个节点,不是要删除的节点。如下图:
所以常规的代码是:
while(cur!=None and cur.next!=val):
if cur.next.val == val:
cur.next = cur.next.next # 删除下一个节点
else:
cur = cur.next # 无需删除,跳到下一个节点
!!! 但是,如果要删除头节点怎么办???
头结点前面没有节点了,无法按照之前的模式将上一个节点链接到下一个节点。应对这个问题有两种方法:
- 如果头节点需要删除,则将头节点向后移动一位,直接忽略第一个节点:
head = head.next
但是,这个方法会出现两种删除节点的方法(头结点和中间节点),不统一
- 另一种方法是在头结点之前添加一个虚拟节点
dummyhead
(推荐)。 这样可以保证只使用一种方法删除所有可能的节点。
代码实现
第一种方法:
class Solution(object):
def removeElements(self, head, val):
# 移除头节点
while (head!=None and head.val == val):
head = head.next
# 移除后续节点
cur = head
while(cur!=None and cur.next!=None):
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return head
第二种方法:
class Solution(object):
def removeElements(self, head, val):
# 创建dummynode, dummynode链接到head
dummynode = ListNode(0, head)
cur = dummynode
while(cur!=None and cur.next!=None):
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return dummynode.next
设计链表 (LC 707)
在链表类中实现这些功能:
get(index)
:获取链表中第index
个节点的值。如果索引无效,则返回-1
。addAtHead(val)
:在链表的第一个元素之前添加一个值为val
的节点。插入后,新节点将成为链表的第一个节点。addAtTail(val)
:将值为val
的节点追加到链表的最后一个元素。addAtIndex(index,val)
:在链表中的第index
个节点之前添加值为val
的节点。如果index
等于链表的长度,则该节点将附加到链表的末尾。如果index
大于链表长度,则不会插入节点。如果index
小于0,则在头部插入节点。deleteAtIndex(index)
:如果索引index
有效,则删除链表中的第index
个节点。
示例:
输入
[“MyLinkedList”, “addAtHead”, “addAtTail”, “addAtIndex”, “get”, “deleteAtIndex”, “get”]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]
解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3
myLinkedList.get(1); // 返回 2
myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3
myLinkedList.get(1); // 返回 3
本题一共需要完成五个不同的操作:
- 初始化链表
在完成每个步骤之前,需要先初始化这个linkedlist
。注意,linkedlist
有两个属性: 一个dummyhead
, 作为链表的第一个节点; 另一个是链表的size
,用于储存链表的长度。
class MyLinkedList(object):
def __init__(self):
self.dummyhead = ListNode(0)
self.size=0
- 获取第n个节点的值
在执行这个操作前,需要先判断一下n
是否合法,合法区间0<=n<=size-1
。如果不合法,则返回-1
若合法,则将cur一个个右移至第n个位置,然后返回第n
个位置的val
def get(self, index):
if index < 0 or index > self.size-1:
return -1
cur = self.dummyhead.next
while(index>0):
cur=cur.next
index-=1
return cur.val
- 头部插入节点
先创建一个newnode
, 将新节点指向cur.next, 然后将cur指针指向新的节点。一定要注意节点更新的顺序,如果先更新cur.next = newnode
, 那么下一步newnode.next
的指向无法通过cur.next
知晓(已经更新了)。size+=1
, 因为插入了一个节点。
def addAtHead(self, val):
newnode = ListNode(val)
cur = self.dummyhead
newnode.next = cur.next
cur.next = newnode
self.size+=1
- 在尾部插入节点
先将cur移动到最后一个节点,即cur = cur.next until cur.next==None
。 然后将cur.next
指向newnode
。size+=1
,因为插入了一个节点。
def addAtTail(self, val):
newnode = ListNode(val)
cur = self.dummyhead
while(cur.next!=None):
cur = cur.next
cur.next = newnode
self.size+=1
- 第n个节点前插入节点
在执行这个操作前,需要先判断一下n
是否合法,合法区间0<=n<=size
;n==size
也是合法的,因为题目要求这种情况将newnode
插在尾部。如果不合法,则直接跳过这个步骤返回;如果合法,先将cur
向右边移动n
步, 然后使用之前的方法添加节点(newnode.next=cur.next
cur.next = newnode
)
def addAtIndex(self, index, val):
#注意区间,与其他情况不同,需要考虑index = size
if index<0 or index>self.size:
return
newnode = ListNode(val)
cur = self.dummyhead
while(index>0):
cur = cur.next
index-=1
newnode.next = cur.next
cur.next = newnode
self.size+=1
- 删除第n个节点
在执行这个操作前,需要先判断一下n
是否合法,合法区间0<=n<=size-1
;如果不合法,则直接跳过这个步骤返回;如果合法,先将cur
向右边移动n
步,然后使用cur.next = cur.next.next
删除节点。
def deleteAtIndex(self, index):
if index<0 or index>self.size-1:
return
cur = self.dummyhead
while(index>0):
cur = cur.next
index-=1
cur.next = cur.next.next
self.size-=1
注意在链表操作时,最好添加一个dummyhead
, 同时所有增加删减节点操作都需要将cur
节点指向要操作节点的前一个节点
完整代码:
# 定义链表节点
class ListNode(object):
def __init__(self, val=0, nextnode=None):
self.val = val
self.next = nextnode
# 定义新链表
class MyLinkedList(object):
def __init__(self):
self.dummyhead = ListNode(0)
self.size=0
def get(self, index):
if index < 0 or index > self.size-1:
return -1
cur = self.dummyhead.next
while(index>0):
cur=cur.next
index-=1
return cur.val
def addAtHead(self, val):
newnode = ListNode(val)
cur = self.dummyhead
newnode.next = cur.next
cur.next = newnode
self.size+=1
def addAtTail(self, val):
newnode = ListNode(val)
cur = self.dummyhead
while(cur.next!=None):
cur = cur.next
cur.next = newnode
self.size+=1
def addAtIndex(self, index, val):
#注意区间,与其他情况不同,需要考虑index = size
if index<0 or index>self.size:
return
newnode = ListNode(val)
cur = self.dummyhead
while(index>0):
cur = cur.next
index-=1
newnode.next = cur.next
cur.next = newnode
self.size+=1
def deleteAtIndex(self, index):
if index<0 or index>self.size-1:
return
cur = self.dummyhead
while(index>0):
cur = cur.next
index-=1
cur.next = cur.next.next
self.size-=1
反转链表 (LC 206)
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
双指针法:(一定要掌握)
初始化:快指针cur
指向head, 慢指针prev=Null
。循环向后移动直到cur=Null
, 返回prev
。每次循环,使用一个temp = cur.next
, 然后cur.next = prev
,反转指向;最后向后移动prev和cur指针, pre = cur
, cur = temp
.
代码实现
class Solution(object):
def reverseList(self, head):
cur = head
prev = None
while (cur!=None):
temp = cur.next
cur.next = prev
prev = cur
cur = temp
return prev
递归法:(附加,难理解)
在双指针法基础上改为递归,不太好理解,推荐掌握双指针即可
class Solution(object):
def reverseList(self, head):
# 初始化current = head 指向最左边的null
# 类似双指针,将current.next指向prev
return self.reverse(head, None)
def reverse(self, cur, prev):
if cur == None:
return prev # 返回null前面一个作为新head
temp = cur.next # 保留下一个节点作为用于移动current
cur.next = prev # current.next指向前一个内容
# 进行下一层循环,下一层current = temp, prev = current, 两个向右指针移动
return self.reverse(temp, cur)
注意!!! 记得递归代码每个函数都需要有返回,将想输出一层层返回到上层(主函数)
总结:
今天三题的难度还好,在第二题的addAtIndex
这里debug了很久,主要是没看到题目要求 n=size
的时候也属于合法区间。剩下两题难度还好,但是递归法需要再重新理解一下。继续加油!!!