算法记录 | Day4链表基础
昨天没有更新,实在是没有能挤出做题的时间。( ̄︶ ̄)
LeetCode 24-两两交换链表中的节点
题目描述:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
输入:head = [1,2,3,4]
输出:[2,1,4,3]
解题思路
- 想清楚交换过程就不是很难,正常跟链表基础操作差不多。
双指针法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
# 两两交换相邻链表中的节点
# 头节点的转换和中间节点转换不一样,设置虚拟头节点与其他是一样的,更方便。
dummy_node = ListNode(next=head)
cur = dummy_node
while cur.next and cur.next.next:
p = cur.next
q = cur.next.next
# cur.next= cur.next.next
cur.next = q
p.next = q.next
q.next = p
cur = cur.next.next
return dummy_node.next
难点
- 总是企图用最少的指针来做,像只设置两个指针就直接写了
# cur.next= cur.next.next然后就发现中间的节点就这样被我删除了。只得老老实实多写个q。ಠ_ಠ - 注意考虑交换结点的条件,需要至少两个结点的存在。
总结
本题不是很难,画出节点来,试着交换一次就知道过程了。
LeetCode 19-删除链表的倒数第N个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
解题思路
-
链表的基本的查找操作- 先找到倒数第n+1个结点
- 思考:链表是从头开始遍历的,如何找到第 n+1 个
- ① 先从头遍历到尾,求出链表长度,再求倒数第n个。
- ② 设置快慢指针,两者相差k,快的到最后一个节点,慢的就指向倒数第k+1个
-
删除操作
解法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
# 设置头节点
pre = ListNode(next = head)
fast = slow = pre
while (n+1):
fast = fast.next
n -= 1
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return pre.next
难点
- 快慢指针第一次接触的时候想不到,现在自然而然地知道了,可能也因为前不久还做了双指针吧。
总结
这道题的关键点在于如何找到倒数第n+1个节点的位置
LeetCode 2.07-链表相交
题目描述:给两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
- 思路:算出两个链表的长度,长度相互作差,使两个指针在同一个起始位置。然后知道有两个指针相等的位置。返回该节点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
p_A = headA
p_B = headB
cur_A = headA
cur_B = headB
count_A = 0
count_B = 0
while p_A:
p_A = p_A.next
count_A += 1
while p_B:
p_B = p_B.next
count_B += 1
if count_A > count_B:
k = count_A - count_B
for i in range(k):
cur_A = cur_A.next
else:
k = count_B - count_A
for i in range(k):
cur_B = cur_B.next
while cur_A != cur_B:
cur_A = cur_A.next
cur_B = cur_B.next
return cur_A
难点
- 如何使两个指针在同一个初始位置,我之前做过这类题,所以知道解法,然后瞬间写出解法通过了。
总结
题目关键在于如何找到两个指针同时移动的位置,否则暴力解法一个一个的进行比较过于复杂了。
LeetCode 142-环形链表
题目描述:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
解题思路
- 思路:快慢指针的方法判断是否有环
- 如何判断链表是否有环-再次经过同一位置-第一个想法是利用哈希表,遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。
方法一:哈希表
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
# 这个答案就直接通过了
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
# 直接利用哈希表记录
dicts = dict()
p = head
while p:
if p not in dicts:
dicts[p] = 1
p = p.next
else:
# print(dicts)
return p
方法二:快慢指针
- 快慢指针主要是思路不太容易想到
- 如何判断有环:快指针fast走两个节点,慢指针slow走一个节点,有环的时候,一定会在环内相遇。因为快指针一定会在环中的某个节点追上慢指针(快指针比慢指针多走一个节点,两者的差距每次都会增加一个,则即快指针每次靠近慢指针一个节点,直到相遇)
- 如何找入口:这个是按照数学来计算的
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
# 方法二:快慢指针
if head and head.next:
fast = head.next.next
slow = head.next
else:
return None
while fast:
# 快慢指针相遇
if fast != slow:
if fast.next:
fast = fast.next.next
else:
return None
slow = slow.next
# 两者相遇之后找入口
else:
p = head
while p != slow:
slow = slow.next
p = p.next
return p
难点
- 快慢指针的两个关键点理解
总结
总算补上了最后这一题,很容易忘记这种解法,看看快慢指针的题可能和别的题目可以关联起来的。