【数据结构】数组、双链表代码实现

news2024/11/24 17:00:20

💗💗💗欢迎来到我的博客,你将找到有关如何使用技术解决问题的文章,也会找到某个技术的学习路线。无论你是何种职业,我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章,也欢迎在文章下方留下你的评论和反馈。我期待着与你分享知识、互相学习和建立一个积极的社区。谢谢你的光临,让我们一起踏上这个知识之旅!
请添加图片描述

文章目录

  • 🍋数组(Array)
  • 🍋链表(Linked List)
  • 🍋代码实现
  • 🍋总结

🍋数组(Array)

基本原理:
    数组是一种线性数据结构,它在内存中是一段连续的存储空间。
    数组通过索引(或下标)访问元素,索引从 0 开始递增。
    所有元素的类型相同,占用的内存空间相等。

优点:
    随机访问:可以通过索引快速访问任意位置的元素,时间复杂度为 O(1)。
    索引计算简单:根据索引计算元素的内存地址很简单,只需一个乘法和一个加法操作。

缺点:
    大小固定:数组的大小在创建时就固定了,无法动态调整。
    插入和删除操作效率低:在数组中间插入或删除元素会涉及到大量元素的移动,时间复杂度为 O(n)。
    内存空间的浪费:如果数组预留了很大的空间但只存储了少量元素,会造成内存空间的浪费。

🍋链表(Linked List)

基本原理:
    链表是一种由节点组成的数据结构,每个节点包含数据和指向下一个节点的指针(或引用)。
    节点不必在内存中连续存储,通过指针将它们串联起来。

优点:
    动态大小:链表的大小可以动态增长或缩小,不需要预先分配固定大小的空间。
    插入和删除操作高效:在链表中插入或删除元素只需要改变指针的指向,时间复杂度为 O(1)。

缺点:
    随机访问低效:要访问链表中的第 k 个元素,需要从头节点开始依次遍历,时间复杂度为 O(k)。
    需要额外的空间存储指针:每个节点都需要额外的空间存储指向下一个节点的指针,占用的内存空间较大。

🍋代码实现

数组

from selenium.common import NoSuchElementException


class MyArrayList:
    def __init__(self, init_capacity=1):
        self.data = [None] * init_capacity
        self.size = 0
    #     在列表末尾添加元素 e。
    #     如果列表已满,则调用 _resize 函数扩展列表的容量。

    def add_last(self, e):
        cap = len(self.data)
        if self.size == cap:
            self._resize(2 * cap)
        self.data[self.size] = e
        self.size += 1
    #     在指定索引 index 处插入元素 e。
    #     如果列表已满,则调用 _resize 函数扩展列表的容量。
    #     使用切片操作实现在 index 处插入元素,并将后续元素向后移动一个位置。

    def add(self, index, e):
        self._check_position_index(index)
        cap = len(self.data)
        if self.size == cap:
            self._resize(2 * cap)
        self.data[index+1:self.size+1] = self.data[index:self.size]
        self.data[index] = e
        self.size += 1

    # 在列表开头添加元素 e,实际上是调用 add(0, e)。
    def add_first(self, e):
        self.add(0, e)

    #     移除并返回列表末尾的元素。
    #     如果列表的大小为容量的四分之一,则调用 _resize 函数缩小列表的容量。
    def remove_last(self):
        if self.size == 0:
            raise NoSuchElementException()
        cap = len(self.data)
        if self.size == cap // 4:
            self._resize(cap // 2)
        deleted_val = self.data[self.size - 1]
        self.data[self.size - 1] = None
        self.size -= 1
        return deleted_val

    #     移除并返回指定索引 index 处的元素。
    #     如果列表的大小为容量的四分之一,则调用 _resize 函数缩小列表的容量。
    #     使用切片操作实现在 index 处移除元素,并将后续元素向前移动一个位置。
    def remove(self, index):
        self._check_element_index(index)
        cap = len(self.data)
        if self.size == cap // 4:
            self._resize(cap // 2)
        deleted_val = self.data[index]
        self.data[index:self.size-1] = self.data[index+1:self.size]
        self.data[self.size - 1] = None
        self.size -= 1
        return deleted_val

    # 移除并返回列表开头的元素,实际上是调用 remove(0)。
    def remove_first(self):
        return self.remove(0)

    # 返回指定索引 index 处的元素。
    def get(self, index):
        self._check_element_index(index)
        return self.data[index]

    # 将指定索引 index 处的元素设置为 element,并返回原始值。
    def set(self, index, element):
        self._check_element_index(index)
        old_val = self.data[index]
        self.data[index] = element
        return old_val

    # 返回列表的大小。
    def size(self):
        return self.size

    # 返回列表是否为空。
    def is_empty(self):
        return self.size == 0

    #     将列表的容量调整为 new_cap。
    #     如果新容量小于当前大小,则不进行调整。
    def _resize(self, new_cap):
        if self.size > new_cap:
            return
        temp = [None] * new_cap
        temp[:self.size] = self.data[:self.size]
        self.data = temp

    # 用于检查索引是否在有效范围内。
    def _is_element_index(self, index):
        return 0 <= index < self.size
    def _is_position_index(self, index):
        return 0 <= index <= self.size

    # 用于检查索引是否在有效范围内,如果不在有效范围内,则抛出 IndexError。
    def _check_element_index(self, index):
        if not self._is_element_index(index):
            raise IndexError("Index: " + str(index) + ", Size: " + str(self.size))
    def _check_position_index(self, index):
        if not self._is_position_index(index):
            raise IndexError("Index: " + str(index) + ", Size: " + str(self.size))

    # 使 MyArrayList 对象可迭代,从而可以使用 for 循环遍历其中的元素。
    def __iter__(self):
        self.p = 0
        return self

    def __next__(self):
        if self.p == self.size:
            raise StopIteration
        self.p += 1
        return self.data[self.p - 1]
    # 打印
    def display(self):
        print(f"size = {self.size} cap = {len(self.data)}")
        print(self.data)


if __name__ == "__main__":
    arr = MyArrayList(3)
    for i in range(1, 6):
        arr.add_last(i)

    arr.remove(3)
    arr.add(1, 9)
    arr.add_first(100)
    val = arr.remove_last()

    for i in range(arr.size):
        print(arr.get(i))
    print(arr.display())

双链表

from selenium.common import NoSuchElementException


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

    def __init__(self):
        self.head = self.Node(None)
        self.tail = self.Node(None)
        self.head.next = self.tail
        self.tail.prev = self.head
        self.size = 0

    def add_last(self, e):
        x = self.Node(e)
        temp = self.tail.prev
        temp.next = x
        x.prev = temp
        x.next = self.tail
        self.tail.prev = x
        self.size += 1

    def add_first(self, e):
        x = self.Node(e)
        temp = self.head.next
        temp.prev = x
        x.next = temp
        self.head.next = x
        x.prev = self.head
        self.size += 1

    def add(self, index, element):
        self._check_position_index(index)
        if index == self.size:
            self.add_last(element)
            return
        p = self._get_node(index)
        temp = p.prev
        x = self.Node(element)
        p.prev = x
        temp.next = x
        x.prev = temp
        x.next = p
        self.size += 1

    def remove_first(self):
        if self.size < 1:
            raise NoSuchElementException()
        x = self.head.next
        temp = x.next
        self.head.next = temp
        temp.prev = self.head
        x.prev = x.next = None
        self.size -= 1
        return x.val

    def remove_last(self):
        if self.size < 1:
            raise NoSuchElementException()
        x = self.tail.prev
        temp = x.prev
        temp.next = self.tail
        self.tail.prev = temp
        x.prev = x.next = None
        self.size -= 1
        return x.val

    def remove(self, index):
        self._check_element_index(index)
        x = self._get_node(index)
        prev = x.prev
        next = x.next
        prev.next = next
        next.prev = prev
        x.prev = x.next = None
        self.size -= 1
        return x.val

    def get(self, index):
        self._check_element_index(index)
        p = self._get_node(index)
        return p.val

    def get_first(self):
        if self.size < 1:
            raise NoSuchElementException()
        return self.head.next.val

    def get_last(self):
        if self.size < 1:
            raise NoSuchElementException()
        return self.tail.prev.val

    def set(self, index, val):
        self._check_element_index(index)
        p = self._get_node(index)
        old_val = p.val
        p.val = val
        return old_val

    def size(self):
        return self.size

    def is_empty(self):
        return self.size == 0

    def _get_node(self, index):
        self._check_element_index(index)
        p = self.head.next
        for _ in range(index):
            p = p.next
        return p

    def _is_element_index(self, index):
        return 0 <= index < self.size

    def _is_position_index(self, index):
        return 0 <= index <= self.size

    def _check_element_index(self, index):
        if not self._is_element_index(index):
            raise IndexError("Index: " + str(index) + ", Size: " + str(self.size))

    def _check_position_index(self, index):
        if not self._is_position_index(index):
            raise IndexError("Index: " + str(index) + ", Size: " + str(self.size))

    def display(self):
        print("size =", self.size)
        p = self.head.next
        while p != self.tail:
            print(p.val, "-> ", end="")
            p = p.next
        print("null")
        print()

    def __iter__(self):
        self.p = self.head.next
        return self

    def __next__(self):
        if self.p == self.tail:
            raise StopIteration
        val = self.p.val
        self.p = self.p.next
        return val
if __name__ == "__main__":
    # 创建一个 MyLinkedList 实例
    linked_list = MyLinkedList()

    # 在链表末尾添加元素
    linked_list.add_last(1)
    linked_list.add_last(2)
    linked_list.add_last(3)

    # 在链表开头添加元素
    linked_list.add_first(0)

    # 在指定位置插入元素
    linked_list.add(2, 1.5)

    # 输出链表大小
    print("Size of linked list:", linked_list.size)

    # 输出链表中的元素
    print("Linked list elements:")
    for val in linked_list:
        print(val)

    # 移除链表开头和末尾的元素
    print("Removed first element:", linked_list.remove_first())
    print("Removed last element:", linked_list.remove_last())

    # 输出链表中的元素
    print("Linked list elements after removal:")
    for val in linked_list:
        print(val)

🍋总结

下一节,我把单链表的也给出来,顺便做两道题应用一下以上的基本操作

请添加图片描述

挑战与创造都是很痛苦的,但是很充实。

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

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

相关文章

类和对象的内存分配机制

一、类和对象的内存分配机制 二、分配机制(总结) 三、内存图分析题

git stash 正确用法

目录 一、背景 二、使用 2.1 使用之前&#xff0c;先简单了解下 git stash 干了什么&#xff1a; 2.2 git stash 相关命令 2.3 使用流程 1. 执行 git stash 2. 查看刚才保存的工作进度 git stash list 3. 这时候在看分支已经是干净无修改的(改动都有暂存到 stash) 4. 现在…

数字的魅力之情有独钟的素数

情有独钟的素数 什么是素数 素数&#xff08;Prime number&#xff09;也称为质数&#xff0c;是指在非0自然数中&#xff0c;除了1与其本身之外不拥有其他因数的自然数。也就是说&#xff0c;素数需要满足两个条件&#xff1a; 大于1的整数&#xff1b;只拥有1和其自身两个…

LeetCode “AddressSanitizer:heat-use-after-free on address“问题解决方法

heat-use-after-free &#xff1a; 访问堆上已经被释放的内存地址 现象&#xff1a;同样代码在LeetCode上报错&#xff0c;但是自己在IDE手动打印并不会报错 个人猜测&#xff0c;这个bug可能来源于LeetCode后台输出打印链表的代码逻辑问题。 问题描述 题目来自LeetCode的8…

红队打靶练习:Alfa:1

下载连接点击此处即可&#xff01; 目录 信息收集 1、arp 2、nmap 3、gobuster WEB web信息收集 FTP登录 smaba服务 crunch密码生成 提权 系统信息收集 权限提升 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, type: EN10MB, …

【C++航海王:追寻罗杰的编程之路】关于模板,你知道哪些?

目录 1 -> 泛型编程 2 -> 函数模板 2.1 -> 函数模板概念 2.2 -> 函数模板格式 2.3 -> 函数模板的原理 2.4 -> 函数模板的实例化 2.5 -> 函数参数的匹配原则 3 -> 类模板 3.1 -> 类模板的定义格式 3.2 -> 类模板的实例化 1 -> 泛型编…

深度学习疆界:探索基本原理与算法,揭秘应用力量,展望未来发展与智能交互的新纪元

目录 什么是深度学习 深度学习的基本原理和算法 深度学习的应用实例 深度学习的挑战和未来发展方向 挑战 未来发展方向 深度学习与机器学习的关系 深度学习与人类的智能交互 什么是深度学习 深度学习是一种基于神经网络的机器学习方法&#xff0c;旨在模仿人类大脑分析…

C语言学习day14:跳转语句

今天学习的跳转语句主要是三种&#xff1a; break continue goto 上一篇文章已经说过了break和continue break&#xff1a;结束这个循环 continue&#xff1a;结束当前的循环迭代&#xff0c;进行下一次的迭代 看看二者代码的区别 代码&#xff08;break&#xff09;&am…

php基础学习之文件包含

描述 在一个php脚本中&#xff0c;将另一个php文件包含进来&#xff0c;合作实现某种功能 这个描述看起来似乎和C/Java等语言的头文件/包有点类似&#xff0c;但本质是不一样的 打个比方&#xff1a; C/Java的头文件/包更像是一个工具箱&#xff0c;存放各种很完善的工具&#…

C++集群聊天服务器 muduo+nginx+redis+mysql数据库连接池 笔记 (下)

C集群聊天服务器 网络模块业务模块CMake构建项目 笔记 &#xff08;上&#xff09;-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/135991635?spm1001.2014.3001.5501C集群聊天服务器 数据模块业务模块CMake构建项目 笔记 &#xff08;上&#xff09;-CSDN博…

TMGM官网平台开户运作流程如下:

TMGM官网平台开户运作流程如下&#xff1a; 首先&#xff0c;投资者需要注册并登录TMGM官网平台。在平台上&#xff0c;投资者可以选择适合自己的交易账户类型&#xff0c;包括标准账户、高级账户等。 然后&#xff0c;投资者需要进行身份验证和资金入账操作。TMGM会要求投资…

AcWing 122 糖果传递(贪心)

[题目概述] 有 n 个小朋友坐成一圈&#xff0c;每人有 a[i] 个糖果。 每人只能给左右两人传递糖果。 每人每次传递一个糖果代价为 1。 求使所有人获得均等糖果的最小代价。 输入格式 第一行输入一个正整数 n&#xff0c;表示小朋友的个数。 接下来 n 行&#xff0c;每行一个…

【51单片机】一个简单的例子TMOD&TCON带你永远理解【(不)可位寻址】

前言 大家好吖&#xff0c;欢迎来到 YY 滴单片机系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过单片机的老铁 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Linux》专栏YY的《数据…

Vulhub kali 环境安装教程

进入 root 权限 sudo su 更新软件 apt-get update 安装 HTTPS 协议和 CA 证书 apt-get install -y apt-transport-https ca-certificates 安装 docker apt install docker.io 查看 docker 是否安装完成 docker -v 安装 pip apt-get install python3-pip 安装 docker-compose do…

装饰工程|装饰工程管理系统-项目立项子系统的设计与实现|基于Springboot的装饰工程管理系统设计与实现(源码+数据库+文档)

装饰工程管理系统-项目立项子系统目录 目录 基于Springboot的装饰工程管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员功能实现 &#xff08;2&#xff09;合同报价管理 &#xff08;3&#xff09;装饰材料总计划管理 &#xff08;4&#xff0…

CGAL Mesh分割

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 网格分割是将一个网格分解成更小的、有意义的子网格的过程。该过程用于建模,索具,纹理,形状检索,变形等应用。CGAL为我们提供了一个依赖于形状直径函数(SDF)的算法实现,即给定一个三角形表面网格包围一个3D实体…

(01)Hive的相关概念——架构、数据存储、读写文件机制

目录 一、架构及组件介绍 1.1 Hive整体架构 1.2 Hive组件 1.3 Hive数据模型&#xff08;Data Model&#xff09; 1.3.1 Databases 1.3.2 Tables 1.3.3 Partitions 1.3.4 Buckets 二、Hive读写文件机制 2.1 SerDe 作用 2.2 Hive读写文件流程 2.2.1 读取文件的过程 …

[嵌入式系统-8]:逻辑地址、虚拟地址、物理地址以及地址映射

目录 前言&#xff1a; 一、三种内存地址 1.1 逻辑地址&#xff1a;相对地址 1.1.1 什么是逻辑地址 1.1.2 逻辑地址示例 1.2 虚拟地址 1.2.1 什么是虚拟地址 1.2.2 虚拟地址实例 1.3 物理地址 1.3.1 什么是物理地址 1.3.2 物理地址示例 1.3.3 什么情况适用物理地址…

Android---Jetpack Compose学习005

动画 1. 简单值动画 示例&#xff1a;背景颜色在紫色和绿色之间&#xff0c;以动画形式切换。使用 animateColorAsState() val backgroundColor by animateColorAsState(if (tabPage TabPage.Home) Purple100 else Green300) 该句代码中&#xff0c;有一个 backgroundColo…

Swift Combine 网络受限时从备用 URL 请求数据 从入门到精通十四

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三Swift Combine 发布者publisher的生命周期 从入门到精通四Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五Swift Com…