1. 概述
- 线性结构的特点
- 存在惟一的一个被称作“第一个”的数据元素
- 存在惟一的一个被称作“最后一个”的数据元素
- 除第一个之外,集合中每个数据元素均只有一个前驱
- 除最后一个之外,集合中每个数据元素均只有一个后继
2. 线性表的类型定义
- 线性表
- 是$n$个数据元素的有限序列
- 复杂的线性表
- 一个元素可以由若干个数据项(item)组成,数据元素称为记录(record),含有大量记录的线性表又称文件(file)
- 同一线性表中的元素必定具有相同属性,即属同一数据对象
- 数组
- 线性表的顺序存储结构,即一段连续的内存空间存储的数据元素
- 线性链表
- 用一组任意的存储单元存储线性表的数据元素,由结点构成
- 一个结点不仅存储数据元素,也需要存储后继结点指针
- 只包含一个指针域的线性链表称为单链表
- 包含前后指针域的线性链表称为双向线性链表
- 循环链表
- 表中最后一个结点的指针域指向头结点,整个链表形成一个环
3. 单向链表实现
- 创建链表
- 插入数据
- 在指定数据点后插入数据
- 更新指定数据点的数据
- 删除指定数据的结点
- 链表反转
- 打印
- 清空链表
代码:
- List.h
1 |
|
- List.cpp
1 |
|
4. 双向链表实现
- 创建链表
- 插入数据
- 在指定数据点后插入数据
- 更新指定数据点的数据
- 删除指定数据的结点
- 打印
- 清空链表
代码:
- DoubleLinkedList.hpp
1 |
|
- DoubleLinkedList.cpp
1 |
|
5. 循环链表实现
- 创建链表
- 插入数据
- 在指定数据点后插入数据
- 更新指定数据点的数据
- 删除指定数据的结点
- 打印
- 清空链表
代码:
- CircularList.h
1 |
|
- CircularList.cpp
1 |
|
6. 编程题
6.1 查找单链表中的倒数第k个结点
- 这里需要声明两个指针:即两个结点型的变量first和second,首先让first和second都指向第一个结点,然后让second结点前移k-1个位置,此时first和second就间隔了k-1个位置,然后整体向前移动这两个节点,直到second节点走到最后一个结点的时候,此时first节点所指向的位置就是倒数第k个节点的位置
- 时间复杂度为O(n)
代码:
1 | Node* FindNode(int k) { |
6.2 查找单链表中的中间结点
- 不允许你算出链表的长度
- 设置两个指针first和second,只不过这里是,两个指针同时向前走,second指针每次走两步,first指针每次走一步,直到second指针走到最后一个结点时,此时first指针所指的结点就是中间结点
代码:
1 | Node* FindNode() { |
6.3 合并两个有序单向链表
- 已知两个链表 head1 和 head2 各自有序,请把它们合并成一个链表依然有序。结果链表要包含 head1 和head2 的所有节点,即使节点值相同
不能开辟新空间来存储合并后的链表。如果第一次做该题,很容易会想到使用新链表来存储合并后的有序链表。虽然可以如此实现,但是不符合常规解法和面试官的要求
非递归实现
1 | Node* MergeList(Node *head_1, Node *head_2) { |
- 递归实现
1 | Node* MergeList(Node *head_1, Node *head_2) { |
6.4 单链表的反转
- 不使用额外的空间
- 思路:从头到尾遍历原链表,每遍历一个结点,将其摘下放在新链表的最前端。
注意链表为空和只有一个结点的情况。时间复杂度为O(n)
1 | Node* ReverseList(Node *head) { |
6.5 从尾到头打印单链表
- 递归实现:用递归实现,但有个问题:当链表很长的时候,就会导致方法调用的层级很深,有可能造成栈溢出。
1 | Node* ReversePrint(Node *head) { |
6.6 判断单链表是否有环
- 这里也是用到两个指针,如果一个链表有环,那么用一个指针去遍历,是永远走不到头的。因此,我们用两个指针去遍历:first指针每次走一步,second指针每次走两步,如果first指针和second指针相遇,说明有环。
时间复杂度为O (n)
1 | bool IsCycle(Node *head) { |
6.7 取出有环链表中环的长度
- 思路:环的长度即为从开始到相遇处first走的步数
1 | int GetCycleLength(Node *head) { |
6.8 有环单链表中,取出环的起始点
- 思路:从相遇点开始,设置一个节点从头开始,然后最终相遇的节点就是环的起始点。由a=c可知。
1 | Node* GetCycleLength(Node *head) { |