【数据结构2】链表(使用头插法和尾插法创建链表)、链表的插入和删除、双链表节点的插入、双链表节点的删除

[1 链表](#1 链表)
[1.2 使用头插法和尾插法创建链表](#1.2 使用头插法和尾插法创建链表)
[2 链表的插入和删除](#2 链表的插入和删除)
[3 双链表](#3 双链表)
[3.1 双链表节点的插入](#3.1 双链表节点的插入)
[3.2 双链表节点的删除](#3.2 双链表节点的删除)

1 链表

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

1.1 单链表

python 复制代码
class Node:
    def __init__(self, item=None):
        self.item = item  # 存储节点的数据
        self.next = None  # 指向下一个节点的指针,初始为 None


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

1.2 使用头插法和尾插法创建链表

python 复制代码
def create_link_list_head(li: list):
    """
    使用头插法创建链表
    头插法的特点是新节点总是插入到链表的头部,因此链表的顺序会与原始列表相反。

    :param li: 包含要插入的元素的列表
    :return: 链表的头节点
    """
    head = Node(li[0])  # 初始化链表的头节点
    for element in li[1:]:  # 从列表的第二个元素开始遍历
        node = Node(element)  # 创建一个新的节点
        node.next = head  # 新节点的 next 指向当前的头节点
        head = node  # 更新头节点为新创建的节点
    return head  # 返回新链表的头节点


def create_link_list_tail(li: list):
    """
    使用尾插法创建链表
    尾插法的特点是新节点总是插入到链表的尾部,因此链表的顺序与原始列表相同。

    :param li: 包含要插入的元素的列表
    :return: 链表的头节点
    """
    head = Node(li[0])  # 初始化链表的头节点
    tail = head  # 初始化尾节点为头节点
    for element in li[1:]:  # 从列表的第二个元素开始遍历
        node = Node(element)  # 创建一个新的节点
        tail.next = node  # 当前尾节点的 next 指向新节点
        tail = node  # 更新尾节点为新创建的节点
    return head  # 返回链表的头节点


def print_link_list(lk):
    """
    打印链表中的所有节点
    从链表的头节点开始,逐个打印每个节点的值,直到链表的末尾。

    :param lk: 链表的头节点
    """
    while lk:  # 当当前节点不为 None 时继续循环
        print(lk.item, end=',')  # 打印当前节点的值,并在末尾加上逗号
        lk = lk.next  # 将当前节点更新为下一个节点


lk = create_link_list_head([1, 23, 33])
# lk = create_link_list_tail([1, 23, 33, 44])
print_link_list(lk)

2 链表的插入和删除

链表节点的插入 把4查到1和2中间

链表节点的插入可以分为几种常见情况:

在链表的头部插入节点、在链表的尾部插入节点、以及在链表的中间某个位置插入节点。

1. 在链表头部插入节点

在链表头部插入节点,也称为头插法,操作相对简单。新节点会成为链表的第一个节点,原来的头节点将成为第二个节点。

python 复制代码
class Node:
    def __init__(self, item=None):
        self.item = item  # 存储节点的数据
        self.next = None  # 指向下一个节点的指针

def insert_at_head(head: Node, item) -> Node:
    """
    在链表头部插入新节点
    :param head: 链表的头节点
    :param item: 要插入的新数据
    :return: 新的头节点
    """
    new_node = Node(item)  # 创建一个新的节点
    new_node.next = head  # 新节点的 next 指向当前的头节点
    return new_node  # 新节点现在成为头节点,返回它

2. 在链表尾部插入节点

在链表尾部插入节点,也称为尾插法。你需要遍历链表,找到当前最后的节点,并将新节点插入到它的后面。

python 复制代码
def insert_at_tail(head: Node, item) -> Node:
    """
    在链表尾部插入新节点
    :param head: 链表的头节点
    :param item: 要插入的新数据
    :return: 链表的头节点
    """
    new_node = Node(item)  # 创建一个新的节点
    if head is None:  # 如果链表为空,直接返回新节点作为头节点
        return new_node

    tail = head
    while tail.next:  # 遍历链表找到最后的节点
        tail = tail.next
    tail.next = new_node  # 将新节点插入到最后的节点后面
    return head  # 返回头节点

3. 在链表中间插入节点

在链表中间插入节点需要指定插入的位置。你需要遍历链表找到合适的位置,然后将新节点插入到它的前驱节点之后。

python 复制代码
def insert_at_position(head: Node, item, position: int) -> Node:
    """
    在链表的指定位置插入新节点
    :param head: 链表的头节点
    :param item: 要插入的新数据
    :param position: 插入的位置(从0开始)
    :return: 链表的头节点
    """
    new_node = Node(item)  # 创建一个新的节点
    if position == 0:  # 如果插入位置是头部,直接使用头插法
        new_node.next = head
        return new_node

    current = head
    current_position = 0

    while current and current_position < position - 1:  # 找到指定位置的前驱节点
        current = current.next
        current_position += 1

    if current is None:  # 如果当前节点为空,说明插入位置超出了链表长度
        raise IndexError("Position out of bounds")

    new_node.next = current.next  # 新节点的 next 指向当前位置的下一个节点
    current.next = new_node  # 前驱节点的 next 指向新节点
    return head  # 返回头节点

示例用法

python 复制代码
# 创建一个简单的链表:1 -> 2 -> 3
head = Node(1)
head = insert_at_tail(head, 2)
head = insert_at_tail(head, 3)

# 在链表头部插入0:0 -> 1 -> 2 -> 3
head = insert_at_head(head, 0)

# 在链表尾部插入4:0 -> 1 -> 2 -> 3 -> 4
head = insert_at_tail(head, 4)

# 在位置2插入1.5:0 -> 1 -> 1.5 -> 2 -> 3 -> 4
head = insert_at_position(head, 1.5, 2)

# 打印链表
print_link_list(head)

总结

  • 头插法:新节点插入到链表的头部,成为新的头节点。
  • 尾插法:新节点插入到链表的尾部,成为链表的最后一个节点。
  • 中间插入 :新节点插入到链表的指定位置,前驱节点的 next 指向新节点,新节点的 next 指向后继节点。

链表节点的删除

链表节点的删除操作可以分为几种常见情况:

删除头节点、删除尾节点、以及删除链表中间的某个节点。

1. 删除头节点

删除链表的头节点是最简单的操作。只需将头节点指向它的下一个节点即可。

python 复制代码
def delete_head(head: Node) -> Node:
    """
    删除链表的头节点
    :param head: 链表的头节点
    :return: 删除头节点后的链表新头节点
    """
    if head is None:  # 如果链表为空,直接返回 None
        return None
    return head.next  # 返回第二个节点作为新的头节点

2. 删除尾节点

删除尾节点需要遍历链表,找到倒数第二个节点,然后将它的 next 指向 None

python 复制代码
def delete_tail(head: Node) -> Node:
    """
    删除链表的尾节点
    :param head: 链表的头节点
    :return: 删除尾节点后的链表头节点
    """
    if head is None:  # 如果链表为空,直接返回 None
        return None
    if head.next is None:  # 如果链表只有一个节点,删除该节点后链表为空
        return None

    current = head
    while current.next.next:  # 找到倒数第二个节点
        current = current.next
    current.next = None  # 将倒数第二个节点的 next 指向 None
    return head  # 返回头节点

3. 删除中间节点

删除链表中间的某个节点需要先找到它的前驱节点,然后将前驱节点的 next 指向要删除节点的下一个节点。

python 复制代码
def delete_at_position(head: Node, position: int) -> Node:
    """
    删除链表指定位置的节点
    :param head: 链表的头节点
    :param position: 要删除的节点位置(从0开始)
    :return: 删除节点后的链表头节点
    """
    if head is None:  # 如果链表为空,直接返回 None
        return None
    if position == 0:  # 如果要删除头节点,直接使用 delete_head 函数
        return delete_head(head)

    current = head
    current_position = 0

    while current and current_position < position - 1:  # 找到待删除节点的前驱节点
        current = current.next
        current_position += 1

    if current is None or current.next is None:  # 如果位置超出链表长度,抛出异常
        raise IndexError("Position out of bounds")

    current.next = current.next.next  # 将前驱节点的 next 指向待删除节点的下一个节点
    return head  # 返回头节点

示例用法

python 复制代码
# 创建一个简单的链表:1 -> 2 -> 3 -> 4 -> 5
head = Node(1)
head = insert_at_tail(head, 2)
head = insert_at_tail(head, 3)
head = insert_at_tail(head, 4)
head = insert_at_tail(head, 5)

# 删除头节点:2 -> 3 -> 4 -> 5
head = delete_head(head)

# 删除尾节点:2 -> 3 -> 4
head = delete_tail(head)

# 删除位置1的节点(删除值为3的节点):2 -> 4
head = delete_at_position(head, 1)

# 打印链表
print_link_list(head)

总结

  • 删除头节点:直接将头节点指向它的下一个节点。
  • 删除尾节点 :找到倒数第二个节点,将其 next 指向 None,删除最后一个节点。
  • 删除中间节点 :找到要删除节点的前驱节点,将前驱节点的 next 指向待删除节点的下一个节点。

3 双链表

3.1 双链表节点的插入


双链表(Doubly Linked List)是一种链表结构,其中每个节点不仅包含指向下一个节点的指针(next),还包含指向前一个节点的指针(prev)。

这种结构使得在链表中进行插入、删除等操作更加灵活,因为可以从任意节点向前或向后遍历链表。

双链表节点的插入操作

在双链表中,节点的插入操作可以分为以下几种情况:

  1. 在链表头部插入节点
  2. 在链表尾部插入节点
  3. 在链表中间某个节点之前或之后插入节点

1. 在链表头部插入节点

在链表头部插入节点时,新节点将成为新的头节点,原来的头节点成为第二个节点。

python 复制代码
class Node:
    def __init__(self, item=None):
        self.item = item  # 存储节点的数据
        self.next = None  # 指向下一个节点
        self.prev = None  # 指向前一个节点

def insert_at_head(head: Node, item) -> Node:
    """
    在双链表的头部插入新节点
    :param head: 链表的头节点
    :param item: 要插入的新数据
    :return: 新的头节点
    """
    new_node = Node(item)  # 创建一个新的节点
    new_node.next = head  # 新节点的 next 指向当前的头节点

    if head is not None:  # 如果链表不为空
        head.prev = new_node  # 原头节点的 prev 指向新节点

    return new_node  # 新节点现在成为头节点,返回它

2. 在链表尾部插入节点

在链表尾部插入节点时,需要遍历链表找到最后的节点,然后将新节点插入到最后的节点之后。

python 复制代码
def insert_at_tail(head: Node, item) -> Node:
    """
    在双链表的尾部插入新节点
    :param head: 链表的头节点
    :param item: 要插入的新数据
    :return: 链表的头节点
    """
    new_node = Node(item)  # 创建一个新的节点

    if head is None:  # 如果链表为空,直接返回新节点作为头节点
        return new_node

    tail = head
    while tail.next:  # 遍历链表找到最后的节点
        tail = tail.next

    tail.next = new_node  # 将新节点插入到最后的节点后面
    new_node.prev = tail  # 新节点的 prev 指向当前的最后节点

    return head  # 返回头节点

3. 在链表中间某个节点之前或之后插入节点

在双链表的中间插入节点时,可以选择在指定节点之前或之后插入新节点。

这里假设我们需要在某个位置插入新节点。

python 复制代码
def insert_after(node: Node, item):
    """
    在指定节点之后插入新节点
    :param node: 当前节点
    :param item: 要插入的新数据
    """
    if node is None:
        return

    new_node = Node(item)  # 创建一个新的节点
    new_node.next = node.next  # 新节点的 next 指向当前节点的下一个节点
    new_node.prev = node  # 新节点的 prev 指向当前节点

    if node.next:  # 如果当前节点的 next 不为空,调整下一个节点的 prev 指向新节点
        node.next.prev = new_node

    node.next = new_node  # 当前节点的 next 指向新节点

def insert_before(node: Node, item):
    """
    在指定节点之前插入新节点
    :param node: 当前节点
    :param item: 要插入的新数据
    """
    if node is None:
        return

    new_node = Node(item)  # 创建一个新的节点
    new_node.prev = node.prev  # 新节点的 prev 指向当前节点的前一个节点
    new_node.next = node  # 新节点的 next 指向当前节点

    if node.prev:  # 如果当前节点的 prev 不为空,调整前一个节点的 next 指向新节点
        node.prev.next = new_node
    else:
        return new_node  # 如果插入的是第一个位置,返回新节点作为新的头节点

    node.prev = new_node  # 当前节点的 prev 指向新节点

示例用法

python 复制代码
# 创建一个双向链表:1 <-> 2 <-> 3
head = Node(1)
head = insert_at_tail(head, 2)
head = insert_at_tail(head, 3)

# 在链表头部插入0:0 <-> 1 <-> 2 <-> 3
head = insert_at_head(head, 0)

# 在链表尾部插入4:0 <-> 1 <-> 2 <-> 3 <-> 4
head = insert_at_tail(head, 4)

# 在节点2之后插入2.5:0 <-> 1 <-> 2 <-> 2.5 <-> 3 <-> 4
insert_after(head.next.next, 2.5)

# 在节点2之前插入1.5:0 <-> 1 <-> 1.5 <-> 2 <-> 2.5 <-> 3 <-> 4
head = insert_before(head.next.next, 1.5)

总结

  • 头部插入 :新节点成为新的头节点,调整原头节点的 prev 指向新节点。
  • 尾部插入 :新节点成为新的尾节点,调整原尾节点的 next 指向新节点。
  • 中间插入:在指定节点之前或之后插入新节点,调整前驱和后继节点的指针。

使用双链表时,需要确保插入操作时正确更新节点的 nextprev 指针,以保持链表结构的完整性。

3.2 双链表节点的删除

双链表(Doubly Linked List)的删除操作比单链表(Singly Linked List)稍复杂,

但由于双链表的每个节点都包含前一个节点的指针(prev)和下一个节点的指针(next),

删除操作变得更加灵活。

1. 删除头节点

删除双链表的头节点时,只需将头节点指向它的下一个节点,并更新新头节点的 prev 指针为 None

python 复制代码
class Node:
    def __init__(self, item=None):
        self.item = item  # 存储节点的数据
        self.next = None  # 指向下一个节点
        self.prev = None  # 指向前一个节点

def delete_head(head: Node) -> Node:
    """
    删除双链表的头节点
    :param head: 链表的头节点
    :return: 删除头节点后的链表新头节点
    """
    if head is None:  # 如果链表为空,直接返回 None
        return None
    new_head = head.next  # 新的头节点是当前头节点的下一个节点
    if new_head:  # 如果新的头节点存在
        new_head.prev = None  # 将新的头节点的 prev 指向 None
    return new_head  # 返回新的头节点

2. 删除尾节点

删除尾节点时,需要遍历链表找到倒数第二个节点,然后将它的 next 指向 None,并将其 prev 指向 None

python 复制代码
def delete_tail(head: Node) -> Node:
    """
    删除双链表的尾节点
    :param head: 链表的头节点
    :return: 删除尾节点后的链表头节点
    """
    if head is None:  # 如果链表为空,直接返回 None
        return None
    if head.next is None:  # 如果链表只有一个节点,删除该节点后链表为空
        return None

    tail = head
    while tail.next:  # 遍历找到最后一个节点
        tail = tail.next

    if tail.prev:  # 如果有前驱节点
        tail.prev.next = None  # 将倒数第二个节点的 next 指向 None
    return head  # 返回头节点

3. 删除中间节点

删除链表中的某个中间节点时,需找到要删除节点的前驱节点和后继节点,然后调整它们的 nextprev 指针。

python 复制代码
def delete_node(node: Node):
    """
    删除指定的节点
    :param node: 要删除的节点
    """
    if node is None:
        return

    if node.prev:  # 如果前驱节点存在
        node.prev.next = node.next  # 将前驱节点的 next 指向要删除节点的后继节点
    if node.next:  # 如果后继节点存在
        node.next.prev = node.prev  # 将后继节点的 prev 指向要删除节点的前驱节点

    # 可以将要删除的节点的 next 和 prev 设置为 None 来帮助垃圾回收
    node.next = None
    node.prev = None

示例用法

python 复制代码
# 创建一个双链表:1 <-> 2 <-> 3 <-> 4 <-> 5
head = Node(1)
head = insert_at_tail(head, 2)
head = insert_at_tail(head, 3)
head = insert_at_tail(head, 4)
head = insert_at_tail(head, 5)

# 删除头节点:2 <-> 3 <-> 4 <-> 5
head = delete_head(head)

# 删除尾节点:2 <-> 3 <-> 4
head = delete_tail(head)

# 假设我们要删除值为 3 的节点
node_to_delete = head.next  # 获取要删除的节点(值为 3 的节点)
delete_node(node_to_delete)  # 删除指定的节点

# 打印链表
print_link_list(head)

总结

  • 删除头节点 :更新头节点指向下一个节点,并将新的头节点的 prev 设置为 None
  • 删除尾节点 :找到倒数第二个节点,将其 next 设置为 None,并返回头节点。
  • 删除中间节点 :更新前驱节点的 next 指向要删除节点的后继节点,同时更新后继节点的 prev 指向要删除节点的前驱节点。

在进行删除操作时,确保更新了前驱节点和后继节点的指针,以保持双链表的结构完整。

相关推荐
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
阿史大杯茶1 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
Chris _data2 小时前
二叉树oj题解析
java·数据结构
Lenyiin3 小时前
02.06、回文链表
数据结构·leetcode·链表
爪哇学长3 小时前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
爱摸鱼的孔乙己3 小时前
【数据结构】链表(leetcode)
c语言·数据结构·c++·链表·csdn
烦躁的大鼻嘎3 小时前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode
C++忠实粉丝3 小时前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
daiyang123...5 小时前
测试岗位应该学什么
数据结构
kitesxian5 小时前
Leetcode448. 找到所有数组中消失的数字(HOT100)+Leetcode139. 单词拆分(HOT100)
数据结构·算法·leetcode