写程序时你有没有遇到过这种情况:明明逻辑没问题,可一到处理大量数据就卡得不行?比如做个通讯录,查个人要等好几秒,删个联系人还得转半天。问题可能不在思路,而在“怎么存数据”上。
什么是数据结构基本操作
数据结构说白了就是数据的存放方式。不同的结构,支持的操作效率也不同。常见的基本操作有增、删、查、改,但具体怎么实现,差别可大了。
比如数组和链表都能存一组数,但它们的操作特点完全不同。
数组:连续存储,查得快,插得慢
数组像是一排整齐的格子,每个位置都有编号(下标)。你想查第5个元素,直接去5号格子拿就行,时间几乎不花,这就是O(1)的查询效率。
但如果你想在中间插入一个新元素,比如在第3个位置插一个数,那第3个以后的所有数据都得往后挪一位。数据越多,挪得越久,这就是O(n)的时间开销。
int arr[5] = {10, 20, 30, 40, 50};
// 想在索引2位置插入25
// 需要把30、40、50都往后移,再把25放进去
链表:灵活连接,插得快,查得慢
链表像是用绳子串起来的一串钥匙环,每个环里存数据和下一个环的地址。你不能直接跳到第5个,只能从第一个开始一个个找,所以查一个元素可能是O(n)。
但插入就轻松多了。只要找到位置,断开绳子,把新环接进去就行,前后两个环重新连一下,其他数据完全不用动。
struct Node {
int data;
struct Node* next;
};
// 插入新节点
Node* newNode = new Node();
newNode->data = 25;
newNode->next = current->next;
current->next = newNode;
栈和队列:特殊规则下的高效操作
栈像是一摞盘子,只能从上面拿或放,遵循“后进先出”(LIFO)。这种限制反而让它在函数调用、撤销操作(比如Ctrl+Z)中特别有用。
队列则像排队买奶茶,先来的先服务,叫“先进先出”(FIFO)。消息系统、任务调度都靠它。
它们的基本操作——入栈/出栈、入队/出队——都是O(1),因为只在固定端进行。
树:分层管理,快速查找
如果你用过文件夹,那你已经用过树结构了。根目录是根节点,下面分出子文件夹,层层嵌套。
二叉搜索树更聪明,左边的小,右边的大。查一个数时,每次比较都能排除一半,效率达到O(log n),比一个个找快多了。
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
实际场景中的选择
做购物车功能,频繁添加删除商品,用链表比数组更合适;做排行榜,需要快速按分数排序访问,用堆(一种特殊的树)更高效;做字典查询,键值匹配,哈希表几乎是首选。
每种结构都有它的“舒适区”。了解基本操作的特点,才能在写代码时不光让程序跑起来,还能让它跑得快、扛得住压力。