11.单链表
11.单链表
链表是线性表的链式存储方式,逻辑上相邻的数据在计算机内的存储位置不必须相邻,那么怎么表示逻辑上的相邻关系呢?可以给每个元素附加一个指针域,指向下一个元素的存储位置。如图所示:

从图中可以看出,每个结点包含两个域:数据域和指针域,指针域存储下一个结点的地址,因此指针指向的类型也是结点类型
链表的核心要素:
- 每个节点由数据域和指针域组成
- 指针域指向下一个节点的内存地址
其结构体定义:
Typedef struct LinkNode
{
ElemType data;
struct LinkNode *next;
}LinkList, LinkNode;
11.1 链表结构
链表的节点均单向指向下一个节点,形成一条单向访问的数据链

typedef struct _LinkNode
{
int data; //结点的数据域
struct _LinkNode* next; //结点的指针域
}LinkNode, LinkList; //LinkList 为指向结构体LNode 的指针类型
11.2 链表初始化

bool InitList(LinkList*& L)
{
L = new LinkNode;
if (!L) return false;//生成节点失败
L->next = NULL;
return true;
}
11.3 插入数据
11.3.1前插

//前插法
bool ListInsert_front(LinkList* &L, LinkNode *node)
{
if (!L || !node) return false;
node->next = L->next;
L->next = node;
}
11.3.2尾插

//尾插法
bool ListInsert_back(LinkList*& L, LinkNode* node)
{
LinkNode* last = NULL;
if (!L || !node) return false;
last = L;
while (last->next) last = last->next;
node->next = NULL;
last->next = node;
return true;
}
11.3.3 任意位置插入元素

// ---------------------------------------------------------
// 指定位置插入(越界自动转为尾插)
// L: 链表头, i: 插入位置(从1开始计数), e: 插入的数据
// ---------------------------------------------------------
bool LinkListInsert(LinkList*& L, int i, int e)
{
if (!L) return false;
if (i < 0)
{
cout << "插入位置必须大于等于0" << endl;
return false;
}
// p 指向头结点,j 代表 p 当前指向的是第几个结点(头结点算第0个)
LinkNode* p = L;
int j = 0;
// 核心逻辑:
// 我们要找第 i-1 个结点(即插入位置的前驱)。
// 循环条件:
// 1. p->next != NULL:只要后面还有节点,就可以往后找
// 2. j < i - 1:还没找到第 i-1 个位置
// 如果 i 很大,p->next 会先变成 NULL,循环终止,此时 p 指向最后一个节点(满足越界插在尾部的需求)
while (p->next != NULL && j < i - 1)
{
p = p->next;
j++;
}
// 生成新节点
LinkNode* node = new LinkNode;
if (!node) return false; // 内存分配失败检查
node->data = e;
node->next = p->next;
p->next = node;
return true;
}
11.4 打印
//打印
void LinkPrint(LinkList*& L)
{
LinkNode* p = NULL;
if (!L)
{
cout << "链表为空。";
return;
}
p = L->next;
while (p)
{
cout << p->data << "\t";
p = p->next;
}
cout << endl;
}
11.5 查询值
11.5.1 按位置查找值
//按位置查询值
// L: 链表头, i: 要查询的位置(从1开始), e: 用于带回结果的引用
bool GetLinkListElemBySite(LinkList* L, const int i, int& e)
{
// 1. 安全性检查:链表头不能空,i必须合法
if (!L || !L->next || i < 1)
{
return false;
}
// 2. 初始化遍历指针
// index 初始化为 1,对应第一个有效数据节点
int index = 1;
LinkNode* node = L->next;
// 3. 开始查找
// 循环条件:node不为空(没走到尽头) 且 当前位置还没到目标位置 i
while (node && index < i)
{
node = node->next; // 指针后移
index++; // 计数器+1
}
// 4. 结果判断
// 循环结束有两种情况:
// 情况A:node 变成 NULL 了(说明 i 超过了链表长度),查找失败
// 情况B:index == i(说明找到了),此时 node 指向第 i 个节点
if (!node)
{
return false; // 没找到(越界)
}
// 找到了,赋值给 e
e = node->data;
return true;
}
11.5.2按值查找
按位置查询值
bool GetLinkListElemByValue(LinkList*& L, const int e, int& index)
{
if (!L || !L->next)
{
index = 0;
return false;
}
LinkNode* p = L->next;
index = 1;
while (p != NULL)
{
if (p->data == e)
{
return true;
}
p = p->next;
index++;
}
// 如果循环跑完了还没返回,说明没找到。
// 此时 index 的值是 (链表长度 + 1),这是错误的,必须手动置 0
index = 0;
return false;
}
11.6 单链表的删除
//链表结点删除
bool DeleteLinkListNodeByIndex(LinkList*& L, const int index)
{
if (!L || !L->next)
{
return false;
}
LinkNode* p = L;
LinkNode* q = p->next;
int i = 1;
while (q)
{
if (i == index)
{
break;
}
p = p->next;
q = q->next;
i++;
}
// 检查是否找到了要删除的节点
if (!q)
{
return false; // index超出范围
}
// 删除节点q(无论是第几个节点,处理方式都一样)
p->next = q->next;
delete q; // 释放内存
return false;
}
11.7 链表销毁
void LinkDestroy(LinkList*& L)//单链表的销毁
{
//定义临时结点p指向头结点
LinkList* p = L;
cout << "销毁链表!" << endl;
while (p)
{
L = L->next;//L指向下一个结点
delete p;//删除当前结点
p = L;//p移向下一个结点
}
}
完整代码
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
typedef struct _LinkNode
{
int data; // 结点的数据域
struct _LinkNode* next; // 结点的指针域
} LinkList, LinkNode;
// 初始化
bool InitLinkList(LinkList*& L)
{
L = new LinkNode;
if (!L) return false;
L->next = NULL;
L->data = -1; // 头结点通常不存有效数据,或者用于存长度
return true;
}
// 前插法
bool LinkListInsert_front(LinkList*& L, LinkNode* node)
{
if (!L || !node) return false;
node->next = L->next;
L->next = node;
return true;
}
// 尾插法
bool LinkListInsert_back(LinkList*& L, LinkNode* node)
{
if (!L || !node) return false;
LinkNode* last = L;
while (last->next)
{
last = last->next;
}
node->next = NULL;
last->next = node;
return true;
}
// ---------------------------------------------------------
// 指定位置插入(越界自动转为尾插)
// L: 链表头, i: 插入位置(从1开始计数), e: 插入的数据
// ---------------------------------------------------------
bool LinkListInsert(LinkList*& L, int i, int e)
{
if (!L) return false;
if (i < 1)
{
cout << "插入位置必须>=1!" << endl;
return false;
}
LinkNode* node = L;
int j = 0;
while (node->next != NULL && j < i - 1)
{
node = node->next;
j++;
}
LinkNode* newNode = new LinkNode;
if (!newNode) return false;
newNode->data = e;
newNode->next = node->next;
node->next = newNode;
return true;
}
// 打印链表
void LinkListPrint(LinkList*& L)
{
if (!L)
{
cout << "链表不存在!" << endl;
return;
}
LinkNode* node = L->next;
if (!node) {
cout << "链表为空" << endl;
return;
}
while (node)
{
cout << node->data << "\t";
node = node->next;
}
cout << endl;
}
//按位置查询值
bool GetLinkListElemByValue(LinkList*& L, int e, int& index)
{
if (!L || !L->next)
{
index = 0;
return false;
}
LinkNode* p = L->next;
index = 1;
while (p != NULL)
{
if (p->data == e)
{
return true;
}
p = p->next;
index++;
}
// 如果循环跑完了还没返回,说明没找到。
// 此时 index 的值是 (链表长度 + 1),这是错误的,必须手动置 0
index = 0;
return false;
}
//链表结点删除
bool DeleteLinkListNodeByIndex(LinkList*& L, int index)
{
if (!L || !L->next)
{
return false;
}
LinkNode* p = L;
LinkNode* q = p->next;
int i = 1;
while (q)
{
if (i == index)
{
break;
}
p = p->next;
q = q->next;
i++;
}
// 检查是否找到了要删除的节点
if (!q)
{
return false; // index超出范围
}
// 删除节点q(无论是第几个节点,处理方式都一样)
p->next = q->next;
delete q; // 释放内存
return true;
}
//链表销毁
void DestroyLinkList(LinkList*& L)
{
LinkNode* p = L;
while (p)
{
L = L->next;
delete p;
p = L;
}
}
int main()
{
LinkList* L = NULL;
// 1.初始化空链表
InitLinkList(L);
// 2.使用前插法插入数据
cout << "--- 前插法测试 ---" << endl;
cout << "请输入需要插入的元素个数:";
int insertFrontNum;
cin >> insertFrontNum;
LinkNode* s = NULL;
while (insertFrontNum--)
{
cout << "请输入数据:";
s = new LinkNode;
cin >> s->data;
LinkListInsert_front(L, s);
}
cout << "前插法结果:";
LinkListPrint(L);
// 3.使用尾插法插入数据
cout << "\n--- 尾插法测试 ---" << endl;
cout << "请输入需要插入的元素个数:";
int insertBackNum; // 建议用新变量,避免逻辑混淆
cin >> insertBackNum;
while (insertBackNum--)
{
cout << "请输入数据:";
s = new LinkNode;
cin >> s->data;
LinkListInsert_back(L, s);
}
cout << "尾插法结果:";
LinkListPrint(L);
// 4.任意位置插入元素(核心测试)
cout << "\n--- 任意位置插入测试 ---" << endl;
while (true) {
int i, e;
cout << "请输入插入位置 i (输入0退出) 和 数据 e:";
cin >> i;
if (i == 0) break;
cin >> e;
LinkListInsert(L, i, e);
cout << "插入后链表状态:";
LinkListPrint(L);
}
// ---------------------------------------------------------
// 接您代码中的步骤 5...
// ---------------------------------------------------------
// 5. 按值查询 (测试 GetLinkListElemByValue)
cout << "\n>>> 5. 测试按值查询 <<<" << endl;
int searchVal, index;
cout << "请输入要查询的数值:";
cin >> searchVal;
// 调用查找函数,通过返回值判断是否存在
if (GetLinkListElemByValue(L, searchVal, index))
{
cout << "查询成功!值 " << searchVal << " 在第 " << index << " 个位置。" << endl;
}
else
{
cout << "查询失败!链表中不存在该值。" << endl;
}
// 6. 按位置删除 (测试 DeleteLinkListNodeByIndex)
cout << "\n>>> 6. 测试按位置删除 <<<" << endl;
int delIndex;
cout << "请输入要删除第几个节点:";
cin >> delIndex;
// 调用删除函数
if (DeleteLinkListNodeByIndex(L, delIndex))
{
cout << "删除成功!当前链表如下:" << endl;
LinkListPrint(L); // 打印验证
}
else
{
cout << "删除失败!(位置越界或链表为空)" << endl;
}
// 7. 链表销毁 (测试 DestroyLinkList)
cout << "\n>>> 7. 测试链表销毁 <<<" << endl;
cout << "按任意键销毁链表..." << endl;
system("pause"); // 暂停一下,让用户准备好
DestroyLinkList(L);
cout << "销毁完成,尝试打印链表:" << endl;
LinkListPrint(L); // 这里应该提示“链表不存在”
system("pause");
return 0;
}
参考资料:
奇牛学院

浙公网安备 33010602011771号