深入解析:【数据结构】不带头节点单链表的基本操作
2025-10-01 09:21 tlnshuju 阅读(0) 评论(0) 收藏 举报1. 初始化算法(InitList_NoHead
)
// 1. 初始化(不带头节点):空表时L=NULL
Status InitList_NoHead(LinkList &L) {L = NULL; // 不带头节点的空表,链表指针直接为NULL(无首元节点)return OK;
}
功能
创建一个不带头节点的空单链表(空表时无任何节点,链表指针L
直接为NULL
)。
核心原理
不带头节点的单链表无 “占位头节点”,空表的唯一标识是链表指针L
指向NULL
(区别于带头节点的空表:头节点存在,head->next=NULL
)。
详细步骤
- 直接将链表指针
L
赋值为NULL
:L = NULL
(表示无首元节点,链表为空)。 - 返回成功状态
OK
。
时间复杂度
O(1)(仅执行固定次数的赋值操作,无循环)。
关键说明
- 无需为 “头节点” 分配内存,节省一个节点的存储空间;
- 后续所有操作需先判断
L是否为NULL
(避免空表非法访问)。
2. 取值算法(GetElem_NoHead
)
// 2. 取值(不带头节点):首元节点为L,位置i从1开始
Status GetElem_NoHead(LinkList L, int i, ElemType &e) {LinkList p = L; // 从首元节点开始遍历(不带头节点,无“头结点”可跳)int j = 1; // 计数器:当前指向第j个节点// 遍历到第i个节点,或p为NULL(位置超出表长)while (p != NULL && j < i) {p = p->next;j++;}// 位置不合法:i<1 或 j>i(p为NULL,未找到第i个节点)if (p == NULL || j > i) {return ERROR;}e = p->data; // 取出第i个节点的数据return OK;
}
功能
根据位置i
(1-based,即首元节点为第 1 个位置) ,从链表中获取对应元素的值,存入e
。
核心原理
从首元节点(L
指向的节点)开始遍历,用计数器j
记录当前遍历到的 “位置”,直到j == i
(找到目标节点)或p == NULL
(位置i
超出表长)。
详细步骤
- 初始化指针
p = L
(指向首元节点),计数器j = 1
(首元节点为第 1 个位置)。 - 循环遍历:若
p != NULL
且j < i
,则p = p->next
(指针后移)、j++
(计数器加 1)。 - 合法性判断:
- 若
p == NULL
(未找到第i
个节点,i
超出表长)或j > i
(i < 1
,位置非法),返回ERROR
; - 否则,将目标节点的值存入
e
:e = p->data
,返回OK
。
- 若
时间复杂度
O(n)(最坏情况需遍历到表尾,如i
为表长)。
关键说明
- 遍历起点为
L
(首元节点),区别于带头节点的取值(从head->next
开始); - 位置
i
必须满足1 ≤ i ≤ 表长
,否则取值失败。
3. 查找算法 1(LocateElem_NoHead
)
// 3. 查找(不带头节点):返回元素e所在节点的地址,未找到返回NULL
LinkList LocateElem_NoHead(LinkList L, ElemType e) {LinkList p = L; // 从首元节点开始遍历// 遍历所有节点,对比数据域while (p != NULL && p->data != e) {p = p->next;}return p; // 找到则返回节点地址,未找到返回NULL
}
功能
根据元素值e
,查找链表中第一个值等于e
的节点,返回该节点的地址(指针);未找到则返回NULL
。
核心原理
从首元节点开始遍历,逐个对比节点的data
域与e
,找到匹配节点则返回其指针,遍历结束仍无匹配则返回NULL
。
详细步骤
- 初始化指针
p = L
(指向首元节点)。 - 循环遍历:若
p != NULL
且p->data != e
,则p = p->next
(指针后移)。 - 返回
p
:若找到匹配节点,p
指向该节点;否则p
为NULL
。
时间复杂度
O(n)(最坏情况需遍历所有节点)。
关键说明
- 适用于需要修改找到节点数据的场景(通过返回的指针直接操作节点);
- 若链表中有多个值等于
e
的节点,仅返回第一个匹配节点的地址。
4. 查找算法 2(LocateELem_L_NoHead
)
// 4. 查找(不带头节点):返回元素e的位序(1开始),未找到返回0(实现与声明一致)
int LocateELem_L_NoHead(LinkList L, ElemType e) {LinkList p = L; // 从首元节点开始遍历int j = 1; // 记录当前节点的位序// 遍历所有节点,对比数据域while (p != NULL && p->data != e) {p = p->next;j++;}return (p != NULL) ? j : 0; // 找到返回位序,未找到返回0
}
功能
根据元素值e
,查找链表中第一个值等于e
的节点,返回其位置(1-based) ;未找到则返回0
。
核心原理
遍历过程中增加 “位置计数器”j
,找到匹配节点时返回j
,未找到则返回0
。
详细步骤
- 初始化指针
p = L
(指向首元节点),计数器j = 1
。 - 循环遍历:若
p != NULL
且p->data != e
,则p = p->next
、j++
。 - 返回结果:若
p != NULL
(找到),返回j
;否则返回0
。
时间复杂度
O(n)(与查找算法 1 一致)。
关键说明
- 适用于仅需获取元素位置的场景(如后续插入 / 删除操作需位置参数);
- 返回
0
是明确的 “未找到” 标识,避免与合法位置(≥1)混淆。
5. 插入算法(ListInsert1
)
// 5. 插入(不带头节点):在第i个位置前插入元素e,i=1时插入首元
Status ListInsert1(LinkList &L, int i, ElemType e) {// 位置合法性初步判断:i<1 或 空表时i>1(空表只能插入第1个位置)if (i < 1 || (L == NULL && i > 1)) {return ERROR;}LinkList s = new LNode; // 生成新节点s->data = e; // 赋值新节点数据域// 情况1:插入首元节点(i=1,此时L为NULL或指向原首元)if (i == 1) {s->next = L; // 新节点指向原首元(空表时L=NULL,新节点next=NULL)L = s; // 链表指针指向新节点(新首元)return OK;}// 情况2:插入非首元节点(i>1):先找第i-1个节点LinkList p = L;int j = 1;while (p != NULL && j < i - 1) {p = p->next;j++;}// 位置不合法:未找到第i-1个节点(i超出表长+1)if (p == NULL || j > i - 1) {delete s; // 释放未插入的新节点,避免内存泄漏return ERROR;}// 插入新节点:第i-1个节点后接新节点,新节点接原第i个节点s->next = p->next;p->next = s;return OK;
}
功能
在不带头节点的单链表中,第i
个位置之前插入元素e
(插入后原第i
个及之后的节点后移)。
核心原理
不带头节点的插入需分两种场景:
- 插入首元节点(
i=1
):需修改链表指针L
,让L
指向新节点; - 插入非首元节点(
i>1
):需找到第i-1
个节点,建立 “i-1
节点→新节点→原i
节点” 的链接。
详细步骤
- 合法性初步判断:若
i < 1
,或空表(L=NULL
)时i > 1
(空表只能插入第 1 个位置),返回ERROR
。 - 为新节点分配内存:
LinkList s = new LNode;
,赋值s->data = e
。 - 分场景插入:
- 场景 1:插入首元(
i=1
) :- 新节点指向原首元:
s->next = L
(空表时L=NULL
,新节点next
也为NULL
); - 链表指针指向新节点:
L = s
(新节点成为首元);
- 新节点指向原首元:
- 场景 2:插入非首元(
i>1
) :- 找第
i-1
个节点:p = L
,j=1
,循环p != NULL && j < i-1
,p=p->next
,j++
; - 合法性二次判断:若
p == NULL
(未找到i-1
节点,i
超出表长 + 1)或j > i-1
,释放新节点(delete s
),返回ERROR
; - 插入新节点:
s->next = p->next; p->next = s;
。
- 找第
- 场景 1:插入首元(
- 返回
OK
。
时间复杂度
O(n)(最坏情况需遍历到表尾,如插入到最后一个位置)。
关键说明
- 未插入成功时需释放新节点(
delete s
),避免内存泄漏; - 插入后表长增加 1(无需显式记录表长,通过遍历可计算)。
6. 删除算法(ListDelete1
)
// 6. 删除(不带头节点):删除第i个位置的元素,i=1时删除首元
Status ListDelete1(LinkList &L, int i) {// 位置合法性初步判断:i<1 或 空表(L=NULL)if (i < 1 || L == NULL) {return ERROR;}LinkList q; // 暂存待删除节点// 情况1:删除首元节点(i=1)if (i == 1) {q = L; // 待删除节点为当前首元L = L->next; // 链表指针指向原首元的下一个节点(新首元,空表时为NULL)delete q; // 释放原首元节点内存return OK;}// 情况2:删除非首元节点(i>1):先找第i-1个节点LinkList p = L;int j = 1;while (p != NULL && j < i - 1) {p = p->next;j++;}// 位置不合法:未找到第i-1个节点,或第i个节点不存在(p->next=NULL)if (p == NULL || p->next == NULL || j > i - 1) {return ERROR;}// 删除第i个节点:第i-1个节点跳过待删除节点,释放待删除节点内存q = p->next;p->next = q->next;delete q;return OK;
}
功能
删除不带头节点的单链表中第i
个位置的元素(删除后原第i+1
个及之后的节点前移)。
核心原理
与插入类似,需分 “删除首元” 和 “删除非首元” 场景,核心是 “断链 + 释放内存”:
- 删除首元:修改
L
指向原首元的下一个节点,释放原首元; - 删除非首元:找到第
i-1
个节点,跳过第i
个节点,释放其内存。
详细步骤
- 合法性初步判断:若
i < 1
或空表(L=NULL
),返回ERROR
。 - 分场景删除:
- 场景 1:删除首元(
i=1
) :- 暂存原首元节点:
LinkList q = L
; - 链表指针指向新首元:
L = L->next
(空表时L
变为NULL
); - 释放原首元:
delete q
;
- 暂存原首元节点:
- 场景 2:删除非首元(
i>1
) :- 找第
i-1
个节点:p = L
,j=1
,循环p != NULL && j < i-1
,p=p->next
,j++
; - 合法性二次判断:若
p == NULL
(未找到i-1
节点)或p->next == NULL
(无第i
个节点),返回ERROR
; - 暂存待删除节点:
LinkList q = p->next
; - 断链:
p->next = q->next
(跳过待删除节点); - 释放内存:
delete q
。
- 找第
- 场景 1:删除首元(
- 返回
OK
。
时间复杂度
O(n)(最坏情况需遍历到表尾,如删除最后一个节点)。
关键说明
- 必须释放待删除节点的内存(
delete q
),避免内存泄漏; - 删除后表长减少 1,空表时
L
会变为NULL
。
7. 判空算法(ListEmpty_NoHead
)
// 7. 判空(不带头节点):L==NULL即为空表
int ListEmpty_NoHead(LinkList L) {return (L == NULL) ? 1 : 0; // 1-空表,0-非空表
}
功能
判断不带头节点的单链表是否为空。
核心原理
不带头节点的空表唯一标识是 “链表指针L == NULL
”(无任何节点),区别于带头节点的空表(头节点存在,head->next == NULL
)。
详细步骤
- 若
L == NULL
,返回1
(表示空表); - 否则返回
0
(表示非空表)。
时间复杂度
O(1)(仅需一次指针比较)。
关键说明
- 所有涉及 “修改链表” 的操作(插入、删除)前,需先调用此算法判断是否为空表,避免非法访问;
- 是后续操作(如取值、查找)的前置合法性判断条件。
8. 求长算法(ListLength_L_NoHead
)
// 8. 求长(不带头节点):从首元节点L开始遍历,统计节点数
int ListLength_L_NoHead(LinkList L) {int len = 0;LinkList p = L;while (p != NULL) {len++;p = p->next;}return len;
}
功能
计算不带头节点的单链表的长度(即实际存储的节点个数)。
核心原理
从首元节点(L
指向的节点)开始遍历,用计数器记录节点数量,直到指针指向NULL
(遍历结束)。
详细步骤
- 初始化计数器
len = 0
,指针p = L
。 - 循环遍历:若
p != NULL
,则len++
、p = p->next
。 - 返回
len
。
时间复杂度
O(n)(需遍历所有节点,n
为表长)。
关键说明
- 不带头节点的链表无 “表长变量”,需通过遍历计算(若频繁求长,可优化为在链表结构中增加
length
成员); - 遍历起点为
L
,区别于带头节点的求长(从head->next
开始)。
9. 销毁算法(DestroyList_NoHead
)
// 9. 销毁(不带头节点):遍历删除所有节点,最后L=NULL
Status DestroyList_NoHead(LinkList &L) {LinkList p;while (L != NULL) {p = L; // 暂存当前节点L = L->next; // 链表指针后移delete p; // 释放当前节点内存}L = NULL; // 确保销毁后链表指针为NULL,避免野指针return OK;
}
功能
释放不带头节点单链表中所有节点的动态内存,避免内存泄漏,最终将L
置为NULL
。
核心原理
链表节点通过new
动态分配,必须手动遍历释放每个节点;若直接L = NULL
,会导致所有节点内存无法回收(内存泄漏)。
详细步骤
- 初始化指针
p
(暂存当前节点)。 - 循环遍历释放:
- 暂存当前节点:
p = L
; - 链表指针后移:
L = L->next
; - 释放当前节点:
delete p
;
- 暂存当前节点:
- 遍历结束后,
L
已为NULL
,无需额外赋值(若遍历前L=NULL
,循环不执行)。
时间复杂度
O(n)(需遍历并释放所有节点)。
关键说明
- 程序退出前必须调用此算法,尤其是多次创建链表的场景;
- 释放后
L
为NULL
,再次操作前需重新初始化。
10. 打印算法(PrintList_NoHead
)
// 10. 打印(不带头节点):适配空表(L=NULL)、非空表(从L开始遍历)
void PrintList_NoHead(LinkList L) {if (L == NULL) {cout << "空表" << endl;return;}// 非空表:从首元节点L开始遍历打印cout << "[";LinkList p = L;while (p != NULL) {cout << p->data;if (p->next != NULL) {cout << ", ";}p = p->next;}cout << "]" << endl;
}
功能
格式化打印不带头节点的单链表,空表输出 “空表”,非空表输出[元素1, 元素2, ..., 元素n]
。
核心原理
适配空表和非空表的不同场景,遍历非空表时按格式拼接元素。
详细步骤
- 若
L == NULL
(空表),输出 “空表” 并换行,返回; - 非空表处理:
- 输出左括号:
cout << "["
; - 初始化指针
p = L
,循环遍历:- 输出当前节点数据:
cout << p->data
; - 若不是最后一个节点(
p->next != NULL
),输出 “,”; - 指针后移:
p = p->next
;
- 输出当前节点数据:
- 输出右括号并换行:
cout << "]" << endl
。
- 输出左括号:
时间复杂度
O(n)(需遍历所有节点)。
关键说明
- 格式化输出便于直观查看链表内容,是调试和验证其他算法(如插入、删除)的重要工具;
- 处理了 “最后一个节点无后缀逗号” 的细节,保证输出美观。
11. 前插法建表(CreateList_H_NoHead
)
// 11. 前插法建表(不带头节点):每次插入到表头,最终元素逆序
void CreateList_H_NoHead(LinkList &L, int n) {L = NULL; // 先初始化空表for (int i = 0; i < n; i++) {LinkList p = new LNode; // 生成新节点cin >> p->data; // 输入节点数据p->next = L; // 新节点指向当前表头(空表时L=NULL)L = p; // 表头更新为新节点}
}
功能
通过 “前插法”(每次新节点插入到表头)创建不带头节点的单链表,最终链表元素顺序与输入顺序相反。
核心原理
前插法的核心是 “新节点始终成为新表头”:链表指针L
每次指向新节点,新节点的next
指向原表头,导致先输入的元素被后输入的元素 “挤到后面”。
详细步骤
- 初始化空表:
L = NULL
。 - 循环
n
次(n
为元素个数):- 为新节点分配内存:
LinkList p = new LNode
; - 输入节点数据:
cin >> p->data
; - 新节点指向原表头:
p->next = L
; - 链表指针指向新表头:
L = p
。
- 为新节点分配内存:
- 建表完成,
L
指向最终的首元节点。
时间复杂度
O(n)(循环n
次,每次操作均为O(1)
)。
关键说明
- 元素逆序示例:输入
1.5 2.8 3.2
,链表顺序为3.2 → 2.8 → 1.5
; - 优点是建表效率高(无需找表尾),缺点是元素顺序与输入相反,适合无需保持输入顺序的场景。
12. 后插法建表(CreateList_R_NoHead
)
// 12. 后插法建表(不带头节点):每次插入到表尾,最终元素正序
void CreateList_R_NoHead(LinkList &L, int n) {L = NULL; // 先初始化空表LinkList r = NULL; // 尾指针:始终指向当前表尾for (int i = 0; i < n; i++) {LinkList p = new LNode; // 生成新节点cin >> p->data; // 输入节点数据p->next = NULL; // 新节点为表尾,next=NULLif (L == NULL) {L = p; // 空表时,新节点即为首元,L指向首元} else {r->next = p; // 非空表时,尾指针后接新节点}r = p; // 尾指针更新为新节点(保持指向表尾)}
}
功能
通过 “后插法”(每次新节点插入到表尾)创建不带头节点的单链表,最终链表元素顺序与输入顺序一致。
核心原理
后插法需额外维护 “尾指针r
”(始终指向当前表尾),避免每次插入都遍历找表尾;新节点直接插入到r
之后,r
更新为新节点,保证元素顺序与输入一致。
详细步骤
- 初始化空表:
L = NULL
,尾指针r = NULL
。 - 循环
n
次:- 为新节点分配内存:
LinkList p = new LNode
; - 输入节点数据:
cin >> p->data
,设置p->next = NULL
(新节点为表尾,无后续节点); - 分场景插入:
- 空表时(
L == NULL
):L = p
(L
指向首元),r = p
(r
指向表尾); - 非空表时(
L != NULL
):r->next = p
(表尾后接新节点),r = p
(r
更新为新表尾)。
- 空表时(
- 为新节点分配内存:
- 建表完成,
L
指向首元,r
指向表尾。
时间复杂度
O(n)(循环n
次,每次操作均为O(1)
,尾指针避免了遍历找表尾)。
关键说明
- 元素正序示例:输入
1.5 2.8 3.2
,链表顺序为1.5 → 2.8 → 3.2
; - 优点是元素顺序与输入一致,适合需要保持输入顺序的场景;尾指针是后插法高效的关键(若无尾指针,时间复杂度会退化为
O(n²)
)。
完整C++代码如下:
#include
using namespace std;
// 不带头节点单链表的节点结构
typedef double ElemType; // 支持小数输入
typedef int Status;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
// 不带头节点单链表的节点结构
typedef struct LNode *LinkList;
struct LNode {ElemType data; // 数据域LinkList next; // 指针域:指向下一个节点
};
// -------------------------- 不带头节点单链表核心函数声明 --------------------------
Status InitList_NoHead(LinkList &L); // 初始化(不带头节点,空表时L=NULL)
Status GetElem_NoHead(LinkList L, int i, ElemType &e); // 取值(不带头节点)
LinkList LocateElem_NoHead(LinkList L, ElemType e); // 查找:返回节点地址(不带头节点)
int LocateELem_L_NoHead(LinkList L, ElemType e); // 修复:去掉多余的int i参数
Status ListInsert1(LinkList &L, int i, ElemType e); // 插入(不带头节点,i=1时插入首元)
Status ListDelete1(LinkList &L, int i); // 删除(不带头节点,i=1时删除首元)
int ListEmpty_NoHead(LinkList L); // 判空(不带头节点:L==NULL为空)
int ListLength_L_NoHead(LinkList L); // 求长(不带头节点:从L开始遍历)
Status DestroyList_NoHead(LinkList &L); // 销毁(不带头节点:遍历删除所有节点)
void PrintList_NoHead(LinkList L); // 打印(适配不带头节点:空表显示“空表”)
void CreateList_H_NoHead(LinkList &L, int n); // 前插法建表(不带头节点:每次插表头)
void CreateList_R_NoHead(LinkList &L, int n); // 后插法建表(不带头节点:需记录尾指针)
// -------------------------- 主函数 --------------------------
int main() {LinkList L_NoHead = NULL; // 不带头节点的链表指针(空表时为NULL)int choice, n, i, pos;ElemType e;bool isDestroyed_NoHead = false; // 标记链表是否已销毁(避免野指针操作)while (true) {// 不带头节点单链表专属测试菜单cout << "\n======= 不带头节点单链表测试菜单 =======" << endl;cout << "1. 初始化不带头节点链表" << endl;cout << "2. 建表测试(前插法/后插法)" << endl;cout << "3. 取值测试(根据位置获取元素)" << endl;cout << "4. 查找测试(根据元素找位置/地址)" << endl;cout << "5. 插入测试(指定位置插入元素)" << endl;cout << "6. 删除测试(指定位置删除元素)" << endl;cout << "7. 表属性测试(判空/求表长)" << endl;cout << "8. 销毁不带头节点链表" << endl;cout << "0. 退出程序" << endl;cout << "======================================" << endl;cout << "请输入选择(0-8):";cin >> choice;// 退出逻辑:销毁链表并释放内存if (choice == 0) {cout << "程序退出,释放所有内存..." << endl;if (!isDestroyed_NoHead) DestroyList_NoHead(L_NoHead);break;}// 1. 初始化不带头节点链表(空表时L_NoHead=NULL)if (choice == 1) {if (InitList_NoHead(L_NoHead) == OK) {cout << "不带头节点链表初始化成功!(空表时指针为NULL)" << endl;PrintList_NoHead(L_NoHead);isDestroyed_NoHead = false;} else {cout << "不带头节点链表初始化失败!" << endl;}continue;}// 2. 建表测试(前插法/后插法,不带头节点)if (choice == 2) {if (isDestroyed_NoHead) {cout << "链表已销毁,请先初始化!" << endl;continue;}int buildType;cout << "请选择建表方式:1-前插法(逆序) 2-后插法(正序):";cin >> buildType;cout << "请输入要插入的元素个数:";cin >> n;if (n <= 0) {cout << "元素个数必须为正整数!" << endl;continue;}cout << "请依次输入" << n << "个元素(支持小数,用空格分隔):";if (buildType == 1) {CreateList_H_NoHead(L_NoHead, n);cout << "前插法建表完成!";} else if (buildType == 2) {CreateList_R_NoHead(L_NoHead, n);cout << "后插法建表完成!";} else {cout << "无效建表方式!";continue;}PrintList_NoHead(L_NoHead);continue;}// 3. 取值测试(不带头节点:首元节点为L_NoHead)if (choice == 3) {if (isDestroyed_NoHead || ListEmpty_NoHead(L_NoHead)) {cout << "链表已销毁或为空,无法取值!" << endl;continue;}int len = ListLength_L_NoHead(L_NoHead);cout << "当前链表长度:" << len << endl;cout << "请输入要取值的位置i(1-" << len << "):";cin >> i;if (GetElem_NoHead(L_NoHead, i, e) == OK) {cout << "位置" << i << "的元素值为:" << e << endl;} else {cout << "取值失败!位置" << i << "不合法(超出表长或小于1)" << endl;}continue;}// 4. 查找测试(不带头节点:从L_NoHead开始遍历)if (choice == 4) {if (isDestroyed_NoHead || ListEmpty_NoHead(L_NoHead)) {cout << "链表已销毁或为空,无法查找!" << endl;continue;}cout << "请输入要查找的元素值(支持小数):";cin >> e;// 测试1:返回元素位序(调用修正后的函数,仅传2个参数)pos = LocateELem_L_NoHead(L_NoHead, e);if (pos != 0) {cout << "查找成功(位置序号):元素" << e << "在链表中的位置为" << pos << endl;} else {cout << "查找失败(位置序号):链表中无元素" << e << endl;}// 测试2:返回元素节点地址(简化为“是否找到”)LinkList addr = LocateElem_NoHead(L_NoHead, e);if (addr != NULL) {cout << "查找成功(地址验证):元素" << e << "的地址有效(存在该元素)" << endl;} else {cout << "查找失败(地址验证):元素" << e << "的地址为NULL(不存在)" << endl;}continue;}// 5. 插入测试(不带头节点:i=1时插入首元,需改链表指针)if (choice == 5) {if (isDestroyed_NoHead) {cout << "链表已销毁,请先初始化!" << endl;continue;}int len = ListLength_L_NoHead(L_NoHead);int maxPos = len + 1; // 空表时maxPos=1(插入首元)cout << "当前链表长度:" << len << endl;cout << "请输入插入位置i(1-" << maxPos << "):";cin >> pos;cout << "请输入插入的元素值(支持小数):";cin >> e;if (ListInsert1(L_NoHead, pos, e) == OK) {cout << "插入成功!插入后链表:";PrintList_NoHead(L_NoHead);} else {cout << "插入失败!位置" << pos << "不合法(需在1-" << maxPos << "之间)" << endl;}continue;}// 6. 删除测试(不带头节点:i=1时删除首元,需改链表指针)if (choice == 6) {if (isDestroyed_NoHead || ListEmpty_NoHead(L_NoHead)) {cout << "链表已销毁或为空,无法删除!" << endl;continue;}int len = ListLength_L_NoHead(L_NoHead);cout << "当前链表长度:" << len << endl;cout << "请输入删除位置i(1-" << len << "):";cin >> pos;if (ListDelete1(L_NoHead, pos) == OK) {cout << "删除成功!删除后链表:";PrintList_NoHead(L_NoHead);} else {cout << "删除失败!位置" << pos << "不合法(需在1-" << len << "之间)" << endl;}continue;}// 7. 表属性测试(判空+求长,适配不带头节点)if (choice == 7) {if (isDestroyed_NoHead) {cout << "链表已销毁,请先初始化!" << endl;continue;}cout << "不带头节点链表属性测试:" << endl;cout << "是否为空表:" << (ListEmpty_NoHead(L_NoHead) ? "是" : "否") << endl;cout << "当前表长:" << ListLength_L_NoHead(L_NoHead) << endl;cout << "当前链表元素:";PrintList_NoHead(L_NoHead);continue;}// 8. 销毁不带头节点链表(遍历删除所有节点,最后L_NoHead=NULL)if (choice == 8) {if (isDestroyed_NoHead) {cout << "不带头节点链表已销毁,无需重复操作!" << endl;continue;}if (DestroyList_NoHead(L_NoHead) == OK) {cout << "不带头节点链表销毁成功!" << endl;isDestroyed_NoHead = true;L_NoHead = NULL; // 确保野指针置空} else {cout << "不带头节点链表销毁失败!" << endl;}continue;}// 无效选择提示cout << "无效选择,请重新输入(0-8)!" << endl;}return 0;
}
// -------------------------- 不带头节点单链表核心函数实现 --------------------------
// 1. 初始化(不带头节点):空表时L=NULL
Status InitList_NoHead(LinkList &L) {L = NULL; // 不带头节点的空表,链表指针直接为NULL(无首元节点)return OK;
}
// 2. 取值(不带头节点):首元节点为L,位置i从1开始
Status GetElem_NoHead(LinkList L, int i, ElemType &e) {LinkList p = L; // 从首元节点开始遍历(不带头节点,无“头结点”可跳)int j = 1; // 计数器:当前指向第j个节点// 遍历到第i个节点,或p为NULL(位置超出表长)while (p != NULL && j < i) {p = p->next;j++;}// 位置不合法:i<1 或 j>i(p为NULL,未找到第i个节点)if (p == NULL || j > i) {return ERROR;}e = p->data; // 取出第i个节点的数据return OK;
}
// 3. 查找(不带头节点):返回元素e所在节点的地址,未找到返回NULL
LinkList LocateElem_NoHead(LinkList L, ElemType e) {LinkList p = L; // 从首元节点开始遍历// 遍历所有节点,对比数据域while (p != NULL && p->data != e) {p = p->next;}return p; // 找到则返回节点地址,未找到返回NULL
}
// 4. 查找(不带头节点):返回元素e的位序(1开始),未找到返回0(实现与声明一致)
int LocateELem_L_NoHead(LinkList L, ElemType e) {LinkList p = L; // 从首元节点开始遍历int j = 1; // 记录当前节点的位序// 遍历所有节点,对比数据域while (p != NULL && p->data != e) {p = p->next;j++;}return (p != NULL) ? j : 0; // 找到返回位序,未找到返回0
}
// 5. 插入(不带头节点):在第i个位置前插入元素e,i=1时插入首元
Status ListInsert1(LinkList &L, int i, ElemType e) {// 位置合法性初步判断:i<1 或 空表时i>1(空表只能插入第1个位置)if (i < 1 || (L == NULL && i > 1)) {return ERROR;}LinkList s = new LNode; // 生成新节点s->data = e; // 赋值新节点数据域// 情况1:插入首元节点(i=1,此时L为NULL或指向原首元)if (i == 1) {s->next = L; // 新节点指向原首元(空表时L=NULL,新节点next=NULL)L = s; // 链表指针指向新节点(新首元)return OK;}// 情况2:插入非首元节点(i>1):先找第i-1个节点LinkList p = L;int j = 1;while (p != NULL && j < i - 1) {p = p->next;j++;}// 位置不合法:未找到第i-1个节点(i超出表长+1)if (p == NULL || j > i - 1) {delete s; // 释放未插入的新节点,避免内存泄漏return ERROR;}// 插入新节点:第i-1个节点后接新节点,新节点接原第i个节点s->next = p->next;p->next = s;return OK;
}
// 6. 删除(不带头节点):删除第i个位置的元素,i=1时删除首元
Status ListDelete1(LinkList &L, int i) {// 位置合法性初步判断:i<1 或 空表(L=NULL)if (i < 1 || L == NULL) {return ERROR;}LinkList q; // 暂存待删除节点// 情况1:删除首元节点(i=1)if (i == 1) {q = L; // 待删除节点为当前首元L = L->next; // 链表指针指向原首元的下一个节点(新首元,空表时为NULL)delete q; // 释放原首元节点内存return OK;}// 情况2:删除非首元节点(i>1):先找第i-1个节点LinkList p = L;int j = 1;while (p != NULL && j < i - 1) {p = p->next;j++;}// 位置不合法:未找到第i-1个节点,或第i个节点不存在(p->next=NULL)if (p == NULL || p->next == NULL || j > i - 1) {return ERROR;}// 删除第i个节点:第i-1个节点跳过待删除节点,释放待删除节点内存q = p->next;p->next = q->next;delete q;return OK;
}
// 7. 判空(不带头节点):L==NULL即为空表
int ListEmpty_NoHead(LinkList L) {return (L == NULL) ? 1 : 0; // 1-空表,0-非空表
}
// 8. 求长(不带头节点):从首元节点L开始遍历,统计节点数
int ListLength_L_NoHead(LinkList L) {int len = 0;LinkList p = L;while (p != NULL) {len++;p = p->next;}return len;
}
// 9. 销毁(不带头节点):遍历删除所有节点,最后L=NULL
Status DestroyList_NoHead(LinkList &L) {LinkList p;while (L != NULL) {p = L; // 暂存当前节点L = L->next; // 链表指针后移delete p; // 释放当前节点内存}L = NULL; // 确保销毁后链表指针为NULL,避免野指针return OK;
}
// 10. 打印(不带头节点):适配空表(L=NULL)、非空表(从L开始遍历)
void PrintList_NoHead(LinkList L) {if (L == NULL) {cout << "空表" << endl;return;}// 非空表:从首元节点L开始遍历打印cout << "[";LinkList p = L;while (p != NULL) {cout << p->data;if (p->next != NULL) {cout << ", ";}p = p->next;}cout << "]" << endl;
}
// 11. 前插法建表(不带头节点):每次插入到表头,最终元素逆序
void CreateList_H_NoHead(LinkList &L, int n) {L = NULL; // 先初始化空表for (int i = 0; i < n; i++) {LinkList p = new LNode; // 生成新节点cin >> p->data; // 输入节点数据p->next = L; // 新节点指向当前表头(空表时L=NULL)L = p; // 表头更新为新节点}
}
// 12. 后插法建表(不带头节点):每次插入到表尾,最终元素正序
void CreateList_R_NoHead(LinkList &L, int n) {L = NULL; // 先初始化空表LinkList r = NULL; // 尾指针:始终指向当前表尾for (int i = 0; i < n; i++) {LinkList p = new LNode; // 生成新节点cin >> p->data; // 输入节点数据p->next = NULL; // 新节点为表尾,next=NULLif (L == NULL) {L = p; // 空表时,新节点即为首元,L指向首元} else {r->next = p; // 非空表时,尾指针后接新节点}r = p; // 尾指针更新为新节点(保持指向表尾)}
}
完整Python代码如下:
# 定义状态常量(对应原C++的Status)
OK = 1
ERROR = 0
OVERFLOW = -2
# 1. 节点类(对应原C++的LNode结构体)
class Node:def __init__(self, data=None):self.data = data # 数据域(支持float,对应原C++的double)self.next = None # 引用域(替代指针,指向下一个节点)
# 2. 不带头节点单链表类(封装所有核心操作)
class LinkedListNoHead:def __init__(self):"""初始化:不带头节点的空链表(head=None表示空表)"""self.head = None # 链表头引用(直接指向首元节点,空表时为None)self.is_destroyed = False # 标记链表是否已销毁def InitList_NoHead(self):"""重新初始化(对应原InitList_NoHead,空表时head=None)"""if self.is_destroyed:self.head = Noneself.is_destroyed = Falsereturn OKdef GetElem_NoHead(self, i):"""取值:获取第i个位置(1-based)的元素,返回(状态, 数据)"""if self.is_destroyed or self.head is None:return (ERROR, None) # 链表已销毁或空表p = self.head # 从首元节点开始遍历j = 1 # 计数器:当前指向第j个节点# 遍历到第i个节点或p为None(位置超出表长)while p is not None and j < i:p = p.nextj += 1# 位置不合法:i<1或未找到第i个节点if p is None or j > i:return (ERROR, None)return (OK, p.data)def LocateElem_NoHead(self, e):"""查找:返回第一个值为e的节点对象(对应原指针),未找到返回None"""if self.is_destroyed or self.head is None:return Nonep = self.headwhile p is not None and p.data != e:p = p.nextreturn p # 找到返回节点,未找到返回Nonedef LocateELem_L_NoHead(self, e):"""查找:返回第一个值为e的节点位置(1-based),未找到返回0"""if self.is_destroyed or self.head is None:return 0p = self.headj = 1 # 位置计数器while p is not None and p.data != e:p = p.nextj += 1return j if p is not None else 0def ListInsert1(self, i, e):"""插入:在第i个位置前插入元素e,返回状态(OK/ERROR)"""# 合法性初步判断:i<1 或 空表时i>1(空表只能插第1个位置)if i < 1 or (self.head is None and i > 1):return ERROR# 创建新节点(Python自动管理内存,无需手动分配)new_node = Node(e)# 场景1:插入首元节点(i=1)if i == 1:new_node.next = self.head # 新节点指向原首元(空表时为None)self.head = new_node # 头引用指向新首元return OK# 场景2:插入非首元节点(i>1):找第i-1个节点p = self.headj = 1while p is not None and j < i - 1:p = p.nextj += 1# 位置不合法:未找到第i-1个节点(i超出表长+1)if p is None or j > i - 1:return ERROR# 插入新节点:建立链接new_node.next = p.nextp.next = new_nodereturn OKdef ListDelete1(self, i):"""删除:删除第i个位置的元素,返回状态(OK/ERROR)"""# 合法性初步判断:i<1 或 空表if i < 1 or self.head is None or self.is_destroyed:return ERROR# 场景1:删除首元节点(i=1)if i == 1:self.head = self.head.next # 头引用指向原首元的下一个节点# Python垃圾回收自动释放原首元节点内存return OK# 场景2:删除非首元节点(i>1):找第i-1个节点p = self.headj = 1while p is not None and j < i - 1:p = p.nextj += 1# 位置不合法:未找到第i-1个节点 或 第i个节点不存在if p is None or p.next is None or j > i - 1:return ERROR# 跳过待删除节点(自动回收内存)p.next = p.next.nextreturn OKdef ListEmpty_NoHead(self):"""判空:返回1(空表)或0(非空表),对应原C++逻辑"""return 1 if (self.head is None and not self.is_destroyed) else 0def ListLength_L_NoHead(self):"""求长:返回链表节点个数(表长)"""if self.is_destroyed or self.head is None:return 0count = 0p = self.headwhile p is not None:count += 1p = p.nextreturn countdef DestroyList_NoHead(self):"""销毁:释放链表所有节点(Python自动回收,只需置空head)"""self.head = Noneself.is_destroyed = Truereturn OKdef PrintList_NoHead(self):"""打印:适配空表/非空表,格式为[元素1, 元素2, ...]"""if self.is_destroyed:print("已销毁", end="")returnif self.head is None:print("空表", end="")return# 遍历打印非空表print("[", end="")p = self.headwhile p is not None:print(p.data, end="")if p.next is not None:print(", ", end="")p = p.nextprint("]", end="")def CreateList_H_NoHead(self, n):"""前插法建表:每次插表头,元素顺序与输入相反"""if self.is_destroyed:returnself.head = None # 先初始化空表print(f"请依次输入{n}个元素(支持小数,用空格分隔):", end="")elements = list(map(float, input().split()))# 检查输入元素个数是否匹配nif len(elements) != n:print("输入元素个数不匹配!建表失败。")self.head = Nonereturn# 前插:新节点始终成为首元for e in elements:new_node = Node(e)new_node.next = self.headself.head = new_nodedef CreateList_R_NoHead(self, n):"""后插法建表:每次插表尾,元素顺序与输入一致"""if self.is_destroyed:returnself.head = Nonetail = None # 尾指针:始终指向当前表尾print(f"请依次输入{n}个元素(支持小数,用空格分隔):", end="")elements = list(map(float, input().split()))if len(elements) != n:print("输入元素个数不匹配!建表失败。")self.head = Nonereturn# 后插:新节点追加到表尾for e in elements:new_node = Node(e)if self.head is None:self.head = new_node # 空表时,新节点即为首元tail = new_nodeelse:tail.next = new_node # 非空表时,尾指针后接新节点tail = new_node # 更新尾指针
# 3. 主函数:菜单驱动测试(与原C++ main逻辑完全一致)
def main():ll = LinkedListNoHead() # 实例化不带头节点链表while True:# 打印菜单print("\n======= 不带头节点单链表测试菜单 =======")print("1. 初始化不带头节点链表")print("2. 建表测试(前插法/后插法)")print("3. 取值测试(根据位置获取元素)")print("4. 查找测试(根据元素找位置/地址)")print("5. 插入测试(指定位置插入元素)")print("6. 删除测试(指定位置删除元素)")print("7. 表属性测试(判空/求表长)")print("8. 销毁不带头节点链表")print("0. 退出程序")print("======================================")# 获取用户选择try:choice = int(input("请输入选择(0-8):"))except ValueError:print("无效输入!请输入整数(0-8)。")continue# 退出逻辑if choice == 0:print("程序退出,释放所有内存...")if not ll.is_destroyed:ll.DestroyList_NoHead()break# 1. 初始化elif choice == 1:if ll.InitList_NoHead() == OK:print("不带头节点链表初始化成功!(空表时指针为NULL)")ll.PrintList_NoHead()print() # 换行else:print("不带头节点链表初始化失败!")# 2. 建表测试elif choice == 2:if ll.is_destroyed:print("链表已销毁,请先初始化!")continuetry:build_type = int(input("请选择建表方式:1-前插法(逆序) 2-后插法(正序):"))n = int(input("请输入要插入的元素个数:"))except ValueError:print("无效输入!个数和方式需为整数。")continueif n <= 0:print("元素个数必须为正整数!")continueif build_type == 1:ll.CreateList_H_NoHead(n)print("前插法建表完成!", end="")elif build_type == 2:ll.CreateList_R_NoHead(n)print("后插法建表完成!", end="")else:print("无效建表方式!", end="")ll.PrintList_NoHead()print() # 换行# 3. 取值测试elif choice == 3:if ll.is_destroyed or ll.ListEmpty_NoHead() == 1:print("链表已销毁或为空,无法取值!")continuelen_ll = ll.ListLength_L_NoHead()print(f"当前链表长度:{len_ll}")try:i = int(input(f"请输入要取值的位置i(1-{len_ll}):"))except ValueError:print("无效输入!位置需为整数。")continuestatus, e = ll.GetElem_NoHead(i)if status == OK:print(f"位置{i}的元素值为:{e}")else:print(f"取值失败!位置{i}不合法(超出表长或小于1)")# 4. 查找测试elif choice == 4:if ll.is_destroyed or ll.ListEmpty_NoHead() == 1:print("链表已销毁或为空,无法查找!")continuetry:e = float(input("请输入要查找的元素值(支持小数):"))except ValueError:print("无效输入!元素需为数字。")continue# 测试1:返回位置pos = ll.LocateELem_L_NoHead(e)if pos != 0:print(f"查找成功(位置序号):元素{e}在链表中的位置为{pos}")else:print(f"查找失败(位置序号):链表中无元素{e}")# 测试2:返回节点地址(简化为是否找到)node = ll.LocateElem_NoHead(e)if node is not None:print(f"查找成功(地址验证):元素{e}的地址有效(存在该元素)")else:print(f"查找失败(地址验证):元素{e}的地址为NULL(不存在)")# 5. 插入测试elif choice == 5:if ll.is_destroyed:print("链表已销毁,请先初始化!")continuelen_ll = ll.ListLength_L_NoHead()max_pos = len_ll + 1 # 空表时max_pos=1print(f"当前链表长度:{len_ll}")try:pos = int(input(f"请输入插入位置i(1-{max_pos}):"))e = float(input("请输入插入的元素值(支持小数):"))except ValueError:print("无效输入!位置需为整数,元素需为数字。")continuestatus = ll.ListInsert1(pos, e)if status == OK:print("插入成功!插入后链表:", end="")ll.PrintList_NoHead()print()else:print(f"插入失败!位置{pos}不合法(需在1-{max_pos}之间)")# 6. 删除测试elif choice == 6:if ll.is_destroyed or ll.ListEmpty_NoHead() == 1:print("链表已销毁或为空,无法删除!")continuelen_ll = ll.ListLength_L_NoHead()print(f"当前链表长度:{len_ll}")try:pos = int(input(f"请输入删除位置i(1-{len_ll}):"))except ValueError:print("无效输入!位置需为整数。")continuestatus = ll.ListDelete1(pos)if status == OK:print("删除成功!删除后链表:", end="")ll.PrintList_NoHead()print()else:print(f"删除失败!位置{pos}不合法(需在1-{len_ll}之间)")# 7. 表属性测试elif choice == 7:if ll.is_destroyed:print("链表已销毁,请先初始化!")continueis_empty = ll.ListEmpty_NoHead()len_ll = ll.ListLength_L_NoHead()print("不带头节点链表属性测试:")print(f"是否为空表:{'是' if is_empty == 1 else '否'}")print(f"当前表长:{len_ll}")print("当前链表元素:", end="")ll.PrintList_NoHead()print()# 8. 销毁测试elif choice == 8:if ll.is_destroyed:print("不带头节点链表已销毁,无需重复操作!")continueif ll.DestroyList_NoHead() == OK:print("不带头节点链表销毁成功!")ll.is_destroyed = Trueelse:print("不带头节点链表销毁失败!")# 无效选择else:print("无效选择,请重新输入(0-8)!")
if __name__ == "__main__":main()
完整Java代码如下:
import java.util.Scanner;
// 1. 结果封装类:解决Java无引用传参问题(封装状态码+数据)
class Result {private int status; // 状态码:OK=1,ERROR=0private double data; // 取值结果(仅status=OK时有效)// 构造器:无数据(如插入、删除操作)public Result(int status) {this.status = status;}// 构造器:带数据(如取值操作)public Result(int status, double data) {this.status = status;this.data = data;}// Getter方法public int getStatus() {return status;}public double getData() {return data;}
}
// 2. 节点类:不带头节点单链表的节点结构(对应原C++的LNode)
class Node {double data; // 数据域(支持小数,对应原C++的ElemType)Node next; // 引用域(替代C++指针,指向下一个节点)// 构造器:初始化数据public Node(double data) {this.data = data;this.next = null;}// 构造器:空节点(仅用于临时占位)public Node() {this.data = 0.0;this.next = null;}
}
// 3. 核心类:不带头节点单链表的所有操作+菜单测试
public class LinkedListNoHead {// 状态常量(对应原C++的宏定义)public static final int OK = 1;public static final int ERROR = 0;public static final int OVERFLOW = -2;private Node head; // 链表头引用(指向首元节点,空表时为null)private boolean isDestroyed; // 标记链表是否已销毁private Scanner scanner; // 输入工具(替代C++的cin)// 构造器:初始化空链表public LinkedListNoHead() {this.head = null; // 不带头节点,空表标识为head=nullthis.isDestroyed = false;this.scanner = new Scanner(System.in);}// -------------------------- 核心操作方法(与原C++一一对应) --------------------------/*** 1. 初始化:空表时head=null(对应原C++ InitList_NoHead)*/public int InitList_NoHead() {if (isDestroyed) {this.head = null;this.isDestroyed = false;}return OK;}/*** 2. 取值:获取第i个位置(1-based)的元素(对应原C++ GetElem_NoHead)*/public Result GetElem_NoHead(int i) {if (isDestroyed || head == null) {return new Result(ERROR);}Node p = head; // 从首元节点开始遍历int j = 1; // 位置计数器// 遍历到第i个节点或超出表长while (p != null && j < i) {p = p.next;j++;}// 位置不合法(i<1或无第i个节点)if (p == null || j > i) {return new Result(ERROR);}return new Result(OK, p.data);}/*** 3. 查找:返回第一个值为e的节点引用(对应原C++ LocateElem_NoHead)*/public Node LocateElem_NoHead(double e) {if (isDestroyed || head == null) {return null;}Node p = head;while (p != null && p.data != e) {p = p.next;}return p; // 找到返回节点,未找到返回null}/*** 4. 查找:返回第一个值为e的节点位置(1-based,对应原C++ LocateELem_L_NoHead)*/public int LocateELem_L_NoHead(double e) {if (isDestroyed || head == null) {return 0;}Node p = head;int j = 1;while (p != null && p.data != e) {p = p.next;j++;}return (p != null) ? j : 0;}/*** 5. 插入:在第i个位置前插入元素e(对应原C++ ListInsert1)*/public int ListInsert1(int i, double e) {// 空表只能插入第1个位置,i<1非法if (i < 1 || (head == null && i > 1)) {return ERROR;}Node newNode = new Node(e);// 场景1:插入首元节点(i=1)if (i == 1) {newNode.next = head;head = newNode;return OK;}// 场景2:插入非首元节点(找第i-1个节点)Node p = head;int j = 1;while (p != null && j < i - 1) {p = p.next;j++;}// 未找到第i-1个节点(i超出表长+1)if (p == null || j > i - 1) {return ERROR;}// 建立新节点链接newNode.next = p.next;p.next = newNode;return OK;}/*** 6. 删除:删除第i个位置的元素(对应原C++ ListDelete1)*/public int ListDelete1(int i) {if (i < 1 || head == null || isDestroyed) {return ERROR;}// 场景1:删除首元节点(i=1)if (i == 1) {head = head.next; // 头引用指向新首元(空表时为null)return OK;}// 场景2:删除非首元节点(找第i-1个节点)Node p = head;int j = 1;while (p != null && j < i - 1) {p = p.next;j++;}// 未找到第i-1个节点或无第i个节点if (p == null || p.next == null || j > i - 1) {return ERROR;}// 跳过待删除节点(GC自动回收)p.next = p.next.next;return OK;}/*** 7. 判空:空表返回1,非空返回0(对应原C++ ListEmpty_NoHead)*/public int ListEmpty_NoHead() {return (head == null && !isDestroyed) ? 1 : 0;}/*** 8. 求长:返回链表节点个数(对应原C++ ListLength_L_NoHead)*/public int ListLength_L_NoHead() {if (isDestroyed || head == null) {return 0;}int count = 0;Node p = head;while (p != null) {count++;p = p.next;}return count;}/*** 9. 销毁:释放所有节点(GC自动处理,对应原C++ DestroyList_NoHead)*/public int DestroyList_NoHead() {head = null; // 断开头引用,节点无引用后被回收isDestroyed = true;return OK;}/*** 10. 打印:空表输出“空表”,非空表输出[元素1, 元素2, ...](对应原C++ PrintList_NoHead)*/public void PrintList_NoHead() {if (isDestroyed) {System.out.print("已销毁");return;}if (head == null) {System.out.print("空表");return;}System.out.print("[");Node p = head;while (p != null) {System.out.print(p.data);if (p.next != null) {System.out.print(", ");}p = p.next;}System.out.print("]");}/*** 11. 前插法建表:元素顺序与输入相反(对应原C++ CreateList_H_NoHead)*/public void CreateList_H_NoHead(int n) {if (isDestroyed) {System.out.println("链表已销毁,请先初始化!");return;}head = null;System.out.printf("请依次输入%d个元素(支持小数,用空格分隔):", n);double[] elements = new double[n];for (int i = 0; i < n; i++) {elements[i] = scanner.nextDouble();}// 新节点始终插入表头for (double e : elements) {Node newNode = new Node(e);newNode.next = head;head = newNode;}}/*** 12. 后插法建表:元素顺序与输入一致(对应原C++ CreateList_R_NoHead)*/public void CreateList_R_NoHead(int n) {if (isDestroyed) {System.out.println("链表已销毁,请先初始化!");return;}head = null;Node tail = null; // 尾指针:始终指向表尾System.out.printf("请依次输入%d个元素(支持小数,用空格分隔):", n);double[] elements = new double[n];for (int i = 0; i < n; i++) {elements[i] = scanner.nextDouble();}// 新节点追加到表尾for (double e : elements) {Node newNode = new Node(e);if (head == null) {head = newNode; // 空表时新节点为首选tail = newNode;} else {tail.next = newNode;tail = newNode;}}}// -------------------------- 菜单驱动测试(对应原C++ main函数) --------------------------public static void main(String[] args) {LinkedListNoHead ll = new LinkedListNoHead();int choice, n, i, pos;double e;while (true) {// 打印测试菜单System.out.println("\n======= 不带头节点单链表测试菜单 =======");System.out.println("1. 初始化不带头节点链表");System.out.println("2. 建表测试(前插法/后插法)");System.out.println("3. 取值测试(根据位置获取元素)");System.out.println("4. 查找测试(根据元素找位置/地址)");System.out.println("5. 插入测试(指定位置插入元素)");System.out.println("6. 删除测试(指定位置删除元素)");System.out.println("7. 表属性测试(判空/求表长)");System.out.println("8. 销毁不带头节点链表");System.out.println("0. 退出程序");System.out.println("======================================");System.out.print("请输入选择(0-8):");// 读取用户选择(处理非整数输入)try {choice = ll.scanner.nextInt();} catch (Exception ex) {System.out.println("无效输入!请输入整数(0-8)。");ll.scanner.next(); // 清除无效输入缓存continue;}// 退出逻辑:销毁链表+关闭输入流if (choice == 0) {System.out.println("程序退出,释放所有内存...");if (!ll.isDestroyed) {ll.DestroyList_NoHead();}ll.scanner.close();break;}// 1. 初始化操作if (choice == 1) {if (ll.InitList_NoHead() == OK) {System.out.println("不带头节点链表初始化成功!(空表时指针为NULL)");ll.PrintList_NoHead();System.out.println();} else {System.out.println("不带头节点链表初始化失败!");}continue;}// 2. 建表操作if (choice == 2) {if (ll.isDestroyed) {System.out.println("链表已销毁,请先初始化!");continue;}// 选择建表方式System.out.print("请选择建表方式:1-前插法(逆序) 2-后插法(正序):");int buildType;try {buildType = ll.scanner.nextInt();} catch (Exception ex) {System.out.println("无效输入!方式需为整数(1或2)。");ll.scanner.next();continue;}// 输入元素个数System.out.print("请输入要插入的元素个数:");try {n = ll.scanner.nextInt();} catch (Exception ex) {System.out.println("无效输入!个数需为整数。");ll.scanner.next();continue;}if (n <= 0) {System.out.println("元素个数必须为正整数!");continue;}// 执行建表并打印if (buildType == 1) {ll.CreateList_H_NoHead(n);System.out.print("前插法建表完成!");} else if (buildType == 2) {ll.CreateList_R_NoHead(n);System.out.print("后插法建表完成!");} else {System.out.print("无效建表方式!");continue;}ll.PrintList_NoHead();System.out.println();continue;}// 3. 取值操作if (choice == 3) {if (ll.isDestroyed || ll.ListEmpty_NoHead() == 1) {System.out.println("链表已销毁或为空,无法取值!");continue;}int len = ll.ListLength_L_NoHead();System.out.printf("当前链表长度:%d\n", len);System.out.printf("请输入要取值的位置i(1-%d):", len);try {i = ll.scanner.nextInt();} catch (Exception ex) {System.out.println("无效输入!位置需为整数。");ll.scanner.next();continue;}Result result = ll.GetElem_NoHead(i);if (result.getStatus() == OK) {System.out.printf("位置%d的元素值为:%f\n", i, result.getData());} else {System.out.printf("取值失败!位置%d不合法(超出表长或小于1)\n", i);}continue;}// 4. 查找操作if (choice == 4) {if (ll.isDestroyed || ll.ListEmpty_NoHead() == 1) {System.out.println("链表已销毁或为空,无法查找!");continue;}System.out.print("请输入要查找的元素值(支持小数):");try {e = ll.scanner.nextDouble();} catch (Exception ex) {System.out.println("无效输入!元素需为数字。");ll.scanner.next();continue;}// 测试1:返回位置pos = ll.LocateELem_L_NoHead(e);if (pos != 0) {System.out.printf("查找成功(位置序号):元素%f在链表中的位置为%d\n", e, pos);} else {System.out.printf("查找失败(位置序号):链表中无元素%f\n", e);}// 测试2:返回节点引用(简化为是否找到)Node node = ll.LocateElem_NoHead(e);if (node != null) {System.out.printf("查找成功(地址验证):元素%f的地址有效(存在该元素)\n", e);} else {System.out.printf("查找失败(地址验证):元素%f的地址为NULL(不存在)\n", e);}continue;}// 5. 插入操作if (choice == 5) {if (ll.isDestroyed) {System.out.println("链表已销毁,请先初始化!");continue;}int len = ll.ListLength_L_NoHead();int maxPos = len + 1; // 空表时maxPos=1System.out.printf("当前链表长度:%d\n", len);System.out.printf("请输入插入位置i(1-%d):", maxPos);// 读取插入位置try {pos = ll.scanner.nextInt();} catch (Exception ex) {System.out.println("无效输入!位置需为整数。");ll.scanner.next();continue;}// 读取插入元素System.out.print("请输入插入的元素值(支持小数):");try {e = ll.scanner.nextDouble();} catch (Exception ex) {System.out.println("无效输入!元素需为数字。");ll.scanner.next();continue;}// 执行插入并打印int insertStatus = ll.ListInsert1(pos, e);if (insertStatus == OK) {System.out.print("插入成功!插入后链表:");ll.PrintList_NoHead();System.out.println();} else {System.out.printf("插入失败!位置%d不合法(需在1-%d之间)\n", pos, maxPos);}continue;}// 6. 删除操作if (choice == 6) {if (ll.isDestroyed || ll.ListEmpty_NoHead() == 1) {System.out.println("链表已销毁或为空,无法删除!");continue;}int len = ll.ListLength_L_NoHead();System.out.printf("当前链表长度:%d\n", len);System.out.printf("请输入删除位置i(1-%d):", len);try {pos = ll.scanner.nextInt();} catch (Exception ex) {System.out.println("无效输入!位置需为整数。");ll.scanner.next();continue;}// 执行删除并打印int deleteStatus = ll.ListDelete1(pos);if (deleteStatus == OK) {System.out.print("删除成功!删除后链表:");ll.PrintList_NoHead();System.out.println();} else {System.out.printf("删除失败!位置%d不合法(需在1-%d之间)\n", pos, len);}continue;}// 7. 表属性测试if (choice == 7) {if (ll.isDestroyed) {System.out.println("链表已销毁,请先初始化!");continue;}int isEmpty = ll.ListEmpty_NoHead();int len = ll.ListLength_L_NoHead();System.out.println("不带头节点链表属性测试:");System.out.printf("是否为空表:%s\n", isEmpty == 1 ? "是" : "否");System.out.printf("当前表长:%d\n", len);System.out.print("当前链表元素:");ll.PrintList_NoHead();System.out.println();continue;}// 8. 销毁操作if (choice == 8) {if (ll.isDestroyed) {System.out.println("不带头节点链表已销毁,无需重复操作!");continue;}if (ll.DestroyList_NoHead() == OK) {System.out.println("不带头节点链表销毁成功!");ll.isDestroyed = true;} else {System.out.println("不带头节点链表销毁失败!");}continue;}// 无效选择System.out.println("无效选择,请重新输入(0-8)!");}}
}
程序运行结果如下:
======= 不带头节点单链表测试菜单 =======
1. 初始化不带头节点链表
2. 建表测试(前插法/后插法)
3. 取值测试(根据位置获取元素)
4. 查找测试(根据元素找位置/地址)
5. 插入测试(指定位置插入元素)
6. 删除测试(指定位置删除元素)
7. 表属性测试(判空/求表长)
8. 销毁不带头节点链表
0. 退出程序
======================================
请输入选择(0-8):1
不带头节点链表初始化成功!(空表时指针为NULL)
空表
======= 不带头节点单链表测试菜单 =======
1. 初始化不带头节点链表
2. 建表测试(前插法/后插法)
3. 取值测试(根据位置获取元素)
4. 查找测试(根据元素找位置/地址)
5. 插入测试(指定位置插入元素)
6. 删除测试(指定位置删除元素)
7. 表属性测试(判空/求表长)
8. 销毁不带头节点链表
0. 退出程序
======================================
请输入选择(0-8):2
请选择建表方式:1-前插法(逆序) 2-后插法(正序):2
请输入要插入的元素个数:6
请依次输入6个元素(支持小数,用空格分隔):34 12 98 67 43 71
后插法建表完成![34, 12, 98, 67, 43, 71]
======= 不带头节点单链表测试菜单 =======
1. 初始化不带头节点链表
2. 建表测试(前插法/后插法)
3. 取值测试(根据位置获取元素)
4. 查找测试(根据元素找位置/地址)
5. 插入测试(指定位置插入元素)
6. 删除测试(指定位置删除元素)
7. 表属性测试(判空/求表长)
8. 销毁不带头节点链表
0. 退出程序
======================================
请输入选择(0-8):3
当前链表长度:6
请输入要取值的位置i(1-6):2
位置2的元素值为:12
======= 不带头节点单链表测试菜单 =======
1. 初始化不带头节点链表
2. 建表测试(前插法/后插法)
3. 取值测试(根据位置获取元素)
4. 查找测试(根据元素找位置/地址)
5. 插入测试(指定位置插入元素)
6. 删除测试(指定位置删除元素)
7. 表属性测试(判空/求表长)
8. 销毁不带头节点链表
0. 退出程序
======================================
请输入选择(0-8):4
请输入要查找的元素值(支持小数):67
查找成功(位置序号):元素67在链表中的位置为4
查找成功(地址验证):元素67的地址有效(存在该元素)
======= 不带头节点单链表测试菜单 =======
1. 初始化不带头节点链表
2. 建表测试(前插法/后插法)
3. 取值测试(根据位置获取元素)
4. 查找测试(根据元素找位置/地址)
5. 插入测试(指定位置插入元素)
6. 删除测试(指定位置删除元素)
7. 表属性测试(判空/求表长)
8. 销毁不带头节点链表
0. 退出程序
======================================
请输入选择(0-8):5
当前链表长度:6
请输入插入位置i(1-7):4
请输入插入的元素值(支持小数):87
插入成功!插入后链表:[34, 12, 98, 87, 67, 43, 71]
======= 不带头节点单链表测试菜单 =======
1. 初始化不带头节点链表
2. 建表测试(前插法/后插法)
3. 取值测试(根据位置获取元素)
4. 查找测试(根据元素找位置/地址)
5. 插入测试(指定位置插入元素)
6. 删除测试(指定位置删除元素)
7. 表属性测试(判空/求表长)
8. 销毁不带头节点链表
0. 退出程序
======================================
请输入选择(0-8):6
当前链表长度:7
请输入删除位置i(1-7):3
删除成功!删除后链表:[34, 12, 87, 67, 43, 71]
======= 不带头节点单链表测试菜单 =======
1. 初始化不带头节点链表
2. 建表测试(前插法/后插法)
3. 取值测试(根据位置获取元素)
4. 查找测试(根据元素找位置/地址)
5. 插入测试(指定位置插入元素)
6. 删除测试(指定位置删除元素)
7. 表属性测试(判空/求表长)
8. 销毁不带头节点链表
0. 退出程序
======================================
请输入选择(0-8):7
不带头节点链表属性测试:
是否为空表:否
当前表长:6
当前链表元素:[34, 12, 87, 67, 43, 71]
======= 不带头节点单链表测试菜单 =======
1. 初始化不带头节点链表
2. 建表测试(前插法/后插法)
3. 取值测试(根据位置获取元素)
4. 查找测试(根据元素找位置/地址)
5. 插入测试(指定位置插入元素)
6. 删除测试(指定位置删除元素)
7. 表属性测试(判空/求表长)
8. 销毁不带头节点链表
0. 退出程序
======================================
请输入选择(0-8):8
不带头节点链表销毁成功!
======= 不带头节点单链表测试菜单 =======
1. 初始化不带头节点链表
2. 建表测试(前插法/后插法)
3. 取值测试(根据位置获取元素)
4. 查找测试(根据元素找位置/地址)
5. 插入测试(指定位置插入元素)
6. 删除测试(指定位置删除元素)
7. 表属性测试(判空/求表长)
8. 销毁不带头节点链表
0. 退出程序
======================================
请输入选择(0-8):0
程序退出,释放所有内存...
总结:不带头节点单链表算法的核心特点
特点 | 说明 |
---|---|
空表标识 | L == NULL (无任何节点),区别于带头节点的head->next == NULL |
操作首元需特殊处理 | 插入 / 删除首元时需修改L 指针,非首元操作需找i-1 节点 |
内存效率 | 节省一个头节点的内存,适合内存受限场景 |
边界条件更复杂 | 空表插入只能i=1 ,删除首元后需确保L 置为NULL (避免野指针) |
遍历起点 | 所有遍历操作(取值、查找、求长)均从L 开始,无需跳过头节点 |
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/923710.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!