代码随想录算法训练营-链表章节

Leetcode 刷题记录 Day3

链表基础理论

链表是一种以指针模式串在一起的节点, 每个节点有两个部分(数据和指针), 指针区域存放下一个节点的位置, 最后一个节点指向None

链表类型:

  • 单链表(如上图所示)
  • 双链表: 与单链表不同的是有两个指针, 可以指向前一个位置。既可以向上查询也可以向下。
  • 循环链表: 链表头尾相连。

单链表定义:

class ListNode:
    def __init__(self, val, next=None):
        self.val = val
        self.next = next

删除节点:

将前一个节点的next连接到删除节点的next即可(如图删除节点D)

 

 添加节点:

将插入位置前一个节点的next连接到添加节点, 添加节点的next连接至前一个节点原来的next.(如图添加节点F)

性能分析:

数组: 插入/删除时间复杂度O(n), 查询时间复杂度O(1)

链表:  插入/删除时间复杂度O(1), 查询时间复杂度O(n)

203. Remove Linked List Elements

学会使用虚拟节点, 使得后续链表操作一致性(处理头结点), 否则需要额外做删除头结点操作

首先要判断head不为空

从虚拟节点开始而不是head, 因为是单项链表, 如果要删除节点需要让前一个节点指向后面的节点。判断curr.next, 进行删除, curr.next = curr.next.next即可

最后return的是dummyhead.next 而不是head, 因为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]:
        dummy_head = ListNode(next = head)
        curr = dummy_head
        # 即使头结点为空 不会报错
        while curr.next:
            if curr.next.val == val:
                curr.next = curr.next.next
            else:
                curr = curr.next
        return dummy_head.next

707. Design Linked List

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class MyLinkedList:
    def __init__(self):
        self.dummy_head = ListNode()

    def get(self, index: int) -> int:
        curr = self.dummy_head
        for i in range(index + 1):
            if curr.next:
                curr = curr.next
            else:
                return -1
        return curr.val
        
    def addAtHead(self, val: int) -> None:
        new_head = ListNode(val=val, next = self.dummy_head.next)
        self.dummy_head.next = new_head

    def addAtTail(self, val: int) -> None:
        cur = self.dummy_head
        while cur.next:
            cur = cur.next
        cur.next = ListNode(val = val)

    def addAtIndex(self, index: int, val: int) -> None:
        cur = self.dummy_head
        i = 0
        while cur.next and i < index:
            cur = cur.next
            i += 1
        if cur and i == index:
            cur.next = ListNode(val = val, next = cur.next)

    def deleteAtIndex(self, index: int) -> None:
        cur = self.dummy_head
        i = 0
        while cur.next and i < index:
            cur = cur.next
            i += 1
        if cur and cur.next:
            cur.next = cur.next.next

# 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)

206. Reverse Linked List

双指针法:

        prev, curr 两个指针, 初始化curr为head, prev为None

        循环中, 记录curr.next作为tmp用于后续更新curr, 让curr指向prev, 后续更新prev为curr, curr为tmp。循环直至curr == None停止s

# 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]:
        curr = head
        prev = None
        while curr:
            # 记录下一个节点用于后续更新curr
            tmp = curr.next
            # 更新curr指针
            curr.next = prev
            # 更新对应curr和prev,县移动prev,否则找不到curr
            prev = curr
            curr = tmp
        return prev

Recursion递归法:

# 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]:
        def reverse(curr, prev):
            if curr == None:
                return prev
            tmp = curr.next
            curr.next = prev
            return reverse(tmp, curr)

        return reverse(head, None)

Leetcode 刷题记录 Day4

24. Swap Nodes in Pairs

A -> B -> C -> D -> E ==========> B -> A -> D -> C -> E

使用虚拟头节点方式操作(才能操作head节点, 让dummyhead指向B);

用tmp1记录A的位置, 否则让dummyhead指向B后找不到A;

用tmp2记录C的位置, 否则让B指向A后找不到C;

遍历链表需要看后续是否还有两个节点进行交换, 没有节点或者只有一个节点则停止;

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy_head = ListNode(next = head)
        curr = dummy_head
        while curr.next and curr.next.next:
            tmp1 = curr.next
            tmp2 = curr.next.next.next
            curr.next = tmp1.next
            curr.next.next = tmp1
            tmp1.next = tmp2
            curr = tmp1
        return dummy_head.next

19. Remove Nth Node From End of List

同样适用dummy_head进行操作保持一致性

使用快慢指针找到倒数第N+1个节点, 因为删除倒数第N个点需要在倒数第N+1个节点进行操作.next

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy_head = ListNode(next = head)
        slow, fast = dummy_head, dummy_head
        for i in range(n):
            fast = fast.next
        while fast.next:
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next
        return dummy_head.next

160. Intersection of Two Linked List

若两个链表相交, 后续链表长度相同, 先裁剪长度较长的链表。然后一一确认收个intersection位置。

注意: 是找相同节点 而不是相同value

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        
        def get_len(head):
            '''
            Get length of linked list
            '''
            count = 0
            while head:
                head = head.next
                count += 1
            return count

        lenA, lenB = get_len(headA), get_len(headB)
        currA, currB = ListNode(next = headA), ListNode(next = headB)
        # 让后续两个list长度相同
        if lenA >= lenB:
            for _ in range(lenA - lenB):
                currA = currA.next
        else:
            for _ in range(lenB - lenA):
                currB = currB.next
        
        while currA != currB and currA:
            currA = currA.next
            currB = currB.next
        # if currA == currB, return currA, if currA == None, return None
        return currA

142. Linked List Cycle II

如何判断环: 快慢指针, 如果慢指针追上了快指针, 说明环内相遇了;

快指针一次+2, 慢指针一次+1, 快指针相对于慢指针一次走一个节点,一定相遇;

如何找环的入口

 

slow走了: x + y

fast走了: x + y + n(y + z), n代表fast在里边转了几圈(n >= 1)

2(x + y) = x + y + n(y + z) ======> x = n(y + z) - y ======> x = (n - 1)(y + z) + z

(图中x+y那个点假设叫B点,开始点叫A点)

A点出发一个指针,B点出发一个指针,相同速度, 一定会在环形入口相遇(因为B点出发指针转了n - 1圈后又走了z, 在环形入口相遇)

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummyhead = ListNode(next = head)
        fast, slow = dummyhead, dummyhead
        # 为了防止没有cycle, fast/fast.next 为None
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            # 快慢指针相遇
            if fast == slow:
                while fast != dummyhead:
                    # 头部和相遇点同时移动
                    fast = fast.next
                    dummyhead = dummyhead.next
                return fast
        return None

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值