链表的原理和实现python

为什么需要链表

数组的底层原理是顺序存储,是一块连续的内存空间,有了这块内存空间的首地址,就能直接通过索引计算出任意位置的元素地址。

链表不一样,一条链表并不需要一整块连续的内存空间存储元素。链表的元素可以分散在内存空间的天涯海角,通过每个节点上的next, prev 指针,将零散的内存块串联起来形成一个链式结构。

这样做的好处很明显,首先就是可以提高内存的利用效率,链表的节点不需要挨在一起,给点内存 new 出来一个节点就能用,操作系统会觉得这娃好养活。

可以抽象为一条弯曲的链子,通过一个个环连接在一起。

另外一个好处,它的节点要用的时候就能接上,不用的时候拆掉就行了,从来不需要考虑扩缩容和数据搬移的问题,理论上讲,链表是没有容量限制的(除非把所有内存都占满,这不太可能)。

当然,不可能只有好处没有局限性。数组最大的优势是支持通过索引快速访问元素,而链表就不支持

这个不难理解吧,因为元素并不是紧挨着的,所以如果你想要访问第 3 个链表元素,你就只能从头结点开始往顺着 next 指针往后找,直到找到第 3 个节点才行。

单链表

链表的创建

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


def create_linked_list(nums):
    head = Listnode(nums[0])
    cur = head
    for i in range(1, len(nums)):
        cur.next = Listnode(nums[i])
        cur = cur.next
    return head


def print_linked_list(current_node):
    while current_node:
        print(current_node.val, end='->')
        current_node = current_node.next
    print('None')


if __name__ == '__main__':
    nums = [1, 2, 3, 4, 5]
    linked_list = create_linked_list(nums)
    print_linked_list(linked_list)

最终的输出结果:1->2->3->4->5->None

链表的实现

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


class MyLinkedList(object):

    def __init__(self):
        self.size = 0
        self.head = ListNode(0)

    def get(self, index):
        """
        从链表中获取指定位置的元素值。
        :param index: 要获取元素的位置索引,从0开始计数。
        :type index: int
        :return: 如果索引有效,则返回对应位置的元素值;否则返回-1。
        :rtype: int
        """
        # 检查索引是否超出链表范围
        if index < 0 or index >= self.size:
            return -1
        cur = self.head
        # 遍历链表至指定索引位置
        for _ in range(index + 1):
            cur = cur.next
        return cur.val  # 返回指定位置的元素值

    def addAtHead(self, val):
        """
        :type val: int
        :rtype: None
        """
        self.addAtIndex(0, val)

    def addAtTail(self, val):
        """
        :type val: int
        :rtype: None
        """
        self.addAtIndex(self.size, val)

    def addAtIndex(self, index, val):
        """
        在指定索引处插入一个新节点。

        :param index: 要插入节点的索引位置。如果索引大于当前链表长度,则不做任何操作;
                      如果索引小于0,则将节点插入到链表开头。
        :param val: 要插入的节点的值。
        :return: None
        """
        if index > self.size:  # 如果索引大于链表长度,则直接返回,不做任何操作
            return
        if index < 0:  # 如果索引小于0,将节点插入到链表开头
            index = 0
        self.size += 1  # 更新链表大小
        pre = self.head
        # 定位到要插入节点的前一个节点
        for _ in range(index):
            pre = pre.next  # 逐个遍历,找到正确的位置
        to_add = ListNode(val)  # 创建新节点
        to_add.next = pre.next  # 新节点指向pre节点的next节点
        pre.next = to_add  # pre的next指向新节点

    def deleteAtIndex(self, index):
        """
        从链表中删除指定索引的元素。

        :param index: 要删除的元素的索引。如果索引无效(小于0或大于等于链表长度),则不进行任何操作。
        :type index: int
        :return: None
        """
        if index < 0 or index >= self.size:
            return
        self.size -= 1
        pre = self.head
        # 移动到要删除节点的前一个节点
        for _ in range(index):
            pre = pre.next
        # 要删除节点的前一个节点next指向删除节点的后一个节点
        pre.next = pre.next.next

双链表

链表的创建

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


def create_double_linked_list(arr):
    if not arr:
        return None
    head = ListNode(arr[0])
    cur = head
    for i in range(1, len(arr)):
        node = ListNode(arr[i])
        cur.next = node
        node.prev = cur
        cur = cur.next
    return head


def print_double_linked_list(head):
    while head:
        print(head.val, end='->')
        head = head.next


if __name__ == '__main__':
    head = create_double_linked_list([1, 2, 3, 4, 5])
    print_double_linked_list(head)

链表的实现

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


class MyLinkedList:

    def __init__(self):
        self.size = 0
        self.head, self.tail = ListNode(0), ListNode(0)
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, index: int) -> int:
        """
        从链表中获取指定位置的元素值。
        """
        # 检查索引是否超出链表范围
        if index < 0 or index >= self.size:
            return -1
        # 选择从头结点还是尾结点开始遍历取决于索引的位置,以优化遍历效率
        # 以索引值作为分割,左边短就从头结点开始遍历,右边短就从尾结点开始遍历
        if index + 1 < self.size - index:
            curr = self.head
            # 遍历链表到达指定索引位置
            for _ in range(index + 1):
                curr = curr.next
        else:
            curr = self.tail
            # 遍历链表到达指定索引位置
            for _ in range(self.size - index):
                curr = curr.prev
        # 遍历链表到达指定索引位置
        return curr.val

    def addAtHead(self, val: int) -> None:
        self.addAtIndex(0, val)

    def addAtTail(self, val: int) -> None:
        self.addAtIndex(self.size, val)

    def addAtIndex(self, index: int, val: int) -> None:
        """
        在双向链表的指定位置插入一个新节点,并保持双向链表的有序。
        """
        # 处理index大于链表长度的情况,直接返回不进行操作
        if index > self.size:
            return
        # 将负索引调整为0
        if index < 0:
            index = 0

        # 确定插入位置的前驱节点和后继节点
        if index < self.size - index:
            # 当index在链表前半部分时,正常计算前驱节点和后继节点
            pred = self.head
            for _ in range(index):
                pred = pred.next
            succ = pred.next
        else:
            # 当index在链表后半部分时,从尾部向前计算前驱节点和后继节点
            succ = self.tail
            for _ in range(self.size - index):
                succ = succ.prev
            pred = succ.prev

        # 更新链表大小
        self.size += 1
        # 创建新节点并插入到链表中
        to_add = ListNode(val)
        to_add.next = succ
        to_add.prev = pred
        pred.next = to_add
        succ.prev = to_add

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self.size:
            return
        if index < self.size - index:
            # 当index在链表前半部分时,正常计算前驱节点和后继节点
            pred = self.head
            for _ in range(index):
                pred = pred.next
            succ = pred.next.next
        else:
            # 当index在链表后半部分时,从尾部向前计算前驱节点和后继节点
            succ = self.tail
            for _ in range(self.size - index - 1):
                succ = succ.prev
            pred = succ.prev.prev
        self.size -= 1
        pred.next = succ
        succ.prev = pred

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大模型微调实战之强化学习 贝尔曼方程及价值函数(五)

大模型微调实战之强化学习 贝尔曼方程及价值函数&#xff08;五&#xff09; 现在&#xff0c; 看一下状态-动作值函数的示意图&#xff1a; 这个图表示假设首先采取一些行动(a)。因此&#xff0c;由于动作&#xff08;a&#xff09;&#xff0c;代理可能会被环境转换到这些状…

不止于量子!“光与热”两大架构重塑计算前沿

在探索超越传统计算机性能的途径中&#xff0c;量子计算通常被视为一种前沿技术。然而&#xff0c;它并非解决所有计算挑战的唯一方案。事实上&#xff0c;最近有两家公司推出了基于独特物理原理的计算设备&#xff0c;这些设备专门针对特定应用设计&#xff0c;据称在处理特定…

Python数据分析之绘制相关性热力图的完整教程

前言 文章将介绍如何使用Python中的Pandas和Seaborn库来读取数据、计算相关系数矩阵&#xff0c;并绘制出直观、易于理解的热力图。我们将逐步介绍代码的编写和执行过程&#xff0c;并提供详细的解释和示例&#xff0c;以便读者能够轻松地跟随和理解。 大家记得需要准备以下条…

家用洗地机应该怎么选?哪个牌子好?市场上主流洗地机品牌推荐

洗地机的出现&#xff0c;让越来越多的家庭享受清洁的过程&#xff0c;给人们腾出来更多的时间陪伴家人和休息。但是在选购一台洗地机前&#xff0c;大家多多少少肯定有些疑问&#xff0c;洗地机到底实不实用&#xff1f;好不好用&#xff1f;能扫干净吗&#xff1f;还有哪些好…

重置密码之后无法ssh登录

背景描述 我这边有个服务器S&#xff0c;我从ServerA可以ssh上去&#xff0c;但是我从堡垒机B无法ssh上去&#xff1b;一开始以为是密码问题&#xff0c;手动重置密码&#xff0c;但是依然无法登录进去&#xff1b;一直提示密码错误&#xff1b;改了好几次密码都不行 问题原因…

OpenCV4.8 VS2019 MFC编程出现的诡异现象

OpenCV4.8及OpenCV4.4 VS2019MFC编程在调用imred&#xff08;&#xff09;函数时&#xff0c;debug X64试运行没问题。 release X64试运行时出现下面错误。 void CEasyPictureDlg::OnBnClickedOpen() {CFileDialog fdlg(TRUE, NULL, 0, OFN_HIDEREADONLY | OFN_OVERWRITEPROMP…

数据结构——实现通讯录(附源码)

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 点击主页&#xff1a;optimistic_chen和专栏&#xff1a;c语言&#xff0c; 创作不易&#xff0c;大佬们点赞鼓…

Python中cv2 (OpenCV, opencv-python)库的安装、使用方法demo最新详细教程

&#x1f42f; Python中cv2 (OpenCV, opencv-python)库的安装、使用方法demo最新详细教程 &#x1f4f8; 文章目录 &#x1f42f; Python中cv2 (OpenCV, opencv-python)库的安装、使用方法demo最新详细教程 &#x1f4f8;摘要引言正文&#x1f4d8; OpenCV库概述&#x1f680; …

手动下载huggingface数据集到本地加载

需要使用huggingface上的数据集时一般如下加载&#xff1a; import datasets dataset datasets.load_dataset("dataset_name")但是经常会报连接错误等问题&#xff0c;所以我们可以去huggingface官网下载好数据集&#xff0c;然后直接用数据集路径替换dataset_name…

Springboot+Vue项目-基于Java+MySQL的流浪动物管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

快速话术本(常用文本快速复制工具)EXE成品+软件源码

功能介绍 经常性需要重复性的输入几个不同的文本&#xff0c;来回复制很麻烦&#xff0c;这个小工具可以帮你解决&#xff0c;把要经常输入的文本添加进去&#xff0c;点击即可复制~ 链接&#xff1a;https://pan.baidu.com/s/14-U_9uzkvpCrpzBkQaDZeA?pwdu7ot 提取码&#…

IT项目管理 选择/判断 【太原理工大学】

第一章、IT项目管理 判断题 1、搬家属于项目。&#xff08; 对 &#xff09; 2、项目是为了创造一个唯一的产品或提供一个唯一的服务而进行的永久性的努力。&#xff08; 错 &#xff09; 3、项目具有临时性的特征。&#xff08; 对 &#xff09; 4、项目开发过程…

用户下单操作

一&#xff1a;用户下单需求分析和设计&#xff1a; 用户下单业务说明&#xff1a; 在电商系统中&#xff0c;用户是通过下单的方式通知商家&#xff0c;用户已经购买了商品&#xff0c;需要商家进行备货和发货。 用户下单后会产生订单相关数据&#xff0c;订单数据需要能够体…

情感分类学习笔记(1)

文本情感分类&#xff08;二&#xff09;&#xff1a;深度学习模型 - 科学空间|Scientific Spaces 一、代码理解 cw lambda x: list(jieba.cut(x)) #定义分词函数 您给出的代码定义了一个使用 jieba 分词库的分词函数。jieba 是一个用于中文分词的 Python 库。该函数 cw 是…

docker部署小试

一 1.1 需求&#xff1a;根据docker部署nginx并且实现https 1.2 前期准备 准备一台装备好的docker-ce虚拟机&#xff0c;容量至少满足4G/2C&#xff0c;同时做好关闭防火墙的操作 systemctl stop firewalld setenforce 0 1.3 实验部署 1.3.1 创建并进入文件夹 1.3.2 编辑run脚本…

如何设计一个自动化测试平台

之前写过很多自动化测试相关的文章&#xff0c;后台有同学留言&#xff1a;希望写一篇自动化测试平台的文章。他的原话是这样&#xff1a;目前市场上开源或者商业的自动化测试平台很多&#xff0c;但试用下来总感觉有些地方不太融洽&#xff0c;想自己落地一个适合自己团队和项…

紧跟生成式AI暴雨发布新时代推理服务器

近日&#xff0c;暴雨发布最新训推一体AI服务器&#xff0c;以大容量内存和灵活的高速互连选项满足各种AI应用场景&#xff0c;最大可能支持扩展插槽&#xff0c;从而大幅提升智能算力性能&#xff0c;以最优的性能和成本为企业的模型训练推理落地应用提供更好的通用算力。 AIG…

物联网实战--平台篇之(三)账户后台数据库

目录 一、账户后台设计 二、账户数据库 三、数据库操作——增 四、数据库操作——改 五、数据库操作——查 本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240…

Netty的第一个简单Demo实现

说明 Netty 的一个练习&#xff0c;使用 Netty 连通 服务端 和 客户端&#xff0c;进行基本的通信。 需求 Client 连接服务端后&#xff0c;给服务端发送消息HelloServer Server 客户端连接成功后&#xff0c;打印连接成功读取到客户端的消息后&#xff0c;打印到控制台&…

企业是保留传统的MES还是换新的MES?

在选择上MES系统的时候&#xff0c;企业可以根据自身所处行业不同、当前阶段不同&#xff0c;以及业务需求的差异&#xff0c;对症下药&#xff0c;选择适合自己的解决方案。对于有些企业本来就有MES系统&#xff0c;但是已经过时过旧&#xff0c;就要考虑换新的MES系统了. 保留…
最新文章