链表经典问题
1.反转链表
问题:将链表反转,并返回新的头节点。
思路:设置两个指针,反别表示现节点和前节点,遍历链表,同时设置一个临时指针储存下一个节点。然后让现指针的next指向前指针。
def reverseList(self, head):
cur = head
pre = None
while cur:
temp = cur.next
cur.next = pre
pre = cur
cur = temp
return pre
最后前节点pre即为新的头节点。
2.移除链表元素
问题:移除给出链表中的元素等于val的节点,并返回。
(1)递归
思路:函数先判断除头节点外的节点,不断调用该函数,使得找到末节点。当链表为空时返回,对于每个函数中头节点进行判断,如果节点的值等于val则返回head.next,相当于将该节点删除,返回剩下的节点,如果不等则返回该节点,即没有删除。直到最后对真正的头节点进行判断,递归结束。
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
if not head:
return
head.next = self.removeElements(head.next,val)
return head.next if head.val == val else head
(2)迭代
思路:设置一个指针,使他的next指向头节点。遍历链表,不断对指针的下一个节点进行判断,如果节点值等于val,则使指针的next指向next.next,就实现的删除。由于可能会删除头节点,所以需要设置一个哑节点使他的next指向真正头节点
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
dummyhead = ListNode()
dummyhead.next = head
temp = dummyhead
while temp.next:
if temp.next.val == val:
temp.next = temp.next.next
else:
temp = temp.next
return dummyhead.next
(3)双指针
思路:设置两个指针分别表示前一个节点和现在的节点,遍历链表当节点的值等于val时,判断节点的位置,分别进行不同的删除操作。因为保存了前一个节点,所以删除操作会简单很多。
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
cur = head
pre = None
while cur:
if cur.val == val:
if cur == head:
head = cur.next
cur = head
elif cur.next == None:
pre.next = None
break
else:
cur = cur.next
pre.next = cur
else:
pre = cur
cur = cur.next
return head
3.奇偶链表
问题:给定单链表的头节点 head
,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
思路:先分别建立两个新链表,设置两个指针,表示奇数项节点和偶数项节点,遍历链表。指针每次走两个节点,表示都为奇数项或都为偶数项。最后让偶数链表的头节点与奇数链表的末节点相连。
注意:要先设置变量保存偶数项链表的头节点,如果想用head.next的方法访问是不行的,因为head现在与偶数项的头节点无关,head.next指向原来的head.next.next。
def oddEvenList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head:
return head
evenhead = head.next
odd = head
even = head.next
while odd.next and even.next:
odd.next = odd.next.next
even.next = even.next.next
odd = odd.next
even = even.next
odd.next = evenhead
return head
4.回文链表
问题:给你一个单链表的头节点 head
,请你判断该链表是否为回文链表。如果是,返回 true
;否则,返回 false
有以下三种方法
(1)列表
思路:建立一个列表来储存链表的值,再反转列表,如果反转后相等则为回文链表。
def isPalindrome(self, head: Optional[ListNode]) -> bool:
l = []
temp = head
while temp:
l.append(temp.val)
temp = temp.next
r = l.copy()
r.reverse()
return r == l
(2)迭代
思路:先设置一个指针指向头节点位于递归函数外,再设置递归函数。递归不断找到与递归函数外指针对称的节点,判断是否相等,不相等返回Flase。同时如果相等,则递归函数外指针往前进一个节点,即指向它的next。简单来说就是先判断头节点和末节点是否相等,然后分别前进和后退一个节点再进行判断。
def isPalindrome(self, head: Optional[ListNode]) -> bool:
self.head = head
def check(cur = head):
if cur:
if not check(cur.next): #这一行保证只要递归函数的一层不相等,就整个函数返回False
return False
if cur.val != self.head.val:
return False
self.head = self.head.next
return True
return check()
(3)快慢指针
思路:设置快慢指针,快指针一次走两个节点,慢指针一次走一个节点。当快指针走完整个链表时,慢指针刚好走到链表的中间节点。然后对慢指针之后的链表进行反转,再与反转前的链表进行对比,如果不等就返回False,否则返回True。
def isPalindrome(self, head: Optional[ListNode]) -> bool:
self.fast = self.slow = temp = head
while self.fast.next and self.fast.next.next:
self.fast = self.fast.next.next
self.slow = self.slow.next
self.slow = self.reverse(self.slow)
while self.slow and temp:
if temp.val != self.slow.val:
return False
temp = temp.next
self.slow = self.slow.next
return True
def reverse(self,head):
cur = head
pre = None
while cur:
temp = cur.next
cur.next = pre
pre = cur
cur = temp
return pre