一、数据结构绪论:从概念到实战

各位同学好,我是你们这门课的老朋友。今天咱们聊聊数据结构这门课的“开场白”——绪论。别看它叫“绪论”,好像只是些概念,其实它决定了你后面所有代码的“骨架”该怎么搭。

我刚开始学数据结构时,也觉得这些概念太虚了。直到后来做项目,因为没搞清楚“逻辑结构”和“物理结构”的区别,写出来的代码跑起来慢得像蜗牛。嗯,从那以后,我再也不敢小看绪论了。

1.1 数据结构到底是什么?

说白了,数据结构就是“数据怎么组织、怎么存、怎么用”。你想想看,一堆数据散落一地,跟把它们整整齐齐放进抽屉里,效率能一样吗?

我个人习惯把数据结构理解成“数据的收纳术”。比如你写一个学生管理系统,学生信息怎么存?按学号排?按姓名排?还是按成绩排?不同的排法,后面查起来、改起来,速度天差地别。

核心定义:数据结构 = 数据元素 + 数据元素之间的关系 + 对这些关系的操作。

简单说就是:存什么、怎么存、能干啥。

1.2 逻辑结构与物理结构

这里有个坑,我当年踩过。逻辑结构和物理结构,是两个完全不同的概念,但很多人混为一谈。

逻辑结构:数据元素之间的“抽象关系”。比如你脑子里想的“这些学生按成绩从高到低排”,这就是逻辑结构。它不关心数据在内存里到底怎么放的。

物理结构:数据在计算机内存里“实际怎么存”。比如用数组连续存,还是用链表东一个西一个地存。

我曾经在一个项目中,设计了一个很漂亮的逻辑结构——树形组织架构。结果实现时选了数组存储,插入一个节点就要移动大量数据,性能惨不忍睹。后来改成链表存储,问题迎刃而解。

避坑指南:逻辑结构决定“能不能做”,物理结构决定“快不快”。两者必须一起考虑,缺一不可。

常见的逻辑结构有四种:

  • 集合:数据之间“没关系”,就是一堆东西放一起。
  • 线性结构:数据“一对一”排成一条线,比如数组、链表。
  • 树形结构:数据“一对多”,比如文件夹目录、公司组织架构。
  • 图形结构:数据“多对多”,比如社交网络、地图导航。

物理结构主要就两种:

  • 顺序存储:用连续的内存空间,像数组那样。
  • 链式存储:用不连续的内存,靠指针串起来,像链表那样。

1.3 抽象数据类型(ADT)

抽象数据类型,听起来很高大上。其实说白了就是:我只管你能干什么,不管你怎么干

比如你定义一个“栈”的ADT,你只需要说:我能push、pop、查看栈顶。至于底层是用数组实现还是链表实现,那是实现细节,ADT不关心。

我习惯在写代码前,先用ADT把“要做什么”定义清楚。这样后面写代码时,思路特别清晰,不会写着写着就跑偏了。

我的经验:ADT就像一份“合同”。你告诉调用者:“我提供这些功能,你只管用,别管我怎么实现的。”这样代码的耦合度会大大降低。

一个典型的ADT定义包含三部分:

  • 数据对象:你要操作的数据是什么。
  • 数据关系:这些数据之间有什么关系。
  • 基本操作:你能对这些数据做什么。
// 一个简单的栈ADT定义(C语言风格)
// 数据对象:一组元素,后进先出
// 基本操作:
//   initStack(): 初始化一个空栈
//   push(element): 将元素压入栈顶
//   pop(): 弹出栈顶元素
//   top(): 查看栈顶元素(不弹出)
//   isEmpty(): 判断栈是否为空

// 具体实现(用数组):
#define MAX_SIZE 100
typedef struct {
    int data[MAX_SIZE];
    int top;
} Stack;

void initStack(Stack *s) {
    s->top = -1;
}

void push(Stack *s, int value) {
    if (s->top >= MAX_SIZE - 1) {
        printf("栈已满,无法压入\n");
        return;
    }
    s->data[++(s->top)] = value;
}

int pop(Stack *s) {
    if (s->top == -1) {
        printf("栈已空,无法弹出\n");
        return -1;
    }
    return s->data[(s->top)--];
}

1.4 算法与算法分析

算法,就是解决问题的步骤。但同样是解决问题,有的算法快如闪电,有的慢如蜗牛。怎么衡量?这就引出了算法分析。

我刚开始写代码时,觉得“能跑就行”。直到有一次,我写了一个排序算法,数据量一上去,程序直接卡死。后来一分析,时间复杂度是O(n²),数据量大了当然扛不住。

时间复杂度:衡量算法运行时间随数据规模增长的趋势。常见的有:

符号 名称 例子
O(1) 常数时间 数组随机访问
O(log n) 对数时间 二分查找
O(n) 线性时间 遍历数组
O(n log n) 线性对数时间 快速排序
O(n²) 平方时间 冒泡排序

空间复杂度:衡量算法运行时占用的内存空间。有时候为了省时间,得多用点空间;为了省空间,得多花点时间。这就是经典的“时空权衡”。

记住:算法分析不是让你背公式,而是让你在写代码前,心里有个数——这个算法,数据量大了会不会崩。

知识体系总览

下面这张图,把本章的核心知识点串起来了。你可以把它当作整个数据结构的“地图”。

数据结构绪论 数据结构概念 数据元素 + 关系 + 操作 逻辑结构与物理结构 逻辑:集合/线性/树/图 物理:顺序/链式 抽象数据类型(ADT) 数据对象 + 数据关系 + 基本操作 算法与算法分析 时间复杂度 空间复杂度 核心:逻辑结构决定功能 物理结构决定性能

小结

这一章的内容,说白了就是给整个数据结构课程“打地基”。你可能会觉得概念有点多,但别急,后面每一章都会反复用到这些概念。

我个人建议你:先把逻辑结构和物理结构的区别刻在脑子里。写代码前,先想清楚“我要用什么逻辑结构”,再想“用什么物理结构实现”。这个习惯养成了,后面学链表、树、图都会轻松很多。

好了,这一章就到这里。记住:数据结构不是背出来的,是用出来的。下一章咱们就开始动手,从线性表开始,真正写代码了。


公众号:蓝海资料掘金营,微信deep3321