python算法学习——单链表

单链表结构

单链表结构原理比较简单,主要是编程时指针位置容易搞混。
另:不能像列表一样可以按下标查找,单链表只能顺序遍历查找。
python算法学习——单链表_第1张图片
如上图所示,节点由数据域和指针域构成,当前指针的next指向下一个元素的指针域,val(图中的elem)指向当前的元素值,节点定义如下:

# 定义节点
class Node(object):
    def __init__(self,val,p=0):
        self.val = val
        self.next = p
	一个个节点连起来,即构成了单链表,而也正是这样的原因,我们只要知道一个链表的首个元素的指针,即可以知道整个链表的元素和指针。
	解题时,如下图所示,一般需要知道三个地方的指针,前一个指针prev,当前指针curr,下一个指针Next(curr.next),题目一般是需要改变这三个指针的位置,达到反转链表、删除链表某元素等的目的。
	而一般头节点会变动的情况,则可能需要设置虚拟节点,让它指向head节点,虚拟节点没有元素值。
dummy_node = ListNode(-1)	# 定义一个虚拟节点
dummy_node.next = head

python算法学习——单链表_第2张图片

LeeCode上的单链表题目分享

主要是注意表头的前一节点、当前节点、后一节点的变化关系,一般如果需要前一节点指向头节点,则需要设置一个虚拟节点,即prev.next=head,从而使head前面有一个虚拟节点连接

1. 206 反转链表、剑指offer 24 反转链表

# 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: ListNode) -> ListNode:
        prev = None
        curr = head
        while curr :
            Next = curr.next
            curr.next = prev
            prev = curr
            curr = Next
        return prev

2. 203 移除列表元素、剑指offer18

这里主要注意的是,由于移除操作没有改变curr指针指向的下一个元素,即没有改变curr.next,因此不需要创建Next变量存储curr.next。

# 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: ListNode, val: int) -> ListNode:
        header = ListNode(-1)   # 虚拟节点
        header.next=head
        prev, curr = header, head
        while(curr):
            if curr.val==val:
                prev.next=curr.next
            else:
                prev=curr
            curr = curr.next
        return header.next

3. 92 反转链表Ⅱ

用节点画图表示一下当前的prev,curr,left_node,right_node分别指在何处。。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
            def revese_link(head:ListNode):
                pre = None
                cur = head
                while cur:
                    Next = cur.next
                    cur.next = pre
                    pre = cur
                    cur = Next
            
            dummy_node = ListNode(-1)
            dummy_node.next = head
            prev = dummy_node
            for _ in range(left-1):
                prev = prev.next
            right_node = prev
            for _ in range(right-left+1):
                right_node = right_node.next
            
            left_node = prev.next
            curr = right_node.next   # 保存一下当前节点

            prev.next = None
            right_node.next = None

            revese_link(left_node)
            prev.next = right_node
            left_node.next = curr

            return dummy_node.next

4. 剑指offer 35,复杂链表的复制

(待定)比单链表多了个random节点,因此不好用while遍历的方式进行复制。

"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""
class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        def dfs(head):
            if not head: return None
            if head in visit:
                return visit[head]
            
            copy = Node(head.val,None,None)
            visit[head] = copy
            copy.next = dfs(head.next)
            copy.random = dfs(head.random)
            return copy
        visit = {}
        return dfs(head)

5. 剑指offer22. 链表中倒数第k个节点

容易想到的是,先遍历一遍,计算一下链表的长度,第二次则循环长度减k次即可。

 Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        if not head: return None
        curr = head
        count = 0
        while curr:
            count+=1
            curr = curr.next
        count = count -k
        while count:
            count-=1
            head = head.next
        return head

另一种方法是,快慢指针,快指针先走k步,然后慢指针和快指针一起走,直到快指针结束。

        if not head: return None
        fast,low = head,head
        for _ in range(k):
            fast = fast.next
        while fast:
            fast = fast.next
            low = low.next
        return low

6. 剑指offer 25 合并两个排序的链表

遍历最短的一个链表。。。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy_node = ListNode(-1)
        curr = dummy_node
        while l1 and l2:                # 取最短的
            if l1.val<l2.val:           # 迭代至一个空
                curr.next,l1 = l1,l1.next
            else: 
                curr.next,l2 = l2,l2.next
            curr = curr.next            # 往下走
        curr.next = l1 if l1 else l2    # 如果一个为空,则直接接入
        return dummy_node.next

7. 剑指offer 52 两个链表的第一个公共节点

一个绝妙的思路是,当链表A从头开始,到B的公共节点,走a+b-c步,而同时B从头开始,到A处的公共节点,需要走b+a-c步,因此,他们两一起走,走到公共节点的步数一致(a为A的长度,b为B的长度,c为公共节点的长度)。

# 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:
        A,B = headA,headB
        while A!=B:
            A = A.next if A else headB
            B = B.next if B else headA
        return A

你可能感兴趣的:(算法刷题记录,python,单链表,算法)