链接:https://www.zhihu.com/question/21318658/answer/63652147
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最近想了一下,没有讲详细一点的学习方法。从第26,作第一次更新。
学习方法:我认为任何数据结构都可以从线性表演进而来。以顺序表为例,最简单的顺序表是无序的,那么增加一个要求,使其是有序的,那么只需要改动一下插入操作。
依理类推,堆栈和队列,只需要改动插入和删除操作,即可。你看科研论文或实际项目,也是有一个较简单的数据结构,演变而来。
串:在线性表的基础上,增加了子串的操作,变动大一些,有回溯过程。
数组,结构稍有变动,操作也是增加得多一些,例如:回文。
树:结构改得较大,有分支了。性质也多一些。最基本的操作方法,是查询。
图:当然最复杂。各种需求。
因此:
线性表---->树---->图。这是一种演进路径。
线性表--->堆栈、队列、串--->数组--->各种线性表;
二叉树--->各种二叉树---->B树、B+树---->红黑树;
.....
混合使用。
依理类推。线性表是最简单的,从线性表可以逐渐演进出其它的数据结构。
每个大类结构,都可以从基本的结构逐渐增加要求,从而演进出其它的结构来。
--------------------------------------------------------------------------------------------------------------------
不邀自来。
这个问题提得很好,好就好在“本质”两个字。
数据结构的本质就在于:如何将现实世界中各种各样的数据放入到内存中,并且如何在内存中操作这些数据,如何评价这些存储方案和操作方法。
数据结构难学吗?是难学。
为什么难学?一开始上来就讲空间复杂度、时间复杂度,就讲抽象数据,当然难学了。
1、生活、生产等现实世界的数据有各种各样的组成形式。例如一个课程的所有学生的成绩(一组数据),一个班全部学生的所有课程的成绩(一张表)、一个单位的人员结构(树)等等。
2、这些数据都要先加载到内存中,再送到CPU中进行计算。
3、内存的最基本单位叫做存储单元,一个字节(不讨论理论中的、个别情况的)。存储单元相当于一个空盒子,可以放置数据。为了便于管理,盒子会给一个编号,当然存储单元也会有编号,其实就是地址。理论上地址的方案可以有多种(计算机组成原理和操作系统的任务),不过对于程序员来说,这些都跟我们无关,为了简单起见,我们把存储单元的编号(地址)都编成0、1、2、3、4,......这样的,于是这些编号或地址的取值范围,我们就称地址空间。这个地址空间,跟一维坐标轴一样,所以是一维线性空间。
4、很明显,数据就是一个个放入到这些存储单元中,就象我们把一个单位的物品放入盒子一样。现在,假设一个盒子只能装入一个单位的物品。因而,一个存储单元也只能放入一个单位的数据。
5、接下来,假设说,我们有很多很多的空盒子(X个)。有一天,我们要将若干单位物品(N个)放入盒子中,那么我们可以在一个盒子放入一个单位物品。依此类推,我们可以在一个存储单元中放置一个单位的数据。
6、再接下来,我们有两种放置方案:一个挨一个地连续地放置物品;当然,也可以不连续地放置物品。依此类推,在内存当中放置数据,也有两种方案,连续地放置数据,或者不连续地放置数据。为什么会有不连续的放置方案呢?原因很简单,一个主要的原因是,内存的空间利用率高,碎片少(操作系统的存储管理的知识,且不用理会),删除旧有的数据很容易(这个是数据结构的内容)。
7、现在,可以把这两个将数据放入到存储单元的方案叫做物理存储。对连续物理存储方案来说,事情比较好办,通过编号(索引、下标)就可以找到物品,对于不连续的方案,那么我们就要在一个物品上面标记下一个物品的位置,这个标记就是下一个物品的地址(指针)。当然,在计算机中,指针的记录本身也要占用内存的存储单元,所以我们在c语言中用结构体把数据和指针组织成为一个单位。通过这个指向关系,我们可以在不连续的放置方案中依次地查找我们所需要的东西(物品或数据)。
8、接下来,就象我们经常进行从盒子当中查询物品、取用物品或增加物品等操作一样,我们也要进行从内存当中查询数据、取用(删除)数据或增加数据等操作。那么,对于不同的物理存储方案来说,其方法是不一样的。这个想一想,我们如何对付真实的物品,我们就如何对付内存中的数据。这就是数据的物理存储方案的数据操作。
9、好了,搞懂这些,字符串之类的知识点就不难了。
10、记住一点,只有两种物理存储结构:连续的和不连续的,因为内存的存储单元的地址(编号)是0、1、2、3......(一维地址空间、或者线性地址空间)。
11、是不是只有物理存储结构(方案)就可以了呢?在第1条中说过,现实当中的数据是有各种各样的结构的。而在第10条,我们强调了物理放置方案只有2种:连续的和不连续的。
12、于是就产生一个问题,如何将现实世界当中的关系各种各样的数据放入到内存之中。
13、解决第12条中的问题,我们可以分两步走,第1步是将现实世界的数据组织成为逻辑结构,第2步再把逻辑结构的数据映射到物理结构中。
14、显然,在第1步中,我们抛去数据的其它属性,只留下数据的两个属性就可以了:一个属性是数据的值,另一个属性就是数据之间的关系。这两个属性就得到一个逻辑结构:graph(图),这就是离散数学中的图论。那么,这就是科学家的事情,他们负责针对具体的问题,将现实世界的数据构造出对应的graph(图)。
15、在第2步中,我们要做的事情,把这个graph映射到物理存储结构中,这就是数据结构要做的事情了。显然,我们可以用数组来存储,也可以用链表来存储,回忆一下最短路径算法的两个做法。ps.,二维数组、三维数组也是一个连续存储的结构,在c语言debug下,看看地址就知道了。那么,不连续的存储结构,也就是链表,当然有很多的衍生:双向链表、十字链表、等等。
16、显然,不管现实世界中的数据之间的关系如何,我们都可以用graph来描述,只不过是,不同的数据关系有不同的结构而已,比如:树、森林、mesh,等等。
17、当然,我们要掌握一些常见的graph的操作方法,最主要就是搜索方法。而且还要注意,这些方法是分两个层次的,一个物理存储结构这个层次,一个是逻辑存储结构这个层次的。那么现在,深度优先搜索、广度优先搜索是哪个层次呢?
18、当然,我们还要掌握一下存储结构的压缩。
19、到了这个时候,我们还要问一下,各种方案的优劣性质如何,也就是空间复杂度和时间复杂度了。
20、当然,我们这个时候,还要进一步的问一问,能不能将这些逻辑结构给出一个统一的描述,那么,就是抽象数据了。
21、当然,我们还要掌握逻辑存储结构的各种树的优化,特别是针对不同的应用,比如红黑树、B树。
22、当然,我们最后还要学习一下外存的存储结构。
23、当然,实验是少不了的。自己debug一下内存单元的地址,并且在纸上手工的画一下是最好了。
24、最后,有了这些基础,剩下也就好办了。
25、不推荐教材。尤其是国外的教材,先容许我默默地吐一下槽,各种知识点零碎不堪,不成体系,不成系统。
26、之前算是一些铺垫。讲一下数据结构的学习方法。在现实世界中,数据元素之间的关系(逻辑结构)可分为三大类:线性结构、树结构、图结构(有的书多了一种结构——集合,即任何数据元素之间都没有联系)。线性结构是最简单的结构。
27、把握一种数据结构,总的来说,体现在它的结构、内在性质、外在特征、操作方法等4个方面。这4个方面是相关的。
28、线性结构,又称线性表。其特点是:除了第一个元素和最后一个元素之外,其它一个元素都有一个前驱和后继。线性结构的结构简单、性质也较少、特征也很明显。最基本的操作方法有5种方法:初始化、获得当前线性表的长度、插入一个元素、删除一个元素、查询/获得一个元素。
29、线性表有多种类型。最简单的线性表,是无序的。在无序的线性表的基础上,增加一个要求,即线性表中的元素是有序的。这样,就要求插入元素时,要对元素的值进行比较,以找到相应的插入的位置。
30、同理,可以为线性表增加其它方法,例如,逆序的操作。
31、进一步延伸,可以得到许多线性表。最经典的线性表,除了顺序表、单链表、循环链表、双向链表之外,还有3个最常用的线性表——堆栈、队列、串。这3个线性表,并不难得出。
32、堆栈,是在线性表上增加了一个要求——先进后出;队列,也是在线性表上增加一个要求——先进先出。因此,只需要改动一下插入和删除操作,这个改动很容易。
33、串,是一种要求非常多的线性表,所以操作也非常多。有一些操作需要采用回溯算法。
34、堆栈、队列、串,用途广泛。在具体的运用中,有很多变种。比如,队列,在操作系统的进程管理的优先级算法中,采用了多级队列,在进程状态切换的算法中,采用了多个队列。但是,并不可怕,只要学会如何分析需求,从而改动操作方法,就可以实现。基本上,其它的操作方法都是在常规的5个方法的基础上演变而来。
原文地址:
https://www.zhihu.com/question/21318658