前言
学习数据结构也有一段时间了,今天就想总结一下我们平常常用到的一些数据结构,包括表、栈、队列、树、散列、堆、图等。关于算法,本篇博客介绍的很少,尽量的以通俗易懂的方式来让大家理解数据结构吧,因为我本人在最初学习代码的时候是很受其困扰的,相信和我一样的人不在少数吧。关于算法的博客,后续应该会在专栏里陆续更新吧,谁知道呢。
参考书籍:《算法图解》
《数据结构与算法分析》
《大话数据结构》程杰
1.表、栈和队列
什么是表?
可以很简单地理解成相同数据类型的n个数据元素的有限序列。比如:小朋友有序排队出教室(线性表)
又或者,家长散乱地在外面等(散列表)
他们个体并不相同,但又存在一定联系,可以使用该联系将他们串联起来,这就是表。我们初学编程时学到的数组,便是一种表,有头有尾,各自排序。当然,表也包括了很多类型,例如:线性表(链表),散列表,队列、栈等,他们都是表的一种特殊情况,而他们各自又可以根据情况不同,而使用不同的类型,比如说链表在不同的情况下可使用链表、双链表,循环链表
因为链表与数组类似,所以我们不做详细的介绍,这里我们的重点是介绍一下栈和队列。
什么是栈?
栈的由来应该可以涉及到寄存器的内容,这里不做详述,感兴趣的朋友可以自己去搜一下。
栈(stack)是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈的顶(top)。对栈的基本操作有Push(进栈)和Pop(出栈),前者相当于插入,后者则是删除最后插入的元素。最后插入的元素可以通过使用Top例程在执行Pop之前进行考查。对空栈进行的Pop或Top一般被认为是栈ADT的错误。另一方面,当运行Push时空间用尽是一个实现错误。我知道这段话可能有点难以理解,那我们用图来说明吧。

你们在用电脑时有没有经历过,机器有时会处于疑似死机的状态,鼠标点什么似乎都没用,双击任何快捷方式都不动弹。就当你失去耐心打算reset时,突然它像酒醒了一样,把你刚才单击的所有操作全部都按顺序执行了一遍,这是因为操作系统在当时可能CPU一时忙不过来,等前面的事忙完后,后面多个指令需要通过一个通道输出,按先后次序排队执行造成的结果。队列类似于栈,你不能随机地访问队列中的元素。队列只支持两种操作:入队和出队。下面是一个示意图:
关于栈和队列的区别,我觉得可以用这张图表示,很容易理解:
同样地,队列也可以使用数组的方式来实现,这里不做具体实现方式的介绍了。队列除了线性队列以外,还有循环队列等方式。
2.树
什么是树?

树的一种实现
对于树,我们介绍一个常见的形式,二叉查找树。二叉查找树类似于下面这样。
3.散列
散列是什么?
关于散列,与我们所理解的映射、或者函数十分相似。就像是在数组上做了一个映射,散列函数是这样的函数,即无论你给它什么数据,它都还你一个数字。
在这里,数值[0]所对应的便是milk,数值[3]所对应的便是apple,当我们查找apple时,它便会立马定位到数值[3],而不是通过遍历的方式从数值[0]一直往后遍历,因此它的时间复杂度仅为O(1)。当然,这里只做简单地介绍,散列表的实际应用情况可能比上面的要复杂,并且有时可能发生冲突,那就由到时候再深入学习吧。
4.堆(优先队列)
这里有一些相关知识,讲的很好【【从堆的定义到优先队列、堆排序】 10分钟看懂必考的数据结构——堆】https://www.bilibili.com/video/BV1AF411G7cA?vd_source=14baf41e0673d9a1ce16edd12323c3b6
堆是什么?
程序执行的优先级并不总是相同,重要的程序总该在计算机里优先运行,因此,便衍生出堆这一概念。
优先队列是允许至少下列两种操作的数据结构:Insert(插入),它的工作是显而易见的,以及DeleteMin(删除最小者),它的工作是找出、返回和删除优先队列中最小的元素。Insert操作等价于Enqueue(入队),而DeleteMin则是队列中Dequeue(出队)在优先队列中的等价操作。DeleteMin函数也变更它的输入。堆可以分为大根堆和小根堆。
堆的实现方式
有几种明显的方法实现优先队列。我们可以使用一个简单链表在表头以O(1)执行插入操作,并遍历该链表以删除最小元,这又需要O(N)时间。另一种方法是,始终让表保持排序状态;这使得插入代价高昂(O(N))而DeleteMin花费低廉(O(1))。基于DeleteMin的操作次数从不多于删除操作次数的事实,因此前者恐怕是更好的想法。
再一种实现优先队列的方法是使用二叉查找树,它对这两种操作的平均运行时间都是O(log N)。尽管插入是随机的,而删除则不是,但这个结论还是成立的。记住我们删除的惟一元素是最小元。反复除去左子树中的节点似乎损害树的平衡,使得右子树加重。然而,右子树是随机的。在最坏的情形,即DeleteMin将左子树删空的情形下,右子树拥有的元素最多也就是它应具有的两倍。这只是在其期望的深度上加了一个小常数。注意,通过使用平衡树,可以把界变成最坏情形的界,这将防止出现坏的插入序列。
注意:堆必须是完全二叉树。
堆的一种实现方式——二叉堆
我们将要使用的这种工具叫做二叉堆(binary heap),它的使用对于优先队列的实现是如此的普遍,以至于当堆(heap)这个词不加修饰地使用时一般都是指该数据结构的这种实现。在本小节,我们把二叉堆只叫做堆。同二叉查找树一样,堆也有两个性质,即结构性和堆序性。
建堆的方法
①自顶向下法:将新元素放到堆的最后一位,然后对他进行上浮操作。
②自下而上法:先把数组变成一个堆,再对父节点进行下沉操作。
5.图
图是什么?