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

news2024/11/11 10:06:20

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

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

在这里插入图片描述

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)

在这里插入图片描述

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. 在链表头部插入节点

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

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. 在链表尾部插入节点

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

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. 在链表中间插入节点

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

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  # 返回头节点

示例用法

# 创建一个简单的链表: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. 删除头节点

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

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

2. 删除尾节点

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

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 指向要删除节点的下一个节点。

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  # 返回头节点

示例用法

# 创建一个简单的链表: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. 在链表头部插入节点

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

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. 在链表尾部插入节点

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

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. 在链表中间某个节点之前或之后插入节点

在双链表的中间插入节点时,可以选择在指定节点之前或之后插入新节点。
这里假设我们需要在某个位置插入新节点。

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 指向新节点

示例用法

# 创建一个双向链表: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

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

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 指针。

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

示例用法

# 创建一个双链表: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 指向要删除节点的前驱节点。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2068699.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【qt】自定义信号

我们在上篇中&#xff0c;服务器收到的消息是由线程类去处理的&#xff0c;消息在线程类中&#xff0c;传不到widget中的ui中去&#xff0c;如果我们要在界面显示客户端的消息&#xff0c;必须通过自定义信号. 1.构建信号 当线程收到信息&#xff0c;就会被填充在ba中&#xf…

Robot Operating System——自定义Service/Client通信消息结构

大纲 初始化环境生成自定义服务的工程创建包自定义消息package.xml完整文件 CMakeLists.txt完整文件 编译注册 使用自定义服务的工程创建包代码CMakeLists.txt编译运行 工程地址参考资料 在《Robot Operating System——自定义订阅/发布的消息结构》一文中&#xff0c;我们讲解…

Linux | 文件系统进阶:Inode与软硬链接艺术剖析

当时共我赏花人&#xff0c;点检如今无一半。 - 《木兰花》(晏殊) 2024.8.24 目录 1. 文件系统的基本概念 1.1 ls -l命令查看目录信息 1.2 stat命令查看具体文件的详细信息 1.3 inode ext2文件系统的主要组成部分&#xff1a; 例子&#xff1a;创建main.c文件 文件的创建步骤&a…

leetcode:2520. 统计能整除数字的位数(python3解法)

难度&#xff1a;简单 给你一个整数 num &#xff0c;返回 num 中能整除 num 的数位的数目。 如果满足 nums % val 0 &#xff0c;则认为整数 val 可以整除 nums 。 示例 1&#xff1a; 输入&#xff1a;num 7 输出&#xff1a;1 解释&#xff1a;7 被自己整除&#xff0c;因…

继续修改原神角色数据列表

<!DOCTYPE html> <html lang"zh-cn"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>原神4.8版本获得角色数据表</title> </…

【STM32】一些外设通用内容

在学习各种外设的过程中&#xff0c;发现外设有一些通用的东西可以总结一下&#xff0c;后面发现再继续更新。图来源于正点原子的学习视频和PPT。 目录 1 外设的时钟的开启 2 外设初始化的回调机制 3 外设的中断服务函数 4 HAL库中断回调机制 5 函数的常见…

SpringCache源码解析(一)

一、springCache如何实现自动装配 SpringBoot 确实是通过 spring.factories 文件实现自动配置的。Spring Cache 也是遵循这一机制来实现自动装配的。 具体来说,Spring Cache 的自动装配是通过 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration 这个类来…

文件树控件开发

文件树控件和获取驱动信息功能 然后添加上查看文件信息的按钮 双击这个按钮添加上如下代码 void CRemoteClientDlg::OnBnClickedBtnFileinfo() {int ret SendCommandPacket(1);if (ret -1) {AfxMessageBox(_T("命令处理失败!!!"));return;}ClientSocket* pClient…

c++每日练习记录5-(链表的结尾指向nullptr)

解题方法&#xff1a;双指针法 ListNode *partition(ListNode *head, int x){ListNode *head1 new ListNode(0);ListNode *head2 head1;ListNode *head3 new ListNode(0);ListNode *head4 head3;while (head! nullptr){if (head->val < x){head1->next head;head…

成品CNC外壳的巧妙使用

有些时候10块买一个CNC外壳&#xff0c;钻个孔&#xff0c;比单独的3D打印更能提升板子的档次感&#xff1a; 这个CNC是真的好看&#xff0c;再加上3D打印辅助设计&#xff0c;堪称精美&#xff1a;

k8s安装Metabase开源报表系统

metabase是什么&#xff1f; metabase是一款开源的简易但强大同时又无缝兼容大数据和传统数据库的分析工具&#xff0c;帮助公司每一个人对企业数据的学习挖掘&#xff0c;进而达到更好的数据化运营和决策。 Metabase is a simple and powerful analytics tool which lets anyo…

热血传奇1.76版本完美仿官单机版安装教程+GM工具+无需虚拟机

今天给大家带来一款单机游戏的架设&#xff1a;热血传奇1.76版本完美仿官。 另外&#xff1a;本人承接各种游戏架设&#xff08;单机联网&#xff09; 本人为了学习和研究软件内含的设计思想和原理&#xff0c;带了架设教程仅供娱乐。 教程是本人亲自搭建成功的&#xff0c;…

软件上显示“mfc140.dll丢失”错误信息?那么mfc140.dll丢失该如何修复

mfc140.dll是 Microsoft Foundation Class (MFC) 库的一部分&#xff0c;这个库被用于基于 C 的 Windows 应用程序的开发。当 Windows 或软件上显示“mfc140.dll丢失”或“找不到 mfc140.dll”这类错误信息时&#xff0c;表示你的系统可能缺少与 Visual C 相关的组件或这些组件…

软考:软件设计师 — 14.算法基础

十四. 算法基础 1. 算法的特性 算法是对特定问题求解步骤的描述&#xff0c;它是指令的有限序列&#xff0c;其中每一条指令表示一个或多个操作。 有穷性&#xff1a;执行有穷步之后结束&#xff0c;且每一步都可在有穷时间内完成。确定性&#xff1a;算法中每一条指令必须有…

代码随想录算法训练营第三十五天 | 416. 分割等和子集

416. 分割等和子集 题目链接&#xff1a;力扣题目链接 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;动态规划之背包问题&#xff0c;这个包能装满吗&#xff1f;| LeetCode&#xff1a;416.分割等和子集 给定一个只包含正整数的非空数组。是否可以将这个数组分割…

面向对象01:类和对象的创建

本节内容视频链接&#xff1a;面向对象04&#xff1a;类与对象的创建_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV12J41137hu?p63&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.类、对象定义及关系 类&#xff08;‌Class&#xff09;‌是一个模板或蓝图&#…

如何规避DDoS攻击带来的风险?服务器DDoS防御软件科普

DDoS攻击是目前最常见的网络攻击方式之一。其见效快、成本低的特点&#xff0c;使它深受不法分子的“喜爱”。对于未受保护的企业来说&#xff0c;每次DDoS攻击的平均成本为20万美元&#xff0c;当DDoS攻击汹涌而至&#xff0c;缺失详细的保护预案&#xff0c;企业很可能会陷入…

erlang学习:gen_server书上案例22.6练习题4

昨天没有输出Fun中的io的原因是因为在任务函数中没有调用Fun方法&#xff0c;相当于只传了Fun函数但是没有进行调用&#xff0c;因此没有执行Fun函数&#xff0c;所以控制台中没有进行io的输出&#xff0c;今天在add_job中调用了Fun方法并执行&#xff0c;所以输出了相应的io。…

图像数据处理22

五、边缘检测 5.4 Hough变换 该技术主要用于检测图像中的基本形状&#xff0c;如直线、圆、椭圆等。 Hough变换的基本原理 Hough变换的基本原理是将图像空间中的直线或曲线变换到参数空间中&#xff0c;通过检测参数空间中的极值点&#xff08;局部最大值&#xff09;&…

自制镜像(贫穷版)

在装了docker的机子root目录操作 mkdir -p docker-images/tomcat-image/ cd docker-images/tomcat-image/ 把这两个红框的拉到docker-images/tomcat-image/ vim Dockerfile #导入基础镜像 from centos:7 #定义作者 maintainer GGBond<2958458916qq.com&…