Java 单链表核心操作:从节点定义到常见算法实现

南乔
南乔
发布时间:2026-10-16 | 2人浏览过
原创

单链表是数据结构中最基础的线性结构之一,采用非连续、非顺式的存储单元来实现数据元素的线性关系。本文将通过 Java 语言实现一个具备完善功能的单链表,涵盖从节点定义、节点增删改查,到链表反转及复杂节点查找等核心算法。

单链表的逻辑结构与节点定义

链表不同于数组,它通过每个节点中的引用域指向下一个节点的位置,从而在内存中串联起整个数据序列。一个标准的链表节点通常包含两个部分:

  • Data 域(数据域):用于存储具体的数据元素,在示例中表现为学生的学号 no、姓名 name 和昵称 nickname。
  • Next 域(指针域):用于存储指向下一个节点的地址引用。

根据是否包含不存放数据的虚拟首元节点,链表可分为“带头节点”和“不带头节点”。以下实现采用带头节点的方式,head 节点仅作为入口标志,不参与业务数据存储,这使得插入和删除操作在处理第一个有效节点时逻辑更加统一,无需特殊判断。

首先,我们需要定义单个节点的数据结构:

package linkedQueue;

public class HeroNode {
    public int no;        // 编号
    public String name;   // 姓名
    public String nickname; // 昵称
    public HeroNode next; // 指向下一个节点

    public HeroNode(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}

单链表类的核心功能实现

构建 SingleLinkedList 类来封装链表的操作。该类内部维护一个 head 头节点,并提供一系列方法来操作链表。以下是各功能的详细实现逻辑。

第1步:无序添加节点

目的:将新节点追加到链表的末尾。 操作:通过临时指针 temp 遍历链表,直到找到 next 为 null 的尾节点,然后将新节点的地址赋给 temp.next。

/**
 * 添加节点
 */
public void add(HeroNode heroNode) {
    HeroNode temp = head;
    while (true) {
        if (temp.next == null) {
            break;
        }
        temp = temp.next;
    }
    temp.next = heroNode;
}

第2步:按序添加节点

目的:根据节点的编号(no)保持链表由小到大有序排列。 操作:找到第一个比待插入节点编号大的节点的前驱节点,或者判断编号是否已存在。

/**
 * 有序添加节点
 */
public void addByOrder(HeroNode herNode) {
    HeroNode temp = head;
    boolean flag = false;
    while (true) {
        if (temp.next == null) {
            break;
        }
        if (temp.next.no > herNode.no) {
            break;
        } else if (temp.next.no == herNode.no) {
            flag = true;
            break;
        }
        temp = temp.next;
    }
    if (flag) {
        System.out.println("你要插入的节点的编号已经存在!!!!");
    } else {
        herNode.next = temp.next;
        temp.next = herNode;
    }
}

第3步:修改节点信息

目的:根据节点编号更新其名称或昵称。 操作:从头节点后开始遍历,若找到对应编号则修改属性;若遍历结束未找到则报错。

/**
 * 更新节点数据
 */
public void update(HeroNode newHeroNode) {
    if (head.next == null) {
        System.out.println("链表为空!!!!");
        return;
    }
    HeroNode temp = head.next;
    boolean flag = false;
    while (true) {
        if (temp == null) {
            break;
        }
        if (temp.no == newHeroNode.no) {
            flag = true;
            break;
        }
        temp = temp.next;
    }
    if (flag) {
        temp.name = newHeroNode.name;
        temp.nickname = newHeroNode.nickname;
    } else {
        System.out.println("没有找到编号" + newHeroNode.no + "的节点,不能修改");
    }
}

第4步:删除节点

目的:从链表中移除指定编号的节点。 操作:关键在于“间接删除”。遍历寻找目标节点的前一个节点 temp,一旦发现 temp.next 即为目标,执行 temp.next = temp.next.next 跳过该节点,即可完成物理删除。

/**
 * 刪除节点
 */
public void del(int no) {
    HeroNode temp = head;
    boolean flag = false;
    while (true) {
        if (temp.next == null) {
            break;
        }
        if (temp.next.no == no) {
            flag = true;
            break;
        }
        temp=temp.next;
    }
    if (flag) {
        temp.next = temp.next.next;
    }
}

第5步:遍历显示所有节点

目的:检查当前链表中的所有数据。 操作:初始化指针为 head.next,循环输出直至指针变为 null。

/**
 * 遍历节点
 */
public void showList() {
    if (head.next == null) {
        System.out.println("链表为空");
        return;
    }
    HeroNode temp = head.next;
    while (true) {
        if (temp == null) {
            break;
        }
        System.out.println(temp);
        temp = temp.next;
    }
}

进阶算法:反向打印、计数与反转

反向打印节点

逻辑:由于链表只能向后访问,反向打印可以使用栈(LIFO)特性实现。依次将所有节点压入栈,然后再弹出打印,即可实现倒序输出。

//反向打印节点
public static void reversePrint(HeroNode head) {
    if (head.next == null) {
        return;
    }
    // 创建一个栈
    Stack<HeroNode> heroNodes = new Stack<>();
    HeroNode cur = head.next;
    while (cur != null) {
        heroNodes.push(cur); //入栈
        cur = cur.next;
    }
    // 出栈打印
    while (heroNodes.size() > 0) {
        System.out.println(heroNodes.pop());
    }
}

获取有效节点个数

逻辑:同样通过遍历计数,统计 head.next 开始的节点总数。

/**
 * 统计有效节点的个数
 */
public static int getLength(HeroNode head) {
    if (head.next == null) {
        return 0;
    }
    int length = 0;
    HeroNode herNode = head.next;
    while (herNode != null) {
        length++; 
        herNode=herNode.next;
    }
    return length;
}

查找倒数第 N 个节点

逻辑:先计算链表总长度 size,则倒数第 N 个即正数第 (size - N) 个。校验索引范围后,从头节点后移动相应步数即可。

/**
 * 求倒数第index个节点
 */
public static HeroNode findLastIndexNode(HeroNode head, int index) {
    if (head.next == null) {
        return null;
    }
    int size = getLength(head);
    if (index <= 0 || index > size) {
        return null;
    }
    HeroNode herNode = head.next;
    for (int i = 0; i < size - index; i++) {
        herNode = herNode.next;
    }
    return herNode;
}

单链表反转

逻辑:这是单链表最经典的算法题。创建一个新的辅助头节点 reverseHead,原链表每经过一个节点,就将其取出并插入到新链表的头部(reverseHead.next)。最终将原链表的 head.next 指向这个新建立的链表即可。

//单链表反转
public static void reverseList(HeroNode head) {
    // 如果当前链表为空或者只有一个节点,直接返回
    if (head.next == null || head.next.next == null) {
        return;
    }

    HeroNode cur = head.next;
    HeroNode next = null; 
    HeroNode reverseHead = new HeroNode(0, "", "");
    
    while (cur != null) {
        next = cur.next;             // 暂时保存当前节点的下一个节点
        cur.next = reverseHead.next; // 将cur的下一个节点指向新的链表的最前端
        reverseHead.next = cur;      // 将cur链接到新的链表
        cur = next;                  // 指针后移
    }
    head.next = reverseHead.next; // 连接原链表的新头部
}

以上就是 Java 实现单链表基础操作的详细内容,更多关于 Java 单链表学习资料的关注,请搜索相关技术资料或关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表