抽象数据类型、数据结构、编程语言的实现?别再傻傻分不清了

320 阅读8分钟

前言

笔者在多年学习数据结构和算法的过程中,偶然发现国内不少高校在计算机基础课中会开两门与数据结构和算法相关的课程。其中数据结构单独开一门课,主要是讲解基本的数据结构和算法,算法(分析)另外开一门课,主要讲解算法的分析和正确性的证明。然而即便如此,有一部分同学和老师还是会混淆数据结构和算法,甚至把二者等同。更为重要的是国内无论是高校还是出版的教材都不会严格区分 抽象数据类型 (ADT) 与 数据结构 (DS),更不会阐明二者之间的关系。这也是我最近在学习 MIT 6.006 是门课后发现的。

作为验证,读者不妨思考5秒钟:

请问以下哪些是数据结构,哪些是 ADT呢?

  • 链表
  • 队列
  • 数组
  • 二叉树
  • 哈希表

时间到!!!

公布答案:链表、数组、哈希表是 数据结构,其余都是 ADT。

定义

  1. 抽象数据类型(Abstract Data Type):

是一种抽象的数学模型。它定义了数据类型的操作接口(逻辑结构)和数据之间的关系。

  1. 数据结构(Data Structure)

是一种物理结构,用来表示数据在计算机内存中的存储和组织方式

  1. 编程语言中的实现

某一门编程语言(Java中的 ArrayList)实现了某种数据结构(数组)。

为何要区分?

可能有朋友要问了,这么多年了我都没区分过ADT和数据结构,对我的工作也没有影响啊,学会了又有什么用呢!确实,如果我只是甘愿做一个码农,确实不需要去区分二者。毕竟每天的工作就是CRUD, 过去通过在搜索引擎中复制粘贴完成任务,现在转向从AI对话中复制粘贴。如果你想要成为一个真正的软件工程师,想要做到至少架构师的层次。那么,请务必耐心读完。

区分ADT与数据结构更实际的好处如下:

  • 掌握模块化编程的思想,提高代码复用性。
  • 提供顶层设计的蓝图。
  • 为跨语言编程打好基石。

如何去区分?

很多人基础知识不扎实的原因往往在于学习初期没有花时间去理解概念,从不去推理,全凭机械式地记忆和题海战术达成目标。这样在起始阶段确实可以比其他人走得更快,可是一旦放到更长的时间去看,靠记忆和刷题是很难比真正花时间理解概念和思考的人走得更远。

紧扣概念,我们发现:

  • ADT 定义的是操作接口和数据之间的关系。即 ADT 回答的是 What 的问题。它告诉你我这个 ADT 是做什么的。至于你用什么做,怎么做,它通通不关心。因此 ADT 的抽象层次也是最高的。

  • 数据结构表示的是存储结构。换句话说,数据结构回答的是 How 的问题。一个列表(list)是用连续的内存空间(数组)来实现,还是用一块块破碎的内存空间(链表)实现呢?这是数据结构关心的问题。数据结构实际上是实现了ADT,反过来某一个ADT可以由多种数据结构来实现。

  • 编程语言的实现就比较好理解了。比如几乎所有的编程语言都内置实现了“数组”这种数据结构。值得注意的是,Python中虽然用的是 list 来表示数组这种数据结构,但它并未直接实现列表(list)这个 ADT。这个 list 本质上是一个动态数组(类似于C++中的 Vector,Java中的 ArrayList)。所以我们才说Python的 list 实现的是数组这种数据结构(更准确地说是动态数组)。

总结一下

  • ADT的抽象层次最高,数据结构次之,抽象层次最低的是对应编程语言中的实现。

换句话说,是数据结构实现了ADT,编程语言又去实现了数据结构。需要注意的是,许多编程语言本质上实现的还是某种数据结构而不是直接实现 ADT。虽然它借用了这个 ADT 的名字,就比如我们刚刚说过的 Python 语言内置的 list类型。

类比&举例

如果用生活中的例子来做类比。ADT 就像是你公司里的大老板,TA只负责企业战略的制定,告诉大家公司要做什么(What),并不负责具体的规划和执行。比如说要开发一个「任务管理系统」,老板说了“该系统要支持任务的增删改查,还要记录操作日志。具体你的数据库是使用 MySQL 还是 MongoDB,日志是存在本地还是云端,老板并不关心。而数据结构就像是部门经理,TA需要负责贯彻落实老板的战略。我们还以开发这个「任务管理系统」为例,到部门领导这里就需要敲定具体的技术细节了,包括使用哪一种数据库,把日志保存在哪一块等等。当然,部门领导毕竟还是领导,他们并不会去干实际的活,在确定了具体的技术路线后还需要有人去写代码。与之对应的,编程语言中内置类型对数据结构的实现就像一线程序员接下领导布置的开发任务,开始没日没夜地编码,系统的细节和边界条件,以及性能的优化都要由程序员来完成。

如果再用大家比较熟悉的OOP做类比,ADT 就像是编程语言中的 interface 或者说是泛型。而抽象类实现 interface 的过程其实就是数据结构实现 ADT 的过程。最后还需要有一个具体的类去实现这个抽象类,它对应的其实就是编程语言的内置类型实现数据结构的工作。

Talk is cheap, show you the code.

// 用Java更好地展现这种关系

// 1. ADT(接口) - 定义系统功能
interface TaskManager {
    void addTask(String taskId);
    void addDependency(String taskId, String dependentTaskId);
    boolean canComplete(String taskId); 
}

// 2. 数据结构(抽象类) - 选择数据结构,提供通用方法
abstract class AbstractTaskManager implements TaskManager {
    protected Map<String, Set<String>> dependencies = new HashMap<>(); 
}

// 3. 具体实现 1:基于列表的实现 - 简化版,不考虑循环依赖
class ListBasedManager extends AbstractTaskManager {
    @Override
    public void addTask(String taskId) {
        dependencies.putIfAbsent(taskId, new HashSet<>()); 
    }

    @Override
    public void addDependency(String taskId, String dependentTaskId) {
        dependencies.get(taskId).add(dependentTaskId); 
    }

    @Override
    public boolean canComplete(String taskId) {
        return dependencies.get(taskId).isEmpty(); 
    }
}

// 3. 具体实现 2:基于图的实现 - 简化版,循环依赖检查省略
class GraphBasedManager extends AbstractTaskManager {
    @Override
    public void addTask(String taskId) {
        dependencies.putIfAbsent(taskId, new HashSet<>());
    }

    @Override
    public void addDependency(String taskId, String dependentTaskId) {
        dependencies.get(taskId).add(dependentTaskId);
        //  此处省略循环依赖检查...
    }

    @Override
    public boolean canComplete(String taskId) {
        //  此处省略循环依赖检查,假设已完成...
        return true; // 简化版,直接返回true
    }
}

回到开头

为什么说链表、数组、哈希表是数据结构呢?因为这三者确定了数据在内存中的具体存储方式。链表是不连续的存储空间,数组是连续的存储空间,哈希表是使用数组来实现(具体的哈希表实现也包含开放寻址法和拉链法,前者用到了数组,后者另外还用到了链表)。具体来看数组和链表实现的是 list 这种ADT。哈希表实现的是 map(映射)这种ADT,map 的另外一种实现方式是用红黑树实现的 TreeMap。

为什么说栈、队列、二叉树是 ADT而不是数据结构呢呢?因为它们只定义了逻辑结构,没有确定具体的的物理结构。比如栈最重要的是LIFO(后进先出),队列的核心是的FIFO(先进先出),它们并不关心你是使用数组还是链表来实现栈还是队列。同理二叉树也是ADT,可以使用数组顺序存储(一般不这么做,在特定场景下用的多,比如满二叉树),也可以使用链表链式存储。

Futhermore:ADT可以进一步抽象,比如BST是一种二叉树,二叉树又是一种普通的树,但其实他们都还在ADT的范畴内。在数据结构中也是一样的,比如我们刚刚提到的哈希表也有不同的实现方式,甚至于数组、链表这种数据结构还是实现其他数据结构的基石。当然像单链表、双链表、循环链表等等都还是属于链表,是链表自身的划分。


首发:2025年2月5日