问题

「数据结构」的主要内容有哪些,难度如何,怎样系统地学习?

回答
数据结构:构建高效软件的基石

数据结构,顾名思义,就是组织和存储数据的方式。它不仅仅是关于如何把一堆数字或者文字堆砌起来,而是关于如何以最高效、最合理的方式来管理这些数据,以便在计算机上进行各种操作,比如查找、插入、删除等等。可以说,数据结构是编写任何高效、优化的程序的根本。

数据结构的主要内容

数据结构的世界琳琅满目,但核心内容可以归纳为以下几类:

1. 线性结构 (Linear Structures):
数组 (Arrays): 这是最基础的数据结构,就像一个连续的内存空间,可以存储同类型的数据。优点是访问速度快(通过索引),缺点是大小固定,插入和删除元素效率较低。
链表 (Linked Lists): 与数组不同,链表中的元素不是连续存储的,而是通过“指针”连接。这使得插入和删除操作非常高效,但访问特定元素需要从头开始遍历。链表又分为单向链表、双向链表和循环链表等。
栈 (Stacks): 遵循“后进先出”(LIFO)原则,就像叠盘子一样,最后放上去的盘子最先被拿走。常见应用有函数调用栈、表达式求值等。
队列 (Queues): 遵循“先进先出”(FIFO)原则,就像排队买票,先来的人先被服务。常见应用有任务调度、消息队列等。

2. 非线性结构 (Nonlinear Structures):
树 (Trees): 具有层级关系的数据结构,每个节点可以有零个或多个子节点。
二叉树 (Binary Trees): 每个节点最多有两个子节点(左子节点和右子节点)。
二叉搜索树 (Binary Search Trees, BST): 二叉树的一种特殊形式,左子节点的值小于父节点,右子节点的值大于父节点。这使得查找、插入、删除操作更高效。
平衡二叉搜索树 (Balanced Binary Search Trees): 例如 AVL 树、红黑树,它们在插入删除后能自动调整结构,保持树的平衡,从而保证查找效率。
堆 (Heaps): 是一种特殊的完全二叉树,通常用于实现优先队列。分为最大堆(父节点值大于等于子节点)和最小堆(父节点值小于等于子节点)。
图 (Graphs): 数据元素之间存在任意关系的数据结构,可以看作是节点的集合和连接节点的边的集合。图的应用非常广泛,例如社交网络、地图导航、网络路由等。图又分为有向图和无向图,有带权图和无权图。
哈希表 (Hash Tables): 通过哈希函数将键映射到存储位置,实现快速查找、插入和删除。虽然平均查找速度极快(接近 O(1)),但可能存在哈希冲突,需要额外的处理机制(如链地址法、开放地址法)。

3. 字符串 (Strings): 虽然常被视为一种基本数据类型,但字符串的内部实现和操作(如模式匹配)也涉及数据结构和算法的知识。

难度分析

数据结构本身的知识点并不算特别难,很多概念都可以用生活中的例子来比喻。真正的难度体现在以下几个方面:

理解的深度: 很多时候,我们可能知道链表怎么用,但理解其底层原理,比如指针的内存管理,或者各种树的平衡机制,需要花时间去消化。
算法的配合: 数据结构与算法是密不可分的。学习数据结构往往伴随着学习各种算法,例如在二叉搜索树上的查找、插入、删除算法,在图上的遍历算法(DFS、BFS)、最短路径算法(Dijkstra、Floyd)等。算法的设计和分析往往是难点。
实际应用中的选择: 在实际开发中,面临各种数据结构时,如何选择最适合的那个,需要权衡效率、空间复杂度、易用性等因素,这需要经验的积累。
抽象思维能力: 数据结构涉及大量的抽象概念,比如“节点”、“指针”、“层级”、“集合”等,需要较强的抽象思维能力来理解。

总体来说,数据结构入门并不难,但要掌握透彻,并能在实际问题中灵活运用,则需要持续的学习和练习。可以说,它是程序员进阶路上的一个重要分水岭。

怎样系统地学习数据结构?

想要系统地学习数据结构,我建议遵循以下步骤,并注重实践:

第一阶段:打好基础,理解核心概念

1. 选择一门编程语言: 建议选择一门你熟悉且广泛使用的语言,比如 Python、Java、C++。Python 的语法简洁,能让你更专注于数据结构本身,而 C++ 在内存管理方面更接近底层,有助于深入理解。
2. 学习基本数据结构:
数组和链表: 从最简单的数组开始,理解其内存连续性。然后学习链表,重点掌握其节点结构、指针操作以及单向、双向、循环链表的区别和应用。
栈和队列: 理解它们的 LIFO 和 FIFO 原则,以及如何用数组或链表来实现它们。
3. 理解复杂度分析: 这是学习数据结构和算法的灵魂。学习时间复杂度 (Time Complexity) 和空间复杂度 (Space Complexity) 的概念,以及如何用大 O 符号 (Big O Notation) 来表示它们。例如,为什么链表的插入是 O(1),而数组的插入是 O(n)?
4. 动手实践:
理论结合代码: 每学一个数据结构,就尝试用你选择的语言实现它。自己动手写代码,会让你对概念的理解更加深刻。
解决简单问题: 找一些与这些基本结构相关的简单算法题来练习,比如用栈实现括号匹配,用队列实现迷宫寻路等。

第二阶段:深入非线性结构,拓展算法视野

1. 学习树结构:
二叉树: 理解二叉树的概念、遍历方式(前序、中序、后序、层序)。
二叉搜索树 (BST): 理解其查找、插入、删除的原理,以及它们的复杂度。
平衡二叉搜索树 (AVL、红黑树): 重点理解它们为什么要平衡,以及平衡的机制(旋转、着色等)。这部分会相对难一些,但非常重要。
堆 (Heap): 理解堆的性质,以及如何用它来实现优先队列,常用于堆排序。
2. 学习图结构:
图的表示: 掌握邻接矩阵和邻接表这两种表示方法,并理解它们的优缺点。
图的遍历: 深入理解深度优先搜索 (DFS) 和广度优先搜索 (BFS) 的原理,并学会用它们来解决问题,例如连通性判断、最短路径(无权图)。
常用图算法: 学习 Dijkstra 算法(单源最短路径,带非负权)、Floyd 算法(所有顶点对最短路径)、Prim 算法和 Kruskal 算法(最小生成树)。
3. 学习哈希表:
哈希函数: 理解哈希函数的概念,以及如何选择一个好的哈希函数。
哈希冲突: 学习常见的哈希冲突解决方法,如链地址法和开放地址法(线性探测、二次探测、双重哈希)。
4. 数据结构与算法的结合:
选择合适的结构: 学习在不同的场景下,如何根据问题的特点选择最合适的数据结构。例如,需要频繁查找,且数据量不大时,数组或哈希表可能不错;需要频繁插入删除,且有序性不那么重要时,链表可能更合适。
复杂度权衡: 思考不同数据结构和算法的复杂度,以及它们在空间和时间上的权衡。

第三阶段:实践、巩固与进阶

1. LeetCode / 刷题: 这是检验和巩固学习成果的最佳途径。从简单到困难,系统地刷题。你会发现很多算法题都是对数据结构知识点的直接应用。
分类刷题: 按照数据结构类型(数组、链表、树、图、哈希表)或算法类型(搜索、动态规划、贪心)来分类刷题,有助于集中突破。
理解题解: 遇到不会的题目,不要只看不思考。先自己尝试,实在不行再看题解,并尝试理解题解的思路和巧妙之处,然后自己再实现一遍。
2. 阅读源码: 学习你所使用的编程语言标准库中数据结构的实现。例如,Java 的 ArrayList、LinkedList,Python 的 list、dict 等。理解它们是如何设计和优化的。
3. 参与项目: 将学到的数据结构知识应用到实际项目中。无论是个人项目还是团队项目,实际应用是最好的学习方式。你会遇到各种各样的问题,迫使你去深入思考和解决。
4. 学习高级数据结构: 随着学习的深入,可以接触一些更高级的数据结构,如 Trie 树(字典树)、B/B+ 树(数据库索引常用)、Skip List(跳跃表)等。
5. 参与社区讨论: 在技术论坛、博客或社区中与其他开发者交流,分享经验,解答疑问,可以帮助你更快地成长。

学习资源推荐:

经典教材:
《算法导论》(Introduction to Algorithms) 科班必读,偏理论,但非常全面。
《数据结构与算法分析》(Data Structures and Algorithm Analysis) C/C++ 语言版和 Java 语言版都很经典。
《图解数据结构》 比较易懂,适合入门。
在线课程: Coursera、edX、Udemy 等平台都有很多优秀的数据结构与算法课程。
在线编程平台: LeetCode、牛客网、Codeforces 等是练习算法的绝佳场所。
技术博客和社区: 搜索与数据结构相关的技术博客,阅读他人的实现和讲解。

最后,请记住:

循序渐进: 不要急于求成,一步一个脚印地打好基础。
勤于思考: 遇到问题,多问“为什么”,多思考不同方案的优劣。
大量实践: 数据结构和算法是“做”出来的,不是“看”出来的。多写代码,多解决问题。
保持好奇心: 编程世界不断发展,总有新的知识等待你去发现。

通过系统的学习和大量的实践,你一定能够掌握数据结构,为你的编程之路打下坚实的基础。

网友意见

user avatar

这个题目不错,数据结构是计算机科学中非常重要的基础!

咱们就翻译一篇medium上超级超级超级火的文章,来学习八种数据结构!


原文链接:medium.com/free-code-ca
原文作者:Fahim ul Haq
原文平台:medium

尼克劳斯·维尔特,瑞士计算机科学家,在1976年写了一本名为《算法 + 数据结构 = 程序》的书。

40多年转瞬即逝,但这个公式依然成立。这也是今天我们程序员面试的时候,需要展示自己对数据结构以及他们应用场景的掌握的原因。

几乎所有的问题都需要面试者证明他们具有扎实的数据结构基本功。无论你是刚毕业也好(从大学还是编程培训营),还是有N多年的经验。

有时候这些面试题则是专门提到某种数据结构。比如,题目描述是这样开头的“给定一颗二叉树。。。”。其他的时候则是那种隐式的,比如说,“我们找到每个作者相关的书籍数目”。

学习数据结构是非常重要的,哪怕你只是想在当前的工作岗位上变得更赞一点。所以,就让我们从基础开始吧。

啥是数据结构?
简单来说,数据结构就是一种容器,按照某种既定的方式存储数据。这种“方式”能让一个数据结构在某些操作下很高效,相反,在另外的操作下就不太理想了。你的目标是为了理解这些数据结构,从而可以能从不同的数据结构中选择适合当前所面对的问题的那一种。

为啥需要数据结构?
因为数据结构是用来有规则地存储数据的,加上数据结构在计算机科学中神一般的存在,他们的价值就不言而喻了。

不过你要解决的问题是啥,你反正都得需要数据结构,方式可能不同而已。无论是面对员工工资,还是股票价格,购物清单,还是简单的电话本,这样的场景。

根据不同的应用场景,数据需要按照不同的方式存储。我们有好多可以将数据按照不同方式保存下来的数据结构。

常用的数据结构
我们先来列一下最最常用的八种数据结构,然后接下来我们会慢慢将他们讲明白。

数组

队列
链表


字母树(其实他们就是树而已,但还是值得单独拿出来讲的)
哈希表

数组


数组是最简单也最常用的数据结构。其他的数据结构诸如栈和队列,都是从数组衍生出来的。

下面是一个拥有四个元素的简单数组,包含了元素1,2,3,4.


每个元素都依附于一个正整数,称作索引,它就对应于数组中该元素所在的位置。大多数的编程语言中,数组的起始索引都为0 (0-based,译者注).

数组一般有以下两种:

一维数组(上图所示)
多维数组(数组里面包含数组)
数组的基本操作

插入 — 在给定位置插入一个元素
取值 — 返回给定位置的数值
删除 — 在给定位置删除元素
元素总数 — 数组包含元素的个数
数组类常问问题

数组中第二小的数
数组中出现的第一个无重复的数
合并两个已排序数组
重新排放数组的正数和负数



我们平时熟悉的软件操作中的撤销(回退)操作,基本会出现在所有应用中。你好奇过它是咋工作的吗?原理是这样的:你把之前的状态(有限的数量)都存到内存中,存的顺序是最新的操作存在最近一个。这个光靠数组是不能实现的。这是栈擅长的地方。

现实中也有栈的例子。比如你把一大堆书垂直叠(一本压着另外一本)起来放。为了拿到他们里面靠中部位置的书,你得把上面的书都拿走才行。这就是著名的LIFO(后进先出)的工作原理。

下图是一个包含有三个元素的栈,数值为1,2,3. 元素3在栈顶,它会被最先删除。

栈的基本操作

进栈 — 在栈顶插入元素
出栈 — 把栈顶元素弹出(删除)
判空 — 返回栈是否为空
栈顶元素 — 只返回栈顶元素而不删除
译者注:对于栈所有的操作,都只出现在栈顶这个地方


栈常见的面试问题

借助栈来计算后缀表达式的值
将栈里的元素进行排序
判断括号表达式是否合法


队列


和栈类似,队列是另外一种线性数据结构。这种数据结构将元素按照顺序的方式存储。和栈最本质的区别就是:和后进先出相反,队列实现了先进先出的特性(FIFO, First in First Out)。

队列在生活中有非常贴切的例子:一堆人排在售票台前面。如果新来了一个人,这个人得排在队尾,而不是队伍前面。另一方面,排在第一的人则能第一个买到票,然后离开队伍。

下面是一个包含了四个元素的队列(1, 2, 3, 4)。1站在队头,会被第一个删除。

队列的常用操作

进队 — 在队尾加入一个元素
出队 — 从队头删除元素
判空 — 判断队列是否为空
队头元素 — 返回但不删除队头元素

常见的队列题

用队列实现栈
将队列里面的前k个元素翻转
借助队列来产生从1到n的二进制数


链表


链表是另外一种重要的线性数据结构。链表初看起来和数组很类似,但他们在内存分配,内部结构,以及像插入和删除这样的基本操作上,都是不一样的。

链表就是一串 串起来 的节点,他们的每一个节点都包含了数据和指向下一个节点的信息。链表有头结点,指向链表中的第一个元素。

链表结构经常用来实现文件系统,哈希表,以及邻接表。

下图是一个链表的内部结构图示。

我们常见的链表有以下两种:

Singly Linked List (Unidirectional)
Doubly Linked List (Bi-directional)
单链表 (单一方向)
双链表 (双向)


链表基本操作:

末端插入 — 在链表的末尾插入给定元素
头部插入 — 在链表的头部插入给定元素
删除 — 在链表中删除给定元素
头部删除 — 删除头部第一个元素
搜索 — 判断给定元素是否存在于链表中
判空 — 判断链表是否为空


常见的链表问题

翻转链表
检查链表中是否有环
返回距离尾部距离为N的节点
删除链表中的重复元素


图包含一系列的节点,这些节点通过网络相互连接起来。这些节点也被称为Vertcies。对于每个对子(x, y),我们则称为边,表示节点x和节点y是相连的。边也可能包含权重或是花费信息,表明了从x到也所需要的消耗。

图的类型:

无向图
有向图


在计算机语言中,图通常用下面两种方法表示:

邻接矩阵
邻接表


常用的图遍历算法:

宽度优先搜索
深度优先搜索

常见的图问题

实现宽搜和深搜
判断一个图是不是一棵树
数图中的边数
找两个节点之间的最短路径



树是非线性数据结构,它也是由节点和边组成的。因此树和图类似,但他们最大的不同是树上没有环存在。

树被广泛应用在AI和其他复杂算法中,因为它能提供高效的存储,使得问题能得以解决。

下面是一颗简单树,图中也包含了常见的树的术语。

我们可以有以下的各种树的形状:

  • N叉树
  • 平衡树
  • 二叉树
  • 二叉搜索树
  • AVL树
  • 红黑树
  • 2-3树


上面这些树中,以二叉树和二叉搜索树最为常用。

常见的树的问题

求二叉树的高度
求二叉搜索树中的第k大的数值
找离根节点距离为k的所有节点
找给定节点的所有祖先节点


字母树


字母树,也叫做前缀树,是一种树形的数据结构,它是一种解决字符串相关的问题的高效数据结构。能快速查询回馈信息,经常用在字典中查询单词的场景下,它能为搜索引擎提供自动补全,甚至能帮到IP查询。

下图演示了如何将三个单词(top, thus, their)插入到字母树中,并保存下来:

在字母树中,单词都是从下至下一个字母一个字母保存起来的。绿色的节点(p, s, r)表示的是该节点是一个单词的最后一个字母,p对应top,s对应thus,而r则对应于their。

常见的字母树问题:

数字母树中的单词总数
打印字母树中所有的单词
用字母树排序数组
借助字母树来从字典中取单词
建一个满足T9的字典(译者注:T9 stands forText on 9 keys)


哈希表


哈希是一个分辨不同的实体,从而将每个实体存储在某个预先计算好的索引上,这个预先算出来的值被称作“键”。因此,实体都是由键值对的形式存放的,把一大堆这样的东西称为字典。每个实体都能通过键来找到。基于哈希这种思想的数据结构有不少,但最常用的是哈希表。

哈希表一般通过数组来实现。

哈希表的效率取决于以下三个因数:

  1. 哈希函数
  2. 哈希表的容量
  3. 冲突避免方式

下面这图演示了我们是怎么从哈希值匹配到一个数组中的。该数组的索引是通过函数函数求出来的。


常见问题:
找数组中的对称对子
追踪旅程的完整路径
检查一个数组是否为另一数组的子集
检查多个数组之间是不是没有共同元素


上面就是八种你在算法面试之前必知必会的数据结构。

具体的学习,可以参考原文作者开发educative上的数据结构课程:

专门针对数据结构的课程则有:

C++:

JavaScript:

Java:

Python:

我上过其中的Java版本,课程是把数据结构里面的基础数据结构都用java实现了一遍,对于用java的同学特别有帮助,java的基础在刷题的过程中,还是要必须掌握的。


(如果你需要上面这些算法课程,那么你可以使用 awesome-developer 的折扣码获得网站所有课程的额外15%off!上面的折扣码针对单独购买所有课程有效。

如果想买订阅Subscriptions)的小伙伴,则可以用0820-ZH93025(必须一模一样输入)的coupon code来获取额外八折的优惠按年和按月均适用,折扣码十月四号过期,有需要的小伙伴抓紧)。

过了十月四号就只能用 ZHIHUEDU-10 的九折优惠了,大家有需要的抓紧,还有不到两周!

user avatar
数据结构是用来学习如何恰当选择数据的存储方式。
数据结构的产生是为了满足特定的存储要求。

常用的数据结构有:

线性结构:顺序表、链表、队列、栈等。

树状结构:二叉树、AVL树、红黑树、B树等。

图结构:无向图、有向图等。

线性结构

顺序表 / 链表:

从常理上看,我们一般存放数据都是顺序表,例如 在图书馆检索对应内容的书籍时,我们会发现内容上相近的书往往在位置上也相近,这就给我们检索某方向内容时提供了便利。

但是当书堆了很高,而我们想要往里面放书的时候,我们要把盖在上面的大部分书移走,放完这本书后再把书移回来,可想而知,随着放的书越多,插入一本书花费的精力也就越多。

而链表就不一样,链表的设计理念更类似藏宝图之旅,首先你会获得一张藏宝图,

藏宝图上标注了第一个宝藏在哪里,你顺着 藏宝图的指引能够顺利找到第一个宝藏,然后每个宝藏里都保存了下一个宝藏在哪以及下一个宝藏是什么的信息,只有挨个往下找才能找到最终的宝藏。这样可以在任意两个宝藏之间加一个新宝藏,只需要改动相邻两个宝藏的信息就可以完成新元素的插入功能。

队列 / 栈:

现实生活中大家排队都以 队列 为主,排队买东西讲究先来后到,在操作系统上也要讲究先来后到,先发送的指令先运行。

但是有时候 不同紧急程度的问题需要调换执行顺序,先执行紧急度高的任务。例如排队上厕所的时候,要是按照先来后到的原则执行,就会有人尿裤子(捂脸),所以要 产生一种按紧急程度需求的实现方法,于是出现了栈。

为什么要把这种数据结构叫做栈?因为就像古时候的客栈一样,栈是旅客和货物的中转站,是供人们休息的地方。

假设A、B两辆车同向而行,对向来了辆车C,但是因为路太窄了,两辆车不能并排开过去。A、B俩司机商量往后倒车,等 B 先倒车好了 A 才能倒车,然后才能让 C 通行。

栈的特点就是先进来的后出去,就像图里的小汽车一样,只有 B 先离开了,A 才能离开退让。

常见的应用主要有火车、地铁等的调度:

通过栈的设计可以使改变火车运行的顺序,达到火车实时调度的作用。

树状结构

二叉树 / AVL 树:

如果要设计一套问卷调查来评价一个人的内心健康程度,我们通常会列举一系列问题,根据他的回答来评价。

假设每个问题只能回答是否,根据回答跳转到相应的下一个问题,最后得到评价结果。那么在设计问题的时候,如果问题设计不好,就会出现下图的问题:

这个时候就需要 AVL 树的设计思路,使得每个人需要回答的问题数量尽可能相同,不能产生太大的偏差,所以我们就需要花更多时间在问卷的设计中,使得问卷最后变成下图这样:

图结构

无向图 / 有向图:

用 QQ 和微信举例,在 QQ 上,你和你朋友互相属于对方的好友,你们之间的关系是对等的,这种关系是一种无向图。

但是在微信上就不一样,你好友可能是你的好友,但你可能不是你好友的好友(开始套娃),这就是有向图。

为什么会出现有向图呢?因为在很多地方,你可以很方便的过去,但想 原路返回是不被允许的(比方单向通道、奈何桥等),如果哪天地图导航坏了,无法显示单行道,你带着老婆孩子出发去娘家,回来的时候发现到了一条单行道,想必你驻足在车道前沉思的样子,一定会很迷茫。

曾经的支付宝、微信提现是没有手续费的,你的钱从支付宝 - 银行卡 - 微信之间的流通是可以原路返回的,但是现在提现开始收手续费后,每次提现都扣你 0.1% 的手续费,你就会发现有向图有多可恶(咬牙)。

所以,要是不能准确表示这种原路返回的特征,就需要指明不同结点之间的不对等关系,所以就会出现有向图等结构。


数据结构的出现就是为了更好地管理、存储数据,每一种数据结构的设计都是用来解决某种程度上的问题。

在学习数据结构前,可以想象一下这种数据结构能应用在哪些场景,有什么优缺点。

想要系统地学习数据结构,可以先学习 C 语言,然后动手学习数据结构的 C 语言实现,能够让你更清晰、迅速地掌握数据结构! @知识库

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有