20220214_datawhale34期_leetcode_2链表

二 链表

1.1 基础

链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

  • 链表结构: 常用两种---------无头单链 和 有头(保护节点)双链
    在这里插入图片描述
    在这里插入图片描述

  • 链表基本操作

  1. 创建链表
# 链节点类
class ListNode:
    def __init__(self, val = 0, next = None):
        self.val = val
        self.next = next
        
# 创建单链表

class CreatLinkedList:
    def creat(self, data):
        self.head = ListNode(0)
        cur = self.head
        for d in data:
            node = ListNode(d)
            cur.next = node
            cur = cur.next
  1. 查找
def serch(self, target):
    cur = self.head
    while cur:
        if cur.val == target:
            return cur.val
        cur = cur.next
  1. i前处插入节点
def InsertInside(self, val, i):
    count = 0
    cur = self.head
    while cur and count < i - 1:
        cur =  cur.next
        count += 1
    if not cur: return "Error"
    node = ListNode(val)
    node.next = cur.next
    cur.next = node
  1. 删除i处节点
def removeInside(self, i):
    count = 0
    cur = self.head
    while cur and count < i - 1:
        cur = cur.next
        count += 1
    if not cur: return "Error"
    
    cur.next = cur.next.next
  • 总结
  1. 链表使用一组任意的存储单元(可以是连续的,也可以是不连续的),来存储一组具有相同类型的数据
  2. 链表最大的优点在于可以灵活的添加和删除元素。链表进行访问元素、改变元素操作的时间复杂度为O(n) ,进行头部插入、尾部插入、头部删除、尾部删除元素操作的时间复杂度是O(1)普通情况下进行插入、删除元素操作的时间复杂度为O(n)

1.2 题目

1.2.1 203 . 移除链表元素

  • 思路: 建立头部保护结点 dummy >>> 遍历链表寻找符合val值节点 >>> 删除继续遍历 >>> 注意: 遍历链表保证 cur.next 非空, 因为需要判断cur.next.val
class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        dummy = ListNode(next = head)
        cur = dummy
        while cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next
            else:               
                cur = cur.next
        return dummy.next

1.2.2 61 . 旋转链表

  • 思路: 首先计算出链表长度 length >>> 使得cur移动到尾部节点, 并将尾部节点与head连接 >>> 找到端点 — length - k处节点 >>> 建立新的尾部节点和头部节点即可
class Solution:
    def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        if not head: return head
        size = 0
        cur = head
        while cur.next:      #计算村链表长度 :size + 1
            size += 1
            cur = cur.next
        cur.next = head         # 将原链表尾部与head先连接
        k = k % (size + 1)

        cur = head
        for i in range(1, size + 1 - k):  # 移动到 length - k 的位置
            cur = cur.next
        head = cur.next
        cur.next = None

        return head

1.2.3 206 . 反转链表

  • 思路: 法一: 迭代法 >>> 先建立 pre空节点, 并由head指向空节点 >>> 遍历链表, 改变 pre, cur指向节点, 并使得 cur对应节点指向 pre对应节点 >>> 完成反转
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        pre = None
        cur = head
        while cur:
            temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        return pre
  • 法二 ---- 递归 :
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if head == None or head.next == None: #边界条件: 当前为空或下一为空
            return head
        cur = self.reverseList(head.next)

        head.next.next = head   # head.next  指向 head 即图中 5 > 4
        head.next = None  # 防止链表循环,需要将head.next设置为空
        return cur        #每次递归返回链表最后节点

1.2.4 21 . 合并两个有序链表

  • 思路: 递归 >>> 当l1节点值较小时 , 其下一节点可由 l1.nextl2求出 >>>>> 当某一链表为空 返回另一链表
class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1: return l2  # 终止条件,直到两个链表都空
        if not l2: return l1
        if l1.val <= l2.val:  # 递归调用
            l1.next = self.mergeTwoLists(l1.next,l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1,l2.next)
            return l2

1.2.5 148 . 排序链表

  • 思路: 利用归并排序 >>> 快排和插排会超时 >>> 归并分为分割合并两步骤 >>> 其中分割即设计快慢指针分为两个链表 >>> 合并即合并两个有序链表同上题思路
class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head or not head.next:return head 
        #分割
        slow, fast = head, head.next
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        left_head, right_head = head, slow.next
        slow.next = None  # 实现分割


        #合并
        def merge(left, right):
            if not left:return right
            if not right:return left
            if left.val <= right.val:
                left.next = merge(left.next, right)
                return left
            else:
                right.next = merge(left, right.next)
                return right
        
        #递归排序
        return merge(self.sortList(left_head), self.sortList(right_head))

1.2.6 82 . 删除排序链表中的重复元素 II

  • 思路: 快慢指针 + 哑结点 >>>> 利用slowfast两个指针, fast遍历链表, slow指向非重复节点 >>>> fast遇到重复节点, 移动到指向最后一节点 >>>> 利用slow.next = fast.next跳过重复节点 >>> 无重复节点 slow``fast各移动一个位置
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        dummy = ListNode(next = head)
        slow, fast = dummy, head
        while fast:
            while fast.next and fast.val == fast.next.val: #当出现重复节点 -> fast移动到重复节点末
                fast = fast.next
            if slow.next == fast: # slow下一节点仍为fast -> slow移动一位
                slow = slow.next
            else:
                slow.next = fast.next  # slow下一节点为重复节点 -> 跳过重复部分
            fast = fast.next
        return dummy.next

1.2.7 160 . 相交链表

  • 思路: 双指针, 长短互补 >>> 利用p1, p2指向两个链表头 >>> 在未找到相同节点前提下, 若其中一节点到达None, 指向另一链表表头 >>> 弥补长度差距, 迟早相交 >>> 若无相同节点, 也会在同时指向None时停下
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        p1, p2 = headA, headB
        while p1 != p2:
            p1 = p1.next if p1 else headB
            p2 = p2.next if p2 else headA
        return p1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值