数据结构全解析:从线性到非线性,优缺点与应用场景深度剖析
1. 线性数据结构
(1)数组(Array)(适合静态数据)
优点:
-
随机访问高效:通过索引可以直接访问元素,时间复杂度为 O(1)。
-
内存连续:数组在内存中是连续存储的,缓存友好,访问速度快。
-
简单易用:实现简单,适合存储固定大小的数据。
缺点:
-
固定大小:数组的大小通常是固定的,动态调整需要重新分配内存,效率较低。
-
插入和删除效率低:在数组中插入或删除元素需要移动其他元素,时间复杂度为 O(n)。
-
内存浪费:如果数组未完全使用,会造成内存浪费。
代码示例:
#include <iostream>
using namespace std;int main() {int arr[5] = {1, 2, 3, 4, 5}; // 静态数组cout << "Element at index 2: " << arr[2] << endl; // 随机访问// 插入元素(低效)for (int i = 4; i >= 2; i--) {arr[i + 1] = arr[i];}arr[2] = 10; // 在索引2处插入10// 打印数组for (int i = 0; i < 5; i++) {cout << arr[i] << " ";}return 0;
}
(2)链表(Linked List)(适合动态数据)
优点:
-
动态大小:链表可以动态分配内存,无需预先指定大小。
-
插入和删除高效:在已知节点的情况下,插入和删除操作的时间复杂度为 O(1)。
-
内存利用率高:链表按需分配内存,不会造成内存浪费。
缺点:
-
随机访问效率低:链表不支持随机访问,查找元素需要遍历,时间复杂度为 O(n)。
-
额外空间开销:链表需要额外的指针空间来存储节点之间的关系。
-
缓存不友好:链表节点在内存中不连续,访问速度较慢。
代码示例:
#include <iostream>
using namespace std;struct Node {int data;Node* next;
};void printList(Node* head) {while (head != nullptr) {cout << head->data << " ";head = head->next;}cout << endl;
}int main() {Node* head = new Node{1, nullptr};head->next = new Node{2, nullptr};head->next->next = new Node{3, nullptr};// 插入元素Node* newNode = new Node{10, head->next};head->next = newNode;printList(head); // 输出: 1 10 2 3return 0;
}
(3)栈(Stack)
优点:
-
简单高效:栈只允许在栈顶进行操作,实现简单,插入和删除的时间复杂度为 O(1)。
-
适合特定问题:栈非常适合解决递归问题、括号匹配、表达式求值等问题。
缺点:
-
功能受限:栈只能在一端进行操作,无法直接访问中间元素。
-
容量限制:栈的容量通常是固定的(如果使用数组实现),动态调整需要额外开销。
代码示例:
#include <iostream>
#include <stack>
using namespace std;int main() {stack<int> s;s.push(10); // 入栈s.push(20);s.push(30);cout << "Top element: " << s.top() << endl; // 访问栈顶s.pop(); // 出栈cout << "Stack size: " << s.size() << endl; // 栈大小return 0;
}
(4)队列(Queue)
优点:
-
先进先出(FIFO):队列适合处理需要按顺序处理的数据,如任务调度、缓冲区等。
-
高效操作:在队尾插入和队头删除的时间复杂度为 O(1)。
缺点:
-
功能受限:队列只能在一端插入,另一端删除,无法直接访问中间元素。
-
容量限制:队列的容量通常是固定的(如果使用数组实现),动态调整需要额外开销。
代码示例:
#include <iostream>
#include <queue>
using namespace std;int main() {queue<int> q;q.push(10); // 入队q.push(20);q.push(30);cout << "Front element: " << q.front() << endl; // 访问队头q.pop(); // 出队cout << "Queue size: " << q.size() << endl; // 队列大小return 0;
}
(5)双端队列(Deque)
优点:
-
灵活操作:双端队列允许在两端进行插入和删除操作,功能更强大。
-
高效操作:在两端插入和删除的时间复杂度为 O(1)。
缺点:
-
实现复杂:相比栈和队列,双端队列的实现更复杂。
-
容量限制:如果使用数组实现,容量通常是固定的,动态调整需要额外开销。
代码示例:
#include <iostream>
#include <deque>
using namespace std;int main() {deque<int> dq;// 在队头和队尾插入元素dq.push_front(10); // 队头插入dq.push_back(20); // 队尾插入dq.push_back(30);cout << "Front element: " << dq.front() << endl; // 访问队头cout << "Back element: " << dq.back() << endl; // 访问队尾// 删除队头和队尾元素dq.pop_front(); // 删除队头dq.pop_back(); // 删除队尾cout << "Size of deque: " << dq.size() << endl; // 输出: 1return 0;
}
2. 非线性数据结构
(1)树(Tree)
优点:
-
层次结构:树适合表示具有层次关系的数据,如文件系统、组织结构等。
-
高效搜索:二叉搜索树(BST)的搜索、插入和删除操作的时间复杂度为 O(log n)。
-
动态调整:树可以动态调整结构,适合频繁插入和删除的场景。
缺点:
-
复杂度高:树的实现和操作比线性结构复杂。
-
平衡问题:如果树不平衡(如退化为链表),性能会下降,搜索时间复杂度退化为 O(n)。
-
额外空间开销:树需要额外的指针空间来存储节点之间的关系。
代码示例:
#include <iostream>
using namespace std;struct TreeNode {int data;TreeNode* left;TreeNode* right;TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}
};// 插入节点
TreeNode* insert(TreeNode* root, int val) {if (root == nullptr) {return new TreeNode(val);}if (val < root->data) {root->left = insert(root->left, val);} else {root->right = insert(root->right, val);}return root;
}// 中序遍历(左-根-右)
void inorderTraversal(TreeNode* root) {if (root == nullptr) return;inorderTraversal(root->left);cout << root->data << " ";inorderTraversal(root->right);
}int main() {TreeNode* root = nullptr;root = insert(root, 10);root = insert(root, 5);root = insert(root, 15);root = insert(root, 3);root = insert(root, 7);cout << "Inorder Traversal: ";inorderTraversal(root); // 输出: 3 5 7 10 15return 0;
}
(2)二叉树(Binary Tree)
优点:
-
简单高效:二叉树的结构简单,适合实现二叉搜索树、堆等数据结构。
-
搜索效率高:在平衡的情况下,搜索、插入和删除的时间复杂度为 O(log n)。
缺点:
-
平衡问题:如果二叉树不平衡,性能会下降。
-
空间开销:需要额外的指针空间来存储左右子节点。
高级数据结构 约等于 基于二叉树的数据结构
代码示例:
#include <iostream>
using namespace std;struct TreeNode {int data;TreeNode* left;TreeNode* right;
};void inorderTraversal(TreeNode* root) {if (root == nullptr) return;inorderTraversal(root->left);cout << root->data << " ";inorderTraversal(root->right);
}int main() {TreeNode* root = new TreeNode{10, nullptr, nullptr};root->left = new TreeNode{5, nullptr, nullptr};root->right = new TreeNode{15, nullptr, nullptr};cout << "Inorder Traversal: ";inorderTraversal(root); // 输出: 5 10 15return 0;
}
(3)图(Graph)
优点:
-
表示复杂关系:图适合表示多对多的关系,如社交网络、地图等。
-
灵活性强:图可以是有向的或无向的,可以带权或不带权。
缺点:
-
实现复杂:图的实现和操作比线性结构和树更复杂。
-
空间开销大:图的存储(如邻接矩阵或邻接表)需要较大的空间。
-
算法复杂度高:图的遍历和搜索算法(如 DFS、BFS)的时间复杂度较高。
代码示例:
#include <iostream>
#include <vector>
using namespace std;class Graph {int V; // 顶点数vector<vector<int>> adj; // 邻接表public:Graph(int V) : V(V), adj(V) {}void addEdge(int u, int v) {adj[u].push_back(v);adj[v].push_back(u); // 无向图}void printGraph() {for (int i = 0; i < V; i++) {cout << "Vertex " << i << ": ";for (int j : adj[i]) {cout << j << " ";}cout << endl;}}
};int main() {Graph g(4);g.addEdge(0, 1);g.addEdge(0, 2);g.addEdge(1, 3);g.printGraph();return 0;
}
(4)堆(Heap)
优点:
-
高效获取极值:堆可以在 O(1) 时间内获取最大值或最小值。
-
适合优先级队列:堆适合实现优先级队列,用于任务调度等场景。
-
动态调整:堆可以动态调整结构,插入和删除的时间复杂度为 O(log n)。
缺点:
-
功能受限:堆只能快速获取极值,无法高效访问其他元素。
-
实现复杂:堆的实现比线性结构复杂。
代码示例:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;class MaxHeap {vector<int> heap;void heapifyUp(int index) {while (index > 0) {int parent = (index - 1) / 2;if (heap[parent] < heap[index]) {swap(heap[parent], heap[index]);index = parent;} else {break;}}}void heapifyDown(int index) {int size = heap.size();while (true) {int left = 2 * index + 1;int right = 2 * index + 2;int largest = index;if (left < size && heap[left] > heap[largest]) {largest = left;}if (right < size && heap[right] > heap[largest]) {largest = right;}if (largest != index) {swap(heap[index], heap[largest]);index = largest;} else {break;}}}public:void push(int val) {heap.push_back(val);heapifyUp(heap.size() - 1);}void pop() {if (heap.empty()) return;heap[0] = heap.back();heap.pop_back();heapifyDown(0);}int top() {if (heap.empty()) throw runtime_error("Heap is empty");return heap[0];}bool empty() {return heap.empty();}
};int main() {MaxHeap heap;heap.push(10);heap.push(30);heap.push(20);heap.push(5);cout << "Max element: " << heap.top() << endl; // 输出: 30heap.pop();cout << "Max element after pop: " << heap.top() << endl; // 输出: 20return 0;
}
(5)哈希表(Hash Table)(散列表)
优点:
-
高效查找:哈希表的查找、插入和删除操作的平均时间复杂度为 O(1)。
-
适合大规模数据:哈希表适合存储和查找大规模数据。
缺点:
-
哈希冲突:哈希冲突(不同的数据,计算出来的哈希值可能相同,从而导致冲突)会影响性能,需要额外的处理(如链地址法或开放地址法)。
-
空间开销大:哈希表需要较大的空间来减少冲突。
-
无序性:哈希表中的元素是无序的,无法直接按顺序访问。
雪崩效应:
- 如果两个数据有一点不同,它们的哈希值就会差别很大,从而不容易冲突。
- 哈希表的空间效率和时间效率是矛盾的,使用的空间越大,越容易设计哈希函数。
- 如果空间很小,再好的哈希函数也会产生冲突。
- 在使用哈希表时,需要在空间和时间效率上取得平衡。
代码示例:
#include <iostream>
#include <unordered_map>
using namespace std;int main() {unordered_map<string, int> hashMap;hashMap["Alice"] = 25;hashMap["Bob"] = 30;cout << "Age of Alice: " << hashMap["Alice"] << endl; // 输出: 25cout << "Age of Bob: " << hashMap["Bob"] << endl; // 输出: 30return 0;
}
总结
-
线性数据结构:适合处理顺序关系明确的数据,操作简单高效,但功能受限。
-
非线性数据结构:适合处理复杂关系的数据,功能强大,但实现和操作复杂度较高。
相关文章:
数据结构全解析:从线性到非线性,优缺点与应用场景深度剖析
1. 线性数据结构 (1)数组(Array)(适合静态数据) 优点: 随机访问高效:通过索引可以直接访问元素,时间复杂度为 O(1)。 内存连续:数组在内存中是连续存储的&…...
《使用 Python Flask + MySQL + ECharts 构建销售数据看板》实战案例笔记
《使用 Python Flask + MySQL + ECharts 构建销售数据看板》实战案例笔记 技术栈说明 后端:Python 3.10 + Flask 框架数据库:MySQL前端:ECharts 5.4 + HTML/CSS数据可视化:柱状图 / 折线图 / 饼图 / 雷达图项目结构 project/ ├── server.py # 后端服务 └──…...
StringBuilder和StringJoiner的运用
package test12; import java.util.Scanner; import java.util.StringJoiner;public class Test { public static void main(String[] args) {/* String str "你玩的真好,下次别玩了,TMD,CNM";String[] arr {"TMD", &…...
科技创新:改变生活的力量与未来趋势
人工智能在智能客服中的应用越来越普遍。它改变了传统的客服模式。AI可以快速回答用户的问题,提高了客服效率和服务质量。 首先,人工智能能够处理大量信息。智能客服可以在几秒钟内回应客户的请求。这比人工客服快得多。客户不需要等待很久就能得到答案…...
Maven指定JDK
在使用 Maven 管理 Java 项目时,有时需要指定使用特定的 JDK 版本。这通常是因为项目需要与特定版本的 JDK 兼容,或者在不同的开发环境中需要确保使用正确的 JDK 版本。通常来说在IDEA工具中设置了正确的JDK版本,使用IDEA编译也不会有任何异常…...
Jenkins持续集成与Web前端、SpringBoot项目的部署
Jenkins是一个开源的持续集成(Continuous Integration, CI)和持续交付(Continuous Delivery, CD)工具,广泛应用于软件开发过程中。它基于Java开发,旨在提供一个开放易用的软件平台,帮助软件项…...
如何使用Opentelemetry+jaeger对Go与Java项目实现分布式链路追踪
本文介绍![如何使用Opentelemetryjaeger实现分布式链路追踪] 关于opentelemetry的介绍可以看下面的文章 https://blog.csdn.net/qq_62368250/article/details/143516314本文中相关图片以及源代码地址 https://github.com/wuchenyanghaoshuai/others/blob/main/step39/README.…...
LabVIEW闭环控制系统硬件选型与实时性能
在LabVIEW闭环控制系统的开发中,硬件选型直接影响系统的实时性、精度与稳定性。需综合考虑数据采集速度(采样率、接口带宽)、计算延迟(算法复杂度、处理器性能)、输出响应时间(执行器延迟、控制周期&#x…...
Html5学习教程,从入门到精通, HTML5超链接应用的详细语法知识点和案例代码(18)
HTML5超链接应用的详细语法知识点和案例代码 超链接(Hyperlink),也称为跃点链接,是互联网和文档编辑中的一种重要概念。 超链接的定义 超链接是指从一个网页指向一个目标的连接关系,这个目标可以是另一个网页&#…...
STM32 HAL库 CAN过滤器配置
之前在STM32 f407 CAN收发 基于HAL库和Cubemx配置_stm32f407can收发程序-CSDN博客这篇博文里写了一下配置CAN收发的方法,当时由于并没有使用过滤器的现实需求,所以就也没仔细研究。现在工作中确实需要用到过滤器了,有些项目中控制器和发动机E…...
【前端面试题】Vu3常见的面试题
1.Vue3与 Vue2的核心区别有哪些? 响应式系统 : Vue2:通过Object.defineProperty 实现响应式。这种方式在处理对象属性的添加和删除时存在局限性,且无法直接监控数组的变化 ;Vue3:采用Proxy 实现响应式&…...
【数据分享】2001-2024年我国逐年植被净初级生产力(NPP)数据
植被净初级生产力(Net Primary Productivity,NPP)是生态学中的一个重要概念,表示单位面积植被在特定时间内吸收的净光合有机物,是衡量生态系统中植物通过光合作用所产生的有机物质减去植物呼吸作用消耗的有机物质的量&…...
java-正则表达式
一、正则表达式能干什么? ✅ 验证格式:手机号、邮箱、日期✅ 提取数据:从日志/文本中抓取关键信息✅ 替换文本:批量修改字符串内容 二、Java正则核心API Java中用 java.util.regex 包的两个类: Pattern:…...
正则表达式(复习)
文章目录 一、[]: 一个字符集合二、{}: 重复次数三、特殊符号四、(): 分组五、python代码示例六、注意 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个…...
sonarqube+SonarScanner+postpresql+jenkins
本地执行参考这篇 sonarqubeSonarScannerPostgreSQL实现代码质量扫描(windows)_sonarqube 10.7部署-CSDN博客 集成到Jenkins中操作如下 在 Jenkins 中安装 SonarQube Scanner 和配置 Job 1、安装 SonarQube Scanner 插件: 在 Jenkins 的管理…...
牛客周赛Round 84
本场比赛难度不大,也是本人第一次AK,最后一题用组合数学推公式,本篇博客主要讲解最后一题的思路 G-小红的陡峭值(五)(hard)_牛客周赛 Round 84 思路:本题要我们求p/q mod M ,即p*&am…...
TDengine 配置 ODBC 数据源
简介 TDengine ODBC 是为 TDengine 实现的 ODBC 驱动程序,支持 Windows 系统的应用(如 PowerBI 等)以及用户自定义开发的应用程序,通过 ODBC 标准接口访问本地、远程和云服务的 TDengine 数据库。 TDengine ODBC 提供基于 WebSo…...
numpy常用函数详解
在深度神经网络代码中经常用到numpy库的一些函数,很多看过之后很容易忘记,本文对经常使用的函数进行归纳总结。 np.arange arange是numpy一个常用的函数,该函数主要用于创建等差数列。它的使用方法如下所示: numpy.arange([star…...
deepseek 3FS编译
3FS在ubuntu22.04下的编译(记录下编译过程,方便后续使用) 环境信息 OS ubuntu 22.04内核版本 6.8.0-52-genericlibfuse 3.16.1rust 1.75.0FoundationDB 7.1.66meson 1.0.0ninja 1.10.1 libfuse编译 以下建议均在root下执行 pip3 install…...
【CXX】6.2 str — rust::Str
Rust::Str 公共 API // rust/cxx.hclass Str final { public:Str() noexcept;Str(const Str &) noexcept;Str(const String &) noexcept;// 如果输入不是 UTF-8,抛出 std::invalid_argument 异常。Str(const std::string &);Str(const char *);Str(con…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...
MySQL体系架构解析(三):MySQL目录与启动配置全解析
MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录,这个目录下存放着许多可执行文件。与其他系统的可执行文件类似,这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中,用…...
【java】【服务器】线程上下文丢失 是指什么
目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失? 直观示例说明 为什么上下文如此重要? 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程,代码应该如何实现 推荐方案:使用 ManagedE…...
深入理解 C++ 左值右值、std::move 与函数重载中的参数传递
在 C 编程中,左值和右值的概念以及std::move的使用,常常让开发者感到困惑。特别是在函数重载场景下,如何合理利用这些特性来优化代码性能、确保语义正确,更是一个值得深入探讨的话题。 在开始之前,先提出几个问题&…...
