数据结构 08 线性结构
1 算法可以没有输入,但是必须有输出。
- 算法可以没输入:比如 “直接输出数字 5”,不用你给任何东西,它自己就能跑。
- 算法得有输出:不然跑完啥结果没有,解决不了问题,就不算有用的算法。
- 关键看这俩:一是能跑完(不无限循环),二是每步都明确(不模棱两可)。
2 在具有N个结点的单链表中,访问结点和增加结点的时间复杂度分别对应为O(N)和O(1)
1. 访问节点:时间复杂度 O (N)
单链表的存储结构是 “节点 + 指针”,每个节点仅存储自身数据和下一个节点的地址,不支持随机访问。
- 要访问第 k 个节点,必须从头节点开始,通过指针依次遍历前 k-1 个节点,才能找到目标节点。
- 最坏情况下(访问尾节点),需要遍历全部 N 个节点,因此时间复杂度为 O (N)。
2. 增加节点:时间复杂度分情况讨论
增加节点的操作效率,核心取决于是否已获取 “目标位置的前驱节点指针”。
| 增加位置 | 时间复杂度 | 核心原因 |
|---|---|---|
| 表头插入 | O(1) | 无需遍历,直接将新节点的指针指向原头节点,再更新头节点为新节点。 |
| 表尾插入 | 若已知尾节点指针:O (1)- 若未知尾节点指针:O (N) | 已知尾节点时,直接将尾节点指针指向新节点;未知时,需先遍历到尾节点(O (N))。 |
| 中间插入 | O(N) | 必须先遍历找到插入位置的前驱节点(O (N)),再修改前驱节点和新节点的指针完成插入。 |
综上,只有在已知插入位置的前驱节点(如表头、已知尾节点) 时,增加节点的时间复杂度才是 O (1);其他情况均为 O (N)。
3 线性表L如果需要频繁地进行不同下标元素的插入、删除操作,此时选择链式存储结构更好。
顺序存储的典型实现是数组,链式存储的典型实现是链表,但二者并非完全等同的概念。
1. 频繁插入删除:链式存储更优
对于频繁在不同下标进行插入、删除的场景,顺序存储(如数组)的效率远低于链式存储(如链表),核心差异在于数据移动成本。
-
顺序存储(数组):时间复杂度 O (N)
- 插入 / 删除元素时,需移动目标位置之后的所有元素,以腾出空间或填补空缺。
- 例如,在数组第 2 个位置插入元素,需将第 2 到第 N 个元素全部后移一位;删除同理,需将后续元素前移。
- 元素移动的次数与数据规模 N 相关,最坏情况下需移动全部元素,时间复杂度为 O (N)。
-
链式存储(链表):时间复杂度 O (1)(已知前驱节点时)
- 插入 / 删除仅需修改目标节点前后的指针指向,无需移动其他元素。
- 例如,在链表两个节点 A 和 B 之间插入节点 C,只需将 A 的指针指向 C,C 的指针指向 B 即可。
- 只要提前找到目标位置的前驱节点,操作本身仅需常数时间,效率远高于顺序存储。
2. 顺序存储与数组、链式存储与链表的关系
二者是 “抽象结构” 与 “具体实现” 的关系,并非完全等同,可理解为 “典型实现” 而非 “唯一实现”。
(1)顺序存储与数组
- 核心关系:数组是顺序存储结构最典型、最常用的实现方式。
- 顺序存储的定义:数据元素按逻辑顺序依次存储在连续的物理存储空间中,元素的逻辑位置与物理位置一致。
- 数组的特性:完全符合顺序存储的定义,元素存储在连续内存中,可通过下标直接计算物理地址,支持随机访问。
- 注意:除数组外,理论上可通过其他方式实现顺序存储(如早期的静态存储区域分配),但在实际编程中,数组是顺序存储的代名词。
(2)链式存储与链表
- 核心关系:链表是链式存储结构最典型、最常用的实现方式。
- 链式存储的定义:数据元素(节点)分散存储在非连续的物理空间中,通过指针或引用关联,体现元素间的逻辑顺序。
- 链表的特性:完全符合链式存储的定义,节点包含数据域和指针域,通过指针串联形成逻辑上的线性结构,不支持随机访问。
- 注意:链式存储也可通过其他结构实现(如用数组模拟链表,通过下标关联节点),但链表是其最直接、最自然的实现形式。
4 循环队列中front(队头)和rear(队尾)的变化规则
- 循环队列的数组大小为
n时,下标范围是0 ~ n-1,且操作具有 “循环性”(超出范围时取模)。 - 删除元素:
front后移(front = (front + 1) % n)。 - 添加元素:
rear后移(rear = (rear + 1) % n)。
步骤 1:初始状态
数组大小n=6,初始front=0,rear=4。
步骤 2:删除两个元素
每次删除元素,front后移 1 位:
- 第一次删除:
front = (0 + 1) % 6 = 1 - 第二次删除:
front = (1 + 1) % 6 = 2
步骤 3:加入两个元素
每次加入元素,rear后移 1 位:
- 第一次加入:
rear = (4 + 1) % 6 = 5 - 第二次加入:
rear = (5 + 1) % 6 = 0
最终front=2,rear=0
5 假设某个带头结点的单链表的头指针为head,则判定该表为空表的条件是( )
A.head==NULL
B.head->next==NULL
C.head!=NULL
D.head->next==head
答案: head->next == NULL
解析:带头结点的单链表中,头结点head始终存在(用于统一操作逻辑)。
若链表为空,意味着头结点之后没有任何有效节点,即头结点的next指针指向NULL。因此,判定该表为空表的条件是head->next == NULL。
