携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情
所谓“巧妇难为无米之炊”,再强大的计算机,也要有“米”下锅才可以干活,否则就是一堆破铜烂铁。这个“米”就是数据,数据是计算机的原始资料。
“米”又可以做成各式各样的美食,比如:米粉、米糕、米饼、米酒、粽子、寿司等等。同理,数据也可以组成各式各类的“数据结构”。噢,明白了,数据结构就是是数据的容器、载体。
数据结构内容很多,早在 1968 年就被作为一门独立的课程在大学中设立。我们引入数据结构的基本介绍,不求面面俱到,旦求要点尽有,可作初学构建印象、或温习梳理体系的用处。
常见数据结构
数据结构中有一些常见的类型,它们是:栈、队列、数组、链表、树、堆、图、散列表。
就这样一眼望过去,肯定是很难记住的,我们将它们作简要归类:
栈、队列、数组、链表都属于线性表一类,什么是线性表?线性表,全名为线性存储结构。使用线性表存储数据的方式可以这样理解,即“把所有数据用一根线儿串起来,再存储到物理空间中”。
树、堆都属于“树”这一类,堆是一种特殊的树结构,这个后面在讲。
图和散列表都是单独一类。
我们将上述关系可以画一张思维导图,在数据结构中的常见类型则一目了然。
栈和队列
我们首先讲一下线性表中“栈”和“队列”的特点,这是数据结构中的重点认知之一。
什么是栈?栈是一种“后进先出”的数据结构。什么是“后进先出”?举个通俗的例子,比如薯片桶,第一片薯片放在桶里最底部,然后再放第二片、第三片,直至放满。当我们要吃的时候,总是会拿最顶层的薯片,这说明越是后来放入薯片桶的薯片将会被优先拿出来。
由于栈的特性,栈这种数据结构主要是“入栈”和“出栈”两种操作。
什么是队列?队列是一种“先进先出”的数据结构。队列的概念相比栈更容易理解。在现实生活中我们经常排队,先排上队的,能优先出队,进一步处理相关事宜。
队列有“入队”和“出队”两种操作,很明显,入队是在队尾进行操作,出队是在队首进行操作,这就和栈结构中不一样了,对于栈来说,入栈和出栈都是在栈顶进行操作。
思考题:结合栈和队列的两种数据结构特性,如果想遍历拿到一组数据中的其中一个,哪种数据结构会更快?
数组和链表
数组几乎是编程中最重要的一种数据结构,它定义了一个有序的元素序列集合。
你可以把数组想象成一个连续的台阶,如果想要知道哪个台阶上放了什么的东西,只需要知道这个台阶的下标号,直接去这个标号的台阶上取东西就行了,不需要查其它台阶上的标号和东西,也不用一阶一阶的去找。所以数组的查询时间非常快。
链表则表示一组可变数量数据项的有续集,它的元素的存储位置并非是连续的,它的核心原理是通过指针指向来实现有序的。和数组相比,链表更适合插入、删除操作频繁的场景。
树和堆
树是一种抽象数据类型,用来模拟具有树状结构性质的数据集合。它大概是长成这样的:
从定义上来讲,它是由 n(n>0)个有限节点组成一个具有层次关系的集合。其实它看起来像是一颗倒挂着的树,根节点朝上,而叶子节点朝下。
具体来说,树又分为两大类,二叉树和多叉树。其中二叉树又有很多细分,比如完全二叉树、平衡二叉树、二叉查找树等,平衡二叉树又再细分为 AVL 树、红黑树等,这里不逐一展开讲解了。
着重讲一下平衡二叉树和完全二叉树,这两个树的定义是一定要知道的,很多面试中关于数据结构考点,都会问它们。
什么是平衡二叉树?
从定义上来讲是指:二叉树中任意一个节点的左右子树的高度相差不能大于1。
什么是完全二叉树?
完全二叉树的定义是:若二叉树的深度为h,除第 h 层外,其它各层的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
堆数据结构堆通常被看做一棵完全二叉树的数组对象。
树结构算是数据结构中比较复杂的一种结构了,需要长久持续的关注它,才能驾轻就熟、灵活运用。
图
图就是一些顶点的集合,这些顶点通过一系列边结对连接。顶点用圆圈表示,边就是这些圆圈之间的连线。
很多现实问题都可以用图数据结构来表示,比如著名的旅行家问题,一位旅行家要旅行n个城市,要求各个城市经历且仅经历一次,然后回到出发城市,并要求所走的路程最短。对于解答有兴趣的朋友可以参加我的这篇文章 《# 改变世界的图算法——Dijkstra》
在图这个数据结构中有 2 个最重要的算法:深度优先搜索(DFS)和广度优先搜索(BFS),是我们一定要花精力重点关注的,后面会再具体展开。
散列表
散列表也叫哈希表或者 Hash 表,是实现字典操作的一种有效数据结构,它其实是数组的一种扩展,由数组演化而来。
散列表记录的是确定的对应关系,每个关键字 key 都能通过确定的对应关系 f(key) 找到存储的值。
散列表的重要性不言而喻,它的现实应用场景遍地都是,比如根据电话号码查找姓名、根据映射IP查找网站、以及很多网站用到的缓存机制都是存储在散列表中。
需要特别提出的是,在散列表中有一个很出名的问题我们需要认识,那就是“散列冲突”,也叫“哈希冲突”或“哈希碰撞”。
什么是“哈希碰撞”?
简而言之,在上述散列表的映射过程中,f(key1) 和 f(key2) 的值是同一个值,则表示发生了“哈希碰撞”。原本的期望是输入和输出逐一对应,现在却有两个输入对应同样的一个输出,这导致无法识别来源。
解决哈希碰撞的有效方法是扩大取值空间,即避免经过哈希运算后得到同样的值。
OK,以上便是本篇分享。点赞关注评论,为好文助力