- C++
📚 C++ 线性表(Linear List):顺序表和单链表
- 2025-5-22 22:55:29 @
📚 C++线性表完全教程:从入门到精通(结构体实现版)
🌟 欢迎进入数据结构的世界
在编程的世界里,我们经常需要处理大量的数据。如何高效地存储、管理和操作这些数据呢?这就需要用到数据结构。而线性表作为最基础、最常用的数据结构之一,是我们必须掌握的核心知识!
今天,我们就来系统学习C++中的线性表,使用结构体实现且不使用函数模板,从概念到实现,从理论到实战,一步一步揭开它的神秘面纱!
🧩 什么是线性表?
线性表是一种数据元素按顺序排列的线性结构,它具有以下特点:
- 元素有序:元素之间存在一对一的前驱和后继关系
- 有限个元素:线性表的长度是有限的
- 同构性:所有元素的类型相同
🌰 生活中的线性表比喻
线性表就像一列火车:
- 每个车厢是一个数据元素
- 车厢之间按照顺序连接
- 可以在任意位置添加或移除车厢
- 可以从头或尾依次访问每个车厢
线性表的分类
线性表主要分为两种实现方式:
- 顺序表:用数组实现,元素存储在连续的内存空间中
- 链表:用节点实现,每个节点包含数据和指向下一个节点的指针
下面我们分别深入学习这两种实现方式!
📦 顺序表(Array List)
🔧 基本概念
顺序表是线性表的顺序存储结构,它使用数组来存储元素。
🌟 优点
- 随机访问效率高(O(1)时间复杂度)
- 实现简单
- 内存空间连续,缓存利用率高
🚫 缺点
- 插入和删除操作效率低(平均O(n)时间复杂度)
- 需要预先分配固定大小的内存空间
- 扩展容量时需要重新分配内存并复制数据
🛠️ C++结构体实现顺序表(以int类型为例)
下面是一个使用结构体实现的顺序表:
#include <iostream>
using namespace std;
// 定义顺序表结构体(存储int类型数据)
struct ArrayList {
int* data; // 存储数据的数组
int capacity; // 数组容量
int size; // 当前元素个数
// 初始化顺序表
void init(int initCapacity = 10) {
data = new int[initCapacity];
capacity = initCapacity;
size = 0;
}
// 销毁顺序表
void destroy() {
delete[] data;
capacity = 0;
size = 0;
}
// 获取元素个数
int getSize() const {
return size;
}
// 判断是否为空
bool isEmpty() const {
return size == 0;
}
// 在末尾添加元素
void add(int element) {
if (size == capacity) {
resize(2 * capacity); // 扩容为原来的2倍
}
data[size++] = element;
}
// 在指定位置插入元素
void insert(int index, int element) {
if (index < 0 || index > size) {
cout << "插入位置不合法" << endl;
return;
}
if (size == capacity) {
resize(2 * capacity);
}
// 将index及其后面的元素后移一位
for (int i = size; i > index; i--) {
data[i] = data[i - 1];
}
data[index] = element;
size++;
}
// 删除指定位置的元素
int remove(int index) {
if (index < 0 || index >= size) {
cout << "删除位置不合法" << endl;
return -1; // 返回错误值
}
int removed = data[index];
// 将index后面的元素前移一位
for (int i = index; i < size - 1; i++) {
data[i] = data[i + 1];
}
size--;
// 如果元素个数小于容量的1/4,则缩容为原来的1/2
if (size <= capacity / 4 && capacity > 10) {
resize(capacity / 2);
}
return removed;
}
// 获取指定位置的元素
int get(int index) const {
if (index < 0 || index >= size) {
cout << "访问位置不合法" << endl;
return -1; // 返回错误值
}
return data[index];
}
// 修改指定位置的元素
void set(int index, int element) {
if (index < 0 || index >= size) {
cout << "修改位置不合法" << endl;
return;
}
data[index] = element;
}
// 查找元素第一次出现的位置
int find(int element) const {
for (int i = 0; i < size; i++) {
if (data[i] == element) {
return i;
}
}
return -1; // 未找到
}
// 打印所有元素
void print() const {
cout << "ArrayList: size = " << size << ", capacity = " << capacity << endl;
cout << "[";
for (int i = 0; i < size; i++) {
cout << data[i];
if (i != size - 1) {
cout << ", ";
}
}
cout << "]" << endl;
}
private:
// 扩容函数(私有方法)
void resize(int newCapacity) {
int* newData = new int[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
delete[] data;
data = newData;
capacity = newCapacity;
}
};
int main() {
ArrayList list;
list.init(); // 初始化顺序表
// 添加元素
list.add(10);
list.add(20);
list.add(30);
list.print(); // 输出: [10, 20, 30]
// 插入元素
list.insert(1, 15);
list.print(); // 输出: [10, 15, 20, 30]
// 删除元素
list.remove(2);
list.print(); // 输出: [10, 15, 30]
// 修改元素
list.set(0, 100);
list.print(); // 输出: [100, 15, 30]
// 查找元素
cout << "元素15的位置是: " << list.find(15) << endl; // 输出: 1
list.destroy(); // 销毁顺序表
return 0;
}
🔗 链表(Linked List)
🔧 基本概念
链表是线性表的链式存储结构,它由一系列节点组成,每个节点包含:
- 数据域:存储元素的值
- 指针域:指向下一个节点的指针(单链表)
🌟 优点
- 插入和删除操作效率高(O(1)时间复杂度,前提是已知插入/删除位置)
- 动态分配内存,无需预先分配固定大小
- 适合频繁插入和删除的场景
🚫 缺点
- 随机访问效率低(O(n)时间复杂度)
- 需要额外的空间存储指针
- 缓存利用率低
链表的分类
链表可以分为:
- 单链表:每个节点只有一个指向下一个节点的指针
- 双向链表:每个节点有两个指针,分别指向前驱和后继节点
- 循环链表:尾节点的指针指向头节点,形成一个环
🛠️ C++结构体实现单链表(以int类型为例)
下面是一个使用结构体实现的单链表:
#include <iostream>
using namespace std;
// 定义链表节点结构体(存储int类型数据)
struct Node {
int data;
Node* next;
Node(int value) : data(value), next(nullptr) {}
};
// 定义链表结构体
struct LinkedList {
Node* head; // 头指针
int size; // 链表长度
// 初始化链表
void init() {
head = nullptr;
size = 0;
}
// 销毁链表
void destroy() {
while (head != nullptr) {
Node* temp = head;
head = head->next;
delete temp;
}
size = 0;
}
// 判断链表是否为空
bool isEmpty() const {
return size == 0;
}
// 获取链表长度
int getSize() const {
return size;
}
// 在头部插入元素
void insertAtHead(int element) {
Node* newNode = new Node(element);
newNode->next = head;
head = newNode;
size++;
}
// 在尾部插入元素
void insertAtTail(int element) {
if (isEmpty()) {
insertAtHead(element);
return;
}
Node* current = head;
while (current->next != nullptr) {
current = current->next;
}
Node* newNode = new Node(element);
current->next = newNode;
size++;
}
// 在指定位置插入元素
void insert(int index, int element) {
if (index < 0 || index > size) {
cout << "插入位置不合法" << endl;
return;
}
if (index == 0) {
insertAtHead(element);
return;
}
Node* current = head;
for (int i = 0; i < index - 1; i++) {
current = current->next;
}
Node* newNode = new Node(element);
newNode->next = current->next;
current->next = newNode;
size++;
}
// 删除头部元素
void removeAtHead() {
if (isEmpty()) {
cout << "链表为空,无法删除" << endl;
return;
}
Node* temp = head;
head = head->next;
delete temp;
size--;
}
// 删除尾部元素
void removeAtTail() {
if (isEmpty()) {
cout << "链表为空,无法删除" << endl;
return;
}
if (size == 1) {
removeAtHead();
return;
}
Node* current = head;
while (current->next->next != nullptr) {
current = current->next;
}
delete current->next;
current->next = nullptr;
size--;
}
// 删除指定位置的元素
void remove(int index) {
if (index < 0 || index >= size) {
cout << "删除位置不合法" << endl;
return;
}
if (index == 0) {
removeAtHead();
return;
}
Node* current = head;
for (int i = 0; i < index - 1; i++) {
current = current->next;
}
Node* temp = current->next;
current->next = temp->next;
delete temp;
size--;
}
// 获取指定位置的元素
int get(int index) const {
if (index < 0 || index >= size) {
cout << "访问位置不合法" << endl;
return -1; // 返回错误值
}
Node* current = head;
for (int i = 0; i < index; i++) {
current = current->next;
}
return current->data;
}
// 查找元素第一次出现的位置
int find(int element) const {
Node* current = head;
int index = 0;
while (current != nullptr) {
if (current->data == element) {
return index;
}
current = current->next;
index++;
}
return -1; // 未找到
}
// 打印链表
void print() const {
Node* current = head;
cout << "LinkedList: size = " << size << endl;
cout << "[";
while (current != nullptr) {
cout << current->data;
if (current->next != nullptr) {
cout << " -> ";
}
current = current->next;
}
cout << "]" << endl;
}
};
int main() {
LinkedList list;
list.init(); // 初始化链表
// 添加元素
list.insertAtTail(10);
list.insertAtTail(20);
list.insertAtTail(30);
list.print(); // 输出: [10 -> 20 -> 30]
// 插入元素
list.insert(1, 15);
list.print(); // 输出: [10 -> 15 -> 20 -> 30]
// 删除元素
list.remove(2);
list.print(); // 输出: [10 -> 15 -> 30]
// 查找元素
cout << "元素15的位置是: " << list.find(15) << endl; // 输出: 1
list.destroy(); // 销毁链表
return 0;
}
📊 顺序表 vs 链表
特性 | 顺序表 (数组) | 链表 (Linked List) |
---|---|---|
存储结构 | 连续内存空间 | 离散内存空间,通过指针连接 |
随机访问 | O(1)(高效) | O(n)(低效) |
插入/删除操作 | O(n)(平均需移动元素) | O(1)(已知位置时) |
内存利用率 | 预分配可能导致浪费 | 按需分配,无内存浪费 |
空间开销 | 仅数据存储 | 数据 + 指针(额外开销) |
适用场景 | 频繁随机访问,较少插入删除 | 频繁插入删除,较少随机访问 |
💡 线性表的应用场景
线性表是许多复杂数据结构的基础,常见应用场景包括:
- 数组、列表:存储和管理批量数据
- 栈和队列:基于线性表实现的受限访问数据结构
- 哈希表:处理冲突时可能使用链表
- 图的邻接表表示:使用链表存储图的边信息
- 文件系统:目录结构通常使用线性表组织
掌握线性表的基础知识,对于学习更高级的数据结构和算法至关重要!
🌟 总结与鼓励
恭喜你完成了C++线性表的学习!现在你已经掌握了:
- 线性表的基本概念和两种主要实现方式
- 使用结构体实现顺序表和链表的方法(不使用函数模板)
- 顺序表和链表的优缺点及适用场景
- 线性表的常见操作和应用场景
线性表是数据结构的基础,希望你能够熟练掌握并灵活运用。继续加油,你一定能在编程的道路上越走越远! 💪
📚 拓展学习
- 学习双向链表和循环链表的实现
- 探索线性表在实际项目中的应用
- 研究基于线性表实现的其他数据结构(如栈、队列)
- 练习线性表相关的算法题(如反转链表、合并有序链表等)
记住,编程的进步源于不断的实践和思考。继续加油,你一定能成为编程高手! 🚀
1 条评论
-
admin SU @ 2025-5-22 22:57:02
🧠 C++ 线性表(Linear List)通俗易懂教程 🚀
📌 使用结构体实现顺序表与单链表,不使用类和函数模板!适合初学者理解!
🎯 一、什么是线性表?
线性表 是一种最基础的数据结构,表示 一组数据元素的有限序列。
你可以把它想象成:
🧩 一条队伍:每个人站在一个位置上,有前一个人和后一个人。
🔢 每个数据之间有“顺序”关系。✅ 线性表的特点:
- 数据元素是同一类型
- 元素之间是一对一的关系(前驱 & 后继)
- 存储方式可以是连续的(顺序表)或不连续的(链表)
🧱 二、线性表的分类 🔍
类型 描述 顺序表(Array-based List) 使用数组存储,物理地址连续 链表(Linked List) 使用指针连接节点,物理地址不连续
📈 三、线性表的基本操作 🛠️
操作 功能说明 Insert(i, x)
在第 i 个位置插入元素 x Delete(i)
删除第 i 个位置的元素 Get(i)
获取第 i 个位置的元素 Locate(x)
查找值为 x 的元素的位置 Length()
返回线性表当前长度 Empty()
判断是否为空表
🧪 四、顺序表实现(使用结构体)📦
我们使用结构体来封装顺序表的信息,并通过函数对其进行操作。
✅ 示例代码:
#include <iostream> using namespace std; #define MAX_SIZE 100 // 定义顺序表结构体 typedef struct { int data[MAX_SIZE]; // 数据域 int length; // 当前长度 } SeqList; // 初始化顺序表 void initList(SeqList* list) { list->length = 0; } // 插入元素 bool insert(SeqList* list, int index, int value) { if (index < 0 || index > list->length || list->length >= MAX_SIZE) return false; for (int i = list->length; i > index; --i) list->data[i] = list->data[i - 1]; list->data[index] = value; list->length++; return true; } // 删除元素 bool remove(SeqList* list, int index) { if (index < 0 || index >= list->length) return false; for (int i = index; i < list->length - 1; ++i) list->data[i] = list->data[i + 1]; list->length--; return true; } // 获取元素 int get(SeqList* list, int index) { if (index < 0 || index >= list->length) return -1; return list->data[index]; } // 查找元素位置 int locate(SeqList* list, int value) { for (int i = 0; i < list->length; ++i) if (list->data[i] == value) return i; return -1; } // 获取长度 int length(SeqList* list) { return list->length; } // 是否为空 bool empty(SeqList* list) { return list->length == 0; } // 打印线性表 void printList(SeqList* list) { for (int i = 0; i < list->length; ++i) cout << list->data[i] << " "; cout << endl; }
🧪 主函数测试:
int main() { SeqList list; initList(&list); insert(&list, 0, 10); insert(&list, 1, 20); insert(&list, 2, 30); cout << "当前线性表:" << endl; printList(&list); cout << "查找 20 的位置:" << locate(&list, 20) << endl; remove(&list, 1); cout << "删除第1个元素后的线性表:" << endl; printList(&list); return 0; }
📌 输出:
当前线性表: 10 20 30 查找 20 的位置:1 删除第1个元素后的线性表: 10 30
🔗 五、单链表实现(使用结构体)🔗
我们使用结构体来定义节点,并用指针连接起来。
✅ 示例代码:
#include <iostream> using namespace std; // 定义链表节点结构体 typedef struct Node { int data; struct Node* next; } Node; // 初始化带头结点的链表 Node* initList() { Node* head = (Node*)malloc(sizeof(Node)); head->next = nullptr; return head; } // 插入元素(在第 index 个位置插入) bool insert(Node* head, int index, int value) { Node* p = head; for (int i = 0; i < index && p != nullptr; ++i) p = p->next; if (p == nullptr) return false; Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = value; newNode->next = p->next; p->next = newNode; return true; } // 删除元素(删除第 index 个位置的元素) bool remove(Node* head, int index) { Node* p = head; for (int i = 0; i < index && p != nullptr; ++i) p = p->next; if (p == nullptr || p->next == nullptr) return false; Node* del = p->next; p->next = del->next; free(del); return true; } // 获取元素 int get(Node* head, int index) { Node* p = head->next; for (int i = 0; i < index && p != nullptr; ++i) p = p->next; if (p == nullptr) return -1; return p->data; } // 查找元素位置 int locate(Node* head, int value) { Node* p = head->next; int index = 0; while (p != nullptr) { if (p->data == value) return index; p = p->next; index++; } return -1; } // 获取长度 int length(Node* head) { int count = 0; Node* p = head->next; while (p != nullptr) { count++; p = p->next; } return count; } // 是否为空 bool empty(Node* head) { return head->next == nullptr; } // 打印链表 void printList(Node* head) { Node* p = head->next; while (p != nullptr) { cout << p->data << " "; p = p->next; } cout << endl; }
🧪 主函数测试:
int main() { Node* head = initList(); insert(head, 0, 10); insert(head, 1, 20); insert(head, 2, 30); cout << "当前链表:" << endl; printList(head); cout << "查找 20 的位置:" << locate(head, 20) << endl; remove(head, 1); cout << "删除第1个元素后的链表:" << endl; printList(head); return 0; }
📌 输出:
当前链表: 10 20 30 查找 20 的位置:1 删除第1个元素后的链表: 10 30
📌 六、注意事项 ❗
注意项 说明 下标从 0 开始 和数组一致,注意边界检查 插入删除效率 顺序表在中间插入/删除慢;链表快 内存分配 静态数组大小固定;链表动态增长 头结点设计 单链表中加入头结点能简化操作逻辑
🧠 七、应用场景举例 💡
场景 示例 学生成绩管理 存储学生成绩并进行增删改查 编辑器文本行 文本编辑器中的每一行内容 浏览器历史记录 前进/后退功能实现 播放列表 音乐播放器的歌曲队列管理
🎁 八、加油鼓励语 💪
👋 亲爱的同学,你已经掌握了 C++ 中非常实用的线性表知识!这是你通往算法高手之路的重要一步!
🎯 线性表是所有复杂数据结构的基础,无论是栈、队列、树还是图,都离不开它。
🧠 掌握它,你会发现自己能轻松应对很多看似复杂的编程挑战!🚀 继续努力,坚持练习,你一定能在编程世界中大放异彩!我们在这里为你打call!👏👏👏
📅 最后更新时间:2025年5月22日 22:52
🎉 祝你学习愉快,早日成为编程高手!🌟
- 1