代码改变世界

深入解析:【数据结构】不带头节点单链表的基本操作

2025-10-01 09:21  tlnshuju  阅读(23)  评论(0)    收藏  举报

1. 初始化算法(InitList_NoHead

// 1. 初始化(不带头节点):空表时L=NULL
Status InitList_NoHead(LinkList &L) {
    L = NULL; // 不带头节点的空表,链表指针直接为NULL(无首元节点)
    return OK;
}

功能

创建一个不带头节点的空单链表(空表时无任何节点,链表指针L直接为NULL)。

核心原理

不带头节点的单链表无 “占位头节点”,空表的唯一标识是链表指针L指向NULL(区别于带头节点的空表:头节点存在,head->next=NULL)。

详细步骤

  1. 直接将链表指针L赋值为NULLL = NULL(表示无首元节点,链表为空)。
  2. 返回成功状态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超出表长)。

详细步骤

  1. 初始化指针p = L(指向首元节点),计数器j = 1(首元节点为第 1 个位置)。
  2. 循环遍历:若p != NULLj < i,则p = p->next(指针后移)、j++(计数器加 1)。
  3. 合法性判断:
    • p == NULL(未找到第i个节点,i超出表长)或j > ii < 1,位置非法),返回ERROR
    • 否则,将目标节点的值存入ee = 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

详细步骤

  1. 初始化指针p = L(指向首元节点)。
  2. 循环遍历:若p != NULLp->data != e,则p = p->next(指针后移)。
  3. 返回p:若找到匹配节点,p指向该节点;否则pNULL

时间复杂度

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

详细步骤

  1. 初始化指针p = L(指向首元节点),计数器j = 1
  2. 循环遍历:若p != NULLp->data != e,则p = p->nextj++
  3. 返回结果:若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节点” 的链接。

详细步骤

  1. 合法性初步判断:若i < 1,或空表(L=NULL)时i > 1(空表只能插入第 1 个位置),返回ERROR
  2. 为新节点分配内存:LinkList s = new LNode;,赋值s->data = e
  3. 分场景插入:
    • 场景 1:插入首元(i=1 :
      • 新节点指向原首元:s->next = L(空表时L=NULL,新节点next也为NULL);
      • 链表指针指向新节点:L = s(新节点成为首元);
    • 场景 2:插入非首元(i>1 :
      • 找第i-1个节点:p = Lj=1,循环p != NULL && j < i-1p=p->nextj++
      • 合法性二次判断:若p == NULL(未找到i-1节点,i超出表长 + 1)或j > i-1,释放新节点(delete s),返回ERROR
      • 插入新节点:s->next = p->next; p->next = s;
  4. 返回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个节点,释放其内存。

详细步骤

  1. 合法性初步判断:若i < 1或空表(L=NULL),返回ERROR
  2. 分场景删除:
    • 场景 1:删除首元(i=1 :
      • 暂存原首元节点:LinkList q = L
      • 链表指针指向新首元:L = L->next(空表时L变为NULL);
      • 释放原首元:delete q
    • 场景 2:删除非首元(i>1 :
      • 找第i-1个节点:p = Lj=1,循环p != NULL && j < i-1p=p->nextj++
      • 合法性二次判断:若p == NULL(未找到i-1节点)或p->next == NULL(无第i个节点),返回ERROR
      • 暂存待删除节点:LinkList q = p->next
      • 断链:p->next = q->next(跳过待删除节点);
      • 释放内存:delete q
  3. 返回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)。

详细步骤

  1. L == NULL,返回1(表示空表);
  2. 否则返回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(遍历结束)。

详细步骤

  1. 初始化计数器len = 0,指针p = L
  2. 循环遍历:若p != NULL,则len++p = p->next
  3. 返回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,会导致所有节点内存无法回收(内存泄漏)。

详细步骤

  1. 初始化指针p(暂存当前节点)。
  2. 循环遍历释放:
    • 暂存当前节点:p = L
    • 链表指针后移:L = L->next
    • 释放当前节点:delete p
  3. 遍历结束后,L已为NULL,无需额外赋值(若遍历前L=NULL,循环不执行)。

时间复杂度

O(n)(需遍历并释放所有节点)。

关键说明

  • 程序退出前必须调用此算法,尤其是多次创建链表的场景;
  • 释放后LNULL,再次操作前需重新初始化。

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]

核心原理

适配空表和非空表的不同场景,遍历非空表时按格式拼接元素。

详细步骤

  1. L == NULL(空表),输出 “空表” 并换行,返回;
  2. 非空表处理:
    • 输出左括号: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指向原表头,导致先输入的元素被后输入的元素 “挤到后面”。

详细步骤

  1. 初始化空表:L = NULL
  2. 循环n次(n为元素个数):
    • 为新节点分配内存:LinkList p = new LNode
    • 输入节点数据:cin >> p->data
    • 新节点指向原表头:p->next = L
    • 链表指针指向新表头:L = p
  3. 建表完成,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=NULL
        if (L == NULL) {
            L = p; // 空表时,新节点即为首元,L指向首元
        } else {
            r->next = p; // 非空表时,尾指针后接新节点
        }
        r = p; // 尾指针更新为新节点(保持指向表尾)
    }
}

功能

通过 “后插法”(每次新节点插入到表尾)创建不带头节点的单链表,最终链表元素顺序与输入顺序一致

核心原理

后插法需额外维护 “尾指针r”(始终指向当前表尾),避免每次插入都遍历找表尾;新节点直接插入到r之后,r更新为新节点,保证元素顺序与输入一致。

详细步骤

  1. 初始化空表:L = NULL,尾指针r = NULL
  2. 循环n次:
    • 为新节点分配内存:LinkList p = new LNode
    • 输入节点数据:cin >> p->data,设置p->next = NULL(新节点为表尾,无后续节点);
    • 分场景插入:
      • 空表时(L == NULL):L = pL指向首元),r = pr指向表尾);
      • 非空表时(L != NULL):r->next = p(表尾后接新节点),r = pr更新为新表尾)。
  3. 建表完成,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=NULL
        if (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 = None
            self.is_destroyed = False
        return OK
    def 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.next
            j += 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 None
        p = self.head
        while p is not None and p.data != e:
            p = p.next
        return p  # 找到返回节点,未找到返回None
    def LocateELem_L_NoHead(self, e):
        """查找:返回第一个值为e的节点位置(1-based),未找到返回0"""
        if self.is_destroyed or self.head is None:
            return 0
        p = self.head
        j = 1  # 位置计数器
        while p is not None and p.data != e:
            p = p.next
            j += 1
        return j if p is not None else 0
    def 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.head
        j = 1
        while p is not None and j < i - 1:
            p = p.next
            j += 1
        # 位置不合法:未找到第i-1个节点(i超出表长+1)
        if p is None or j > i - 1:
            return ERROR
        # 插入新节点:建立链接
        new_node.next = p.next
        p.next = new_node
        return OK
    def 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.head
        j = 1
        while p is not None and j < i - 1:
            p = p.next
            j += 1
        # 位置不合法:未找到第i-1个节点 或 第i个节点不存在
        if p is None or p.next is None or j > i - 1:
            return ERROR
        # 跳过待删除节点(自动回收内存)
        p.next = p.next.next
        return OK
    def ListEmpty_NoHead(self):
        """判空:返回1(空表)或0(非空表),对应原C++逻辑"""
        return 1 if (self.head is None and not self.is_destroyed) else 0
    def ListLength_L_NoHead(self):
        """求长:返回链表节点个数(表长)"""
        if self.is_destroyed or self.head is None:
            return 0
        count = 0
        p = self.head
        while p is not None:
            count += 1
            p = p.next
        return count
    def DestroyList_NoHead(self):
        """销毁:释放链表所有节点(Python自动回收,只需置空head)"""
        self.head = None
        self.is_destroyed = True
        return OK
    def PrintList_NoHead(self):
        """打印:适配空表/非空表,格式为[元素1, 元素2, ...]"""
        if self.is_destroyed:
            print("已销毁", end="")
            return
        if self.head is None:
            print("空表", end="")
            return
        # 遍历打印非空表
        print("[", end="")
        p = self.head
        while p is not None:
            print(p.data, end="")
            if p.next is not None:
                print(", ", end="")
            p = p.next
        print("]", end="")
    def CreateList_H_NoHead(self, n):
        """前插法建表:每次插表头,元素顺序与输入相反"""
        if self.is_destroyed:
            return
        self.head = None  # 先初始化空表
        print(f"请依次输入{n}个元素(支持小数,用空格分隔):", end="")
        elements = list(map(float, input().split()))
        # 检查输入元素个数是否匹配n
        if len(elements) != n:
            print("输入元素个数不匹配!建表失败。")
            self.head = None
            return
        # 前插:新节点始终成为首元
        for e in elements:
            new_node = Node(e)
            new_node.next = self.head
            self.head = new_node
    def CreateList_R_NoHead(self, n):
        """后插法建表:每次插表尾,元素顺序与输入一致"""
        if self.is_destroyed:
            return
        self.head = None
        tail = None  # 尾指针:始终指向当前表尾
        print(f"请依次输入{n}个元素(支持小数,用空格分隔):", end="")
        elements = list(map(float, input().split()))
        if len(elements) != n:
            print("输入元素个数不匹配!建表失败。")
            self.head = None
            return
        # 后插:新节点追加到表尾
        for e in elements:
            new_node = Node(e)
            if self.head is None:
                self.head = new_node  # 空表时,新节点即为首元
                tail = new_node
            else:
                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("链表已销毁,请先初始化!")
                continue
            try:
                build_type = int(input("请选择建表方式:1-前插法(逆序)  2-后插法(正序):"))
                n = int(input("请输入要插入的元素个数:"))
            except ValueError:
                print("无效输入!个数和方式需为整数。")
                continue
            if n <= 0:
                print("元素个数必须为正整数!")
                continue
            if 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("链表已销毁或为空,无法取值!")
                continue
            len_ll = ll.ListLength_L_NoHead()
            print(f"当前链表长度:{len_ll}")
            try:
                i = int(input(f"请输入要取值的位置i(1-{len_ll}):"))
            except ValueError:
                print("无效输入!位置需为整数。")
                continue
            status, 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("链表已销毁或为空,无法查找!")
                continue
            try:
                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("链表已销毁,请先初始化!")
                continue
            len_ll = ll.ListLength_L_NoHead()
            max_pos = len_ll + 1  # 空表时max_pos=1
            print(f"当前链表长度:{len_ll}")
            try:
                pos = int(input(f"请输入插入位置i(1-{max_pos}):"))
                e = float(input("请输入插入的元素值(支持小数):"))
            except ValueError:
                print("无效输入!位置需为整数,元素需为数字。")
                continue
            status = 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("链表已销毁或为空,无法删除!")
                continue
            len_ll = ll.ListLength_L_NoHead()
            print(f"当前链表长度:{len_ll}")
            try:
                pos = int(input(f"请输入删除位置i(1-{len_ll}):"))
            except ValueError:
                print("无效输入!位置需为整数。")
                continue
            status = 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("链表已销毁,请先初始化!")
                continue
            is_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("不带头节点链表已销毁,无需重复操作!")
                continue
            if ll.DestroyList_NoHead() == OK:
                print("不带头节点链表销毁成功!")
                ll.is_destroyed = True
            else:
                print("不带头节点链表销毁失败!")
        # 无效选择
        else:
            print("无效选择,请重新输入(0-8)!")
if __name__ == "__main__":
    main()

完整Java代码如下:

import java.util.Scanner;
// 1. 结果封装类:解决Java无引用传参问题(封装状态码+数据)
class Result {
    private int status;    // 状态码:OK=1,ERROR=0
    private 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=null
        this.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=1
                System.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开始,无需跳过头节点