尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

数据结构第一章精讲:逻辑结构、存储结构与时间复杂度全解析

数据结构第一章精讲:逻辑结构、存储结构与时间复杂度全解析 严蔚敏这本《数据结构C语言版 第2版》第一章看着是整本书最“软”的一章全是概念、术语、数学式子很多同学一周翻过去就急着去啃线性表。但以我带过不少考研和期末备考学生的经验看第一章恰恰是最容易“埋雷”的地方。408统考和多数自主命题院校都爱在绪论部分抠概念细节判断题里换一个词答案就反过来了。更关键的是后面第二章到第七章的所有代码全部建立在这一章那套“逻辑结构、存储结构、算法分析”的框架之上——你在我这篇博文里花半小时把这章吃透后面写代码题会顺很多。这篇内容我打算这么安排先把第一章的知识框架划出重点再对课后习题按类型逐类精讲接着用C语言把抽象概念落到具体代码里最后整理一份易错点排查清单和复习路线参考。不管你是期末突击、考研一轮还是自学入门都可以直接对号入座。1. 第一章到底在讲什么不要低估这门“地基课”1.1 全章节的知识框架第一章在整本书里扮演的角色相当于盖楼之前的“设计图纸”。它不直接教你写任何复杂的数据结构而是回答几个根本问题计算机里处理的数据长什么样数据之间有哪些组织方式这些组织方式在计算机里怎么落地怎么评价一个数据结构配算法的方案好不好把这些点拆开第一章的知识块一共四块数据相关术语数据、数据元素、数据项、数据对象、数据结构。这一组概念纯粹是抠名词但考试特别喜欢混着考。逻辑结构的四种基本形态集合、线性结构、树形结构、图状结构网状结构。这是从“数据元素之间的关系”角度去分类的。存储结构的四种实现方式顺序存储、链式存储、索引存储、散列存储。这是从“计算机内存里的实际摆放方式”角度去分类的。算法与算法分析算法的五个特性、算法设计的要求、时间复杂度、空间复杂度、渐进复杂度大O记号。很多同学复习到这里觉得内容太“虚”不如后面的栈、队列来得实在。但实际上第一章的每一句话都在给后面章节做铺垫。比如逻辑结构与存储结构的关系没搞清楚学到二叉树的时候就会混淆“完全二叉树是逻辑结构还是存储结构”这一类送命题。1.2 必须吃透的三组核心概念第一组是数据结构的三要素。课本上的定义是数据结构包括数据的逻辑结构、数据的存储结构和数据的运算集合。这个“三要素”说法一定要刻在脑子里因为它是全书分析任何一个数据结构的固定套路。学线性表、学树、学图都是先看逻辑上是什么关系再讨论怎么存储最后讨论有哪些基本操作。第二组是逻辑结构与存储结构的区别和联系。逻辑结构描述的是数据元素之间的抽象关系跟计算机无关存储结构则是逻辑结构在计算机里的物理实现。打个比方逻辑结构像一张“通讯录关系图”写着谁是张三的朋友、谁是谁的上级存储结构则是你实际把这些人名记在纸上还是存在手机里记的时候是按字母排序还是按加入时间排序。第三组是抽象数据类型ADT的思想。ADT的核心是“封装”即把数据对象及其操作定义成一个整体使用者只关心操作“是什么”不需要关心“怎么实现”。这一点在C语言里最直观的体现就是头文件加源文件的组织方式——头文件里放接口声明源文件里放实现外部调用者只include头文件。这个习惯如果从第一章就开始养成后面写栈、队列的实验报告会特别顺畅。2. 课后习题逐类精解从概念题到算法题一网打尽2.1 术语解释题答题的标准“话术”第一章课后题里最常见的一类就是让你解释“数据”“数据元素”“数据项”“数据对象”“数据结构”这些名词。这类题看着简单但很多同学丢分就丢在表述不严谨、层次不清楚。我在复习时总结了一套答题模板可以套在几乎所有术语题上先给定义用教材原话或自己组织的准确表述。再举一个具体例子。最后补充一句与相邻概念的关系体现你理解层次清晰。比如解释“数据元素”可以这么答数据元素是数据的基本单位在程序中作为一个整体进行考虑和处理。例如在成绩管理系统中一名学生的记录就是一个数据元素它还可以由若干个数据项组成比如学号、姓名、成绩就是数据项。数据项是构成数据元素的不可分割的最小单位。解释“数据结构”时要特别注意必须涵盖三要素数据结构是相互之间存在一种或多种特定关系的数据元素的集合包括逻辑结构、存储结构和数据的运算三个方面。如果只答“带结构的数据元素的集合”能拿一半分但不够完整。2.2 判断题与选择题出题人最爱埋的坑第一章的判断题、选择题套路非常固定翻来覆去就那几个陷阱但每年都有一大批人踩。我把常见考点整理成了几个判断题你先自己判断一下再对比后面的解析。判断一数据元素是数据的最小单位。这句话是错的。数据的最小单位是数据项数据元素是数据的基本单位。这个“最小”和“基本”的区别就是考点。可以类比一下一条员工记录数据元素由姓名、工号等字段数据项组成字段才是不可以再分的。判断二数据的逻辑结构与存储结构是一一对应的。这句话也是错的。一种逻辑结构可以用多种存储结构来实现。比如线性表既可以用顺序存储数组也可以用链式存储链表。反过来同一种存储结构也可以承载不同的逻辑结构。逻辑结构与存储结构是“一对多”的关系不是“一一对应”。判断三算法的每一步操作必须有确切的定义不能有二义性。这是对的对应算法的“确定性”特性。这里顺便把算法的五个特性一起记住有穷性、确定性、可行性、输入、输出。注意“有穷性”说的是一个算法必须在执行有穷步之后结束且每一步都在有穷时间内完成——因此死循环的代码不能叫算法。而“程序”不一定满足有穷性比如操作系统在没有外部事件时会一直循环等待这属于程序不算算法。判断四算法可以用各种语言描述因此算法最终可以由计算机直接执行。前半句对后半句错。用C语言、伪代码甚至自然语言写的都是“算法描述”要变成计算机能执行的指令还得经过编译、链接等步骤。算法与计算机语言无关它的核心是求解步骤的逻辑但算法实现需要具体的语言和运行环境。判断五时间复杂度为O(n)的算法一定比时间复杂度为O(n²)的算法快。这句话错得离谱但特别常考。大O记号描述的是增长率不是绝对运行时间。一个O(n)的算法在n很小时可能因为常数因子过大而比O(n²)的算法更慢。比如100n和3n²在n10的时候前者要1000次操作后者只要300次。只有在n足够大时渐近复杂度低的算法的优势才能体现出来。考试时遇到类似说法只要出现“一定”“必然”“总是”大概率是错的。2.3 算法设计题时间复杂度分析的规范写法第一章末尾的算法设计题通常有两类一类是要求写出某个简单问题的递归算法或非递归算法比如求数组最大值、求数组元素之和、逆置数组另一类是给出一段程序要求计算它的时间复杂度。先说第一类以“求数组元素的最大值”为例课后题的常见提法是写一个递归算法并分析其时间复杂度。参考实现如下int findMax(int arr[], int n) { if (n 1) { return arr[0]; // 递归出口只有一个元素时它就是最大值 } int subMax findMax(arr, n - 1); // 先求前n-1个元素的最大值 return (subMax arr[n - 1]) ? subMax : arr[n - 1]; }这段代码的时间复杂度分析规模为n的问题递归调用规模为n-1的子问题递归出口是n1所以一共递归n次每次递归只做一次比较操作时间复杂度为O(n)。需要注意的是如果题目要求“不破坏原数组顺序”这个递归版本是可以接受的如果要求“高效”可以考虑分治法折半求最大值再把两个最大值比较时间复杂度同样为O(n)但递归深度是log₂n量级栈空间更小。再说第二类典型程序段的时间复杂度分析。我给你列几个高频样例最好自己先算再看答案样例一int i 1; while (i n) { i i * 2; }设执行次数为t则循环结束时满足2的t次方大于n所以t log₂n 1时间复杂度为O(log₂n)。这类题的核心是“看循环变量的变化规律”i每次都翻倍就是对数级。样例二int sum 0; for (int i 1; i n; i) { for (int j 1; j n; j) { sum i * j; } }外层循环n次内层循环n次总执行次数为n²时间复杂度为O(n²)。这里要注意区分“两层循环”不一定就是O(n²)——如果内层循环的上界是外层循环变量比如j i那总执行次数是12...n n(n1)/2时间复杂度仍然是O(n²)但常数项不同考试如果问“渐进时间复杂度”还是回答O(n²)。样例三for (int i 1; i n; i) { for (int j 1; j i; j) { // 基本操作 } }内层循环次数随i变化总执行次数为n(n1)/2仅看最高阶项且忽略系数所以时间复杂度是O(n²)。这类“i到n、j到i”的结构几乎全是O(n²)可以当成经验直接记。样例四较难int count 0; for (int i 1; i n; i * 2) { for (int j 1; j n; j) { count; } }外层i每次翻倍执行log₂n次内层固定执行n次相乘得到log₂n乘以n即时间复杂度为O(nlog₂n)。这个复杂度在后面的排序算法里归并排序、堆排序会频繁出现现在提前混个脸熟。分析时间复杂度的整体思路是先找基本操作通常是循环体内最深层的那句再计算它的执行次数关于n的表达式最后用大O记号化简只保留最高阶项且忽略系数。如果递归则要写出递归方程比如T(n) T(n-1) O(1)的解是O(n)T(n) 2T(n/2) O(n)的解是O(nlog₂n)这两个结论后面会反复用。3. 用C语言落地第一章把抽象概念写进代码3.1 用结构体实现一个简单ADT第一章讲ADT的时候很多同学觉得抽象其实用C语言实现一遍就全通了。以教材里常见的“复数”为例ADT定义包括数据对象实部和虚部以及操作构造复数、求实部、求虚部、复数的加法和乘法等。用C语言写第一步是定义结构体作为数据对象第二步是把操作写成函数并把这些函数声明集中放在头文件里。头文件的写法大致如下// complex.h #ifndef COMPLEX_H #define COMPLEX_H typedef struct { double real; // 实部 double imag; // 虚部 } Complex; Complex createComplex(double r, double i); // 构建一个复数 double getReal(Complex c); // 返回实部 double getImag(Complex c); // 返回虚部 Complex addComplex(Complex a, Complex b); // 返回两复数之和 #endif对应的实现文件里再写函数体。这里最值得体会的是“封装”两个字使用者在主函数里只需要include这个头文件、调用createComplex和addComplex根本不需要关心Complex结构体内部用了double还是float也不需要关心加法函数内部怎么算。这跟第一章ADT的“数据对象数据操作”的定义方式是完全对应的。我在做这个练习时踩过一个坑结构体里的两个字段如果定义成float做复数乘法时中间结果的精度会丢失。后来统一改成double才通过测试。这种细节课本不会写但实际写代码时很容易暴露。3.2 逻辑结构与存储结构在代码中的差别还是用“线性表”举例。线性表是一种逻辑结构元素之间是一对一的线性关系有且仅有一个前驱和后继首元素和尾元素除外。这种逻辑关系既可以顺序存储数组也可以链式存储指针。顺序存储的代码核心是定义数组加长度变量#define MAXSIZE 100 typedef struct { int data[MAXSIZE]; int length; } SqList;链式存储的核心是定义结点和指针typedef struct LNode { int data; struct LNode *next; } LNode;同一个“线性表”逻辑概念在C语言里对应两种完全不同的实现。这就是为什么第一章反复强调“逻辑结构是抽象的存储结构是具体的”——如果你在代码里看到的是一片连续内存说明是顺序存储如果看到的是结点之间靠next指针串联说明是链式存储。给个自检问题一棵二叉树用数组存放比如完全二叉树按层序存入数组那它的存储结构是什么答案是顺序存储而不是树形存储。逻辑结构是树形存储结构是顺序的两个维度不能混为一谈。这类题几乎每年都考。4. 易错点与考试踩坑实录4.1 最常见的四类翻车现场第一类把“数据项”和“数据元素”说反。前面已经强调过一次但这里还是要单独列出来——考试写错的两个词概念题基本分全丢。建议记忆锚点元素是“一条记录”项是“记录里的一个字段”。第二类混淆“逻辑结构”与“存储结构”的分类。比如问“栈是什么结构”很多同学会答“顺序结构”这就错了。栈是逻辑结构里的线性结构它强调的是后进先出的操作特性至于用数组还是链表实现那是存储结构的事。再比如“哈希表”的存储结构是散列存储但它的逻辑结构通常还是线性结构。第三类时间复杂度只数循环不数递归调用栈。有些同学分析递归算法时把“每次递归里的操作”算对了但忘记递归本身会调用n次导致少算一个n的量级。比如遍历单链表计算长度的递归版本每次递归做一次“当前结点是否为NULL”的判断递归n次时间复杂度和迭代版本一样都是O(n)不能因为“只有一个函数体”就以为它是O(1)。第四类空间复杂度只算变量数组不算递归栈。第一章的空间复杂度通常考两个点原地操作O(1)和递归栈O(n)。如果用递归求斐波那契数列第n项代码很简单但每次递归会压栈空间复杂度是O(n)。不少同学只看程序里没开数组就说空间复杂度O(1)这是典型失误。4.2 快速自查清单考前复习第一章建议用这份清单过一遍能快速发现漏洞能否不看书说出数据结构三要素能否默写逻辑结构的四种类型并各举一个例子能否区分顺序存储、链式存储、索引存储、散列存储的优缺点能否判断一个给定的程序段的时间复杂度包括单循环、双循环、对数型循环能否解释“算法的有穷性”与“程序的无限循环”之间的关系能否用结构体加函数的方式定义一个简单ADT并实现两个基本操作能否正确区分“数据的最小单位”和“数据的基本单位”这七条如果都能做到第一章就算真正过关了。我自己的经验是用这七条来自测比做十道题都高效因为它是按知识板块覆盖的不是零散地抽测。5. 复习路线与配套资料怎么配合用5.1 教材课后题与王道、天勤怎么搭配很多同学手里除了教材还有王道的辅导书复习第一章时经常陷入“不知道以哪个为准”的纠结。我的建议是以教材的习题为主因为考研自主命题院校的出题风格往往直接源自教材王道的选择题可以当补充和自测用来扩展题型覆盖范围。具体操作可以这样第一遍先看教材正文然后合上书自己说一遍三要素、逻辑结构分类、存储结构分类、算法五个特性这一步比抄定义有用十倍。第二遍做教材课后题概念题尽量口述作答判断题要能说出“为什么对、为什么错”算法题亲自动手写代码别看一眼答案觉得会了就直接跳过。第三遍再用王道等资料里的选择题进行训练目标是每道题都能给别人讲清楚。5.2 给考研党的时间规划建议如果你的目标是考研第一章建议控制在3天以内最多不超过4天。第一天通读教材正文边读边划出概念关键词把课后的术语题全部口述一遍。第二天集中做判断题、选择题把错题对应的教材段落重新读一遍并整理到错题本。第三天把算法设计题自己写一遍代码分析时间复杂度和空间复杂度然后对比参考答案重点看精妙之处。第四天回顾错题本再对照上面那七条自查清单过一遍查漏补缺。这里要注意一个误区考研复习的时间非常宝贵但第一章不能直接跳过不能觉得“反正就是概念后面再说”。因为后面每个章节都要用这里的术语体系如果第一章没建立“逻辑结构-存储结构-运算集合”的分析框架学到二叉树和查找时会频繁返工。我在实际复习中有个体会第一章投入的时间不会白费它决定了你在学后面知识点时是不是“有框架地学”。很多人学到图的时候开始蒙回头一看就是因为没有用“逻辑结构是图状、存储结构可以用邻接矩阵或邻接表”这套框架去组织知识。把第一章吃透了后面自然顺。最后再分享一个小技巧把第一章的关键概念做成一页纸的思维导图不需要多漂亮但一定要把“逻辑结构-存储结构-数据运算”三个维度作为主干然后不断往上面挂细节。等你学完整本书这张图就是你考前最后一晚的复习神器。
返回列表