0
点赞
收藏
分享

微信扫一扫

Leetcode学习之链表

若如初梘 2022-02-18 阅读 23

Leetcode学习之链表

链表

链表定义

链表是由一系列节点组成的元素集合,每个节点包括两部分,数据域**item和指向下一个节点的指针next**。通过节点之间的互相连接,最终串联成一个链表

head ----> 86| | ----> 19| | ---->4| |---->12| |

创建

class Node:
    def __init__(self,item):
        self.item = item
        self.next = None

a = Node(1)
b = Node(2)
c = Node(3)
a.next = b
b.next = c

print(a.next.item)	# 2
print(a.next.next.item) # 3
print(a.next.next.next.item) # AttributeError: 'NoneType' object has no attribute 'item'

链表的逻辑结构与存储结构

逻辑结构:数据元素之间的逻辑 ,如:线性结构,树形结构,图结构

存储结构

  • 顺序存储:又称为顺序表,逻辑上相邻的元素物理位置也相邻
1
2
3
4
5
  • 链式存储:又称为单链表,逻辑上相邻的物理元素位置上不一定相邻
1
2
3
4
5
  • 索引存储
  • 散列存储

单链表的定义

代码书写如下

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

带头节点的链表(书写方便)

头结点结点1结点2结点3NULL

不带头结点的链表

结点1结点2结点3结点10NULL

链表插入元素

带头结点插入

头插入法1.png

头插入法2

头插法创建代码

def create_linklist_head(li):
    head = Node(li[0])
    for element in li[1:]:
        node = Node(element)    # 创建新节点
        node.next = head
        head = node
    return head

尾插法

尾插入法1.png

尾插法2.png

 def create_linklist_tail(li):
    head = Node(li[0])
    tail = head
    for element in li[1:]:
        node = Node(element)	# 创建新节点
        tail.next = node
        tail = node
    return head

特性

def print_linklist(lk):
    while lk:
        print(lk.item, end=',')
        lk = lk.next
lk1 = create_linklist_head([1,2,3,6,8])
lk2 = create_linklist_tail([1,2,3,6,8])

print_linklist(
    lk1
)
print('\n')
print_linklist(
    lk2
)        

8,6,3,2,1,	# 头插法创建链表为倒序

1,2,3,6,8,	# 尾插法创建链表为正序

链表节点

插入

链表节点插入.png

# 第一步,将4与2连起来
p.next = curNode.next
# 第二步,将1与4连起来
cirNode.next = p
# -----------------
# 在第i个位置插入elem元素
def Insert(head, i ,elem):
    assert i >=0
    cur = head
    while i !=0:   
        cur = cur.next
        if not cur:
            return False
    temp = cur.next
    cur.next = elem
    elem.next = temp
    return True
# 如下图演示

image-20220217190054219.png

删除

链表节点的删除

p = curNode.next
# 第一步,把1与2连起来
curNode.next= curNode.next.next 	# curNode.next = p.next
# 第二步,把节点进行删除
del p

双链表

双链表的每个节点有两个指针,一个指向后一个节点,另一个指向前一个结点

class Node_double:
    def __init__(self,item,next,prior):
        self.item = item
        self.next = None
        self.prior = None

双链表的插入

单链表插入1

p.next = curNode.next	# 2 -->3
curNode.next.prior = p  # 3 --> 2
p.prior = curNode 	# 2 -->1
curNode.next = p	# 1 -->2

双链表的删除

双链表的删除

p = curNode.next
curNode.next = p.next	# 1 -->3
p.next.prior = curNode	# 3-->1
del p

总结

顺序表(列表和数组)与链表比较

按元素查找

按下标查找

在某元素插入

删除某元素

特点

链表的插入和删除操作明显快于顺序表

链表的内存可以更灵活的分配

链表的链式结构存储对数据结构的树和图有很大的启发

练习

移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

示例 1:

image-20220218004829368.png

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

输入:head = [], val = 1
输出:[]
输入:head = [7,7,7,7], val = 7
输出:[]
# 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:
        head = ListNode(next = head)
        pre = head
        while pre.next:
            if pre.next.val == val:
                pre.next = pre.next.next
            else:
                pre = pre.next
        return head.next

旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:

旋转链表示例1.png

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

示例 2:

在这里插入图片描述

输入:head = [0,1,2], k = 4
输出:[2,0,1]
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        if not head:
            return None
        length = 0
        temp = head
        while temp.next:
            length += 1 
            temp = temp.next
        temp.next = head 
        k = k % (length + 1)
        temp = head 
        for i in range(length - k):
            temp = temp.next 
        head = temp.next
        temp.next = None
        return head

习题:

合并两个有序链表

示例 1:

合并两个有序链表.png

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        if  list1 is None:
            return list2
        elif  list2 is None:
            return list1
        elif list1.val < list2.val:
            list1.next = self.mergeTwoLists(list1.next, list2)
            return list1
        else:
            list2.next = self.mergeTwoLists(list1,list2.next)
            return list2

相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

图示两个链表在节点 c1 开始相交**:**

相交链表.png

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headAheadB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

相交链表示例1 .png

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headAheadB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

image-20220218011638688.png

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

相交链表示例3.png

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。
# 双指针
# 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 

删除排序链表中的重复元素

给定一个已排序的链表的头 head删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表

示例 1:

删除列表中元素示例1.png

示例2:

输入:head = [1,1,2,3,3]
输出:[1,2,3]
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head:
            return head 
        pre = head 
        while pre.next:
            if pre.val == pre.next.val:
                pre.next = pre.next.next
            else:
                pre = pre.next 
        return head 

参考

举报

相关推荐

0 条评论