数组、链表、哈希表的增删查改效率

本文对比了数组、链表和哈希表在创建、增删查改操作上的效率,揭示了不同数据结构的优劣。数组适合小规模数据,创建快但增删慢;链表插入删除快但查找慢;哈希表在增删查改上表现优秀,但创建时间较长。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组、链表、哈希表的增删查改效率

一共进行了4次测试:数据如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

由图可知,

  • 哈希表
    的平均创建时间最长,但是插入、删除和查找平均用时最少。

  • 数组
    创建用时最少,但是插入、删除操作用时最多,查找用时仅次于链表。

  • 链表
    除了查找用时最大,其余都排在数组和哈希表之间。

由以上数据可知,数组的优势在于创建数组用时少,可以适用于规模较小的数据。

链表创建时间比哈希表稍快(约17倍),但是查找用时是哈希表的1000倍以上,是数组的7倍左右。这意味着你要花较多的时间来方便你的插入、删除,但是查找还是算了吧,反正我是不会想经常去查链表里存储的东西的。

哈希表在插入、删除、以及查找操作方面有着优异的表现,但是超长的创建时间

提醒:除非你之后要进行大量的增删查改,不然还是用数组来的方便快捷。每次执行代码我的机器总是在创建哈希表的时候要停顿一下。

https://www.iteye.com/blog/351220704-2201666

为什么哈希表能够加快查找效率?

很多语言都提供map的数据类型,map一个很常用的功能,那就是key-value 的存储和查找功能。这种数据类型的实现原理就是通过哈希表来实现快速查找。

哈希表的基本原理:原本无序的集合经过哈希算法被重新调整位置,排列成新序列,也就是hash table(与其说是表,不如说是某种数据结构的数组)

哈希表的好处,查找复杂度为常数量级o(1),快!
哈希表的代价,需要通过哈希算法将原始序列映射到hash table上,并且构建hash table需要分配一段内存,从本质上说这是一种空间换时间的办法。

哈希是什么?为什么哈希存取比较快?

不太恰当的比喻:
    XM 指的是“小明”,也指的是“小萌”
    XM就是哈希值,小明和小萌就是拥有同一个哈希值的,存在同一个链表的元素。
    想要获取小萌,首先使用hashcode获取到这两个人,然后再通过equals获取到小萌。
  个人理解

在这里插入图片描述

哈希表
  其实就是一个一维数组,而数组中的每一个元素都是一个单向链表而已。这样的数据结构解决了 数组的增删元素的不足 和 链表的查询效率的不足。
  
  数组是存在连续的存储空间,而链表的存储空间不连续

### 各种数据结构操作的时间复杂度比较 #### 数组 (Array) 数组是一种线性数据结构,在固定大小的连续内存位置存储元素。 - **插入**: 在数组末尾插入新元素的操作可以在常数时间内完成 \(O(1)\),但如果需要扩展容量,则可能涉及重新分配和复制整个数组,这将花费线性的额外开销 \(O(N)\)[^1]。 - **删除**: 若要移除某个指定索引处的元素,平均情况下的时间复杂度为 \(O(N)\),因为后续所有元素都需要向前移动一位来填补空缺。 - **查询**: 访问任意给定下标的元素仅需恒定时间 \(O(1)\);然而对于查找特定值的位置则通常需要遍历整个列直到找到目标项为止,最坏情形下达到线性级别 \(O(N)\)[^2]。 - **修改**: 更新已知位置上的现有条目同样只需消耗固定的单位时间 \(O(1)\)。 ```cpp // C++ code snippet demonstrating array operations int arr[] = {0}; // Array initialization with one element. arr[0] = 5; // Modification operation at index 0 takes O(1). ``` #### 链表 (Linked List) 链表由一系列节点组成,每个节点包含指向下一个节点的链接指针以及实际储存的数据部分。 - **插入**: 如果已经定位到要插入的地方(即拥有前驱结点),那么新增加一个节点只需要改变几个指针即可实现 \(O(1)\);但是为了到达该位置而进行搜索的过程可能会耗费多达 \(O(N)\) 的代价[^3]。 - **删除**: 类似地,当确切知道待删对象所在之处时可以迅速执行此动作 \(O(1)\),不过先得花上至多 \(O(N)\) 来确定具体地点。 - **查询/访问**: 寻找某一项意味着从头开始逐个检查直至匹配成功或抵达末端,因此预期耗时也是 \(O(N)\)。 - **修改**: 更改已有记录中的信息不依赖于其他任何因素,故能瞬间达成目的 \(O(1)\)。 ```cpp struct Node { int data; struct Node* next; }; void insert(Node** head_ref, int new_data){ /* Function to add a node */ } ``` #### 哈希表 (Hash Table) 哈希表利用散列函数映射键(key)到桶(bucket)内,并以此为基础快速检索关联起来的价值(value). - **插入**: 只要在理想状态下——不存在冲突的情况下,向hash table里添加一对key-value pair几乎总是瞬时完成的任务 \(O(1)\). 当然如果发生碰撞的话,处理这些特殊情况可能导致更高的成本,比如二次探测法或是开放地址方案等策略会使整体现退化成接近 \(O(N)\)[^4]. - **删除**: 和插入相似,只要能够直接命中对应entry就可以立即清除它 \(O(1)\),反之亦然,遇到大量重复key的情形下效率也会受到影响变为大约 \(O(N)\). - **查询**: 查看是否存在某一特定key或者获取相应value的速度非常快 \(O(1)\),除非存在严重的bucket竞争现象才会拖慢速度至 \(O(N)\). - **修改**: 调整既存entry里的data field不会影响其它成员,所以这项工作依旧保持在最优状态 \(O(1)\). ```cpp #include <unordered_map> std::unordered_map<int, std::string> myMap; myMap.insert(std::make_pair(1,"one")); // Insertion example using unordered_map which is implemented as hashtable in cpp stl ``` #### 二叉搜索(Binary Search Tree, BST) BST 是一种动态集合示方法,允许高效的增减成员并维持有序排列特性. - **插入**: 新加入的关键字会被放置在一个适当的新叶节点之中,这个过程涉及到沿着路径向下寻找合适位置的动作,理论上讲每次分裂都会减少一半可能性范围从而使得期望时间为 \(\log N\) 。但在极端不平衡状况下仍有可能恶化回线性增长趋势 \(O(N)\)[^5]. - **删除**: 移走一片叶子相对简单快捷 \(O(1)\),可是针对内部节点而言就需要考虑更多细节如替换子最小最大值等问题,总体来说还是趋向于对数值取自然对数的结果 \(\log N\). 若形结构失衡严重也难以避免更差的现形式 \(O(N)\). - **查询**: 搜索流程遵循同样的原则,即不断缩小考察区间直至锁定唯一解或确认无果告终,正常情况下应呈现良好渐近性质 \(\log N\). 不过一旦遭遇高度倾斜的情况便会降级为顺序扫描模式 \(O(N)\). - **修改**: 实际上就是先查再插两步合为一体,因而其性能特征基本继承自上述两种基础运算\[O(\log n)\]. ```cpp class TreeNode { public: int val; TreeNode *left; TreeNode *right; explicit TreeNode(int x):val(x), left(nullptr), right(nullptr){} }; ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值