Java数据结构:单向链表

Java数据结构:单向链表

image-20221107190226364

单链表逻辑结构图:

image-20221107190256522

  • 链表以节点的方式来存储
  • 每个节点包含 data 域,next 域:指向下一个节点
  • 链表的各个节点不一定连续存储
  • 链表分带头节点和不带头结点的,根据实际的需求来确定

代码实现

节点类:StudentNode

package com.syj.linkedlist;

/**
 * 链表节点 StudentNode,每一个StudentNode对象就是一个节点
 */
public class StudentNode {
    private int id;  // 学生id
    private String name;  // 学生姓名
    private StudentNode nextStudent;  // 指向下一个学生

    public StudentNode() {
    }

    public StudentNode(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public StudentNode getNextStudent() {
        return nextStudent;
    }

    public void setNextStudent(StudentNode nextStudent) {
        this.nextStudent = nextStudent;
    }

    @Override
    public String toString() {
        return "StudentNode{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

链表类SingleLinkedList

package com.syj.linkedlist;

/**
 * 定义 SingleLinkedList 类表示学生链表,用来管理学生
 * 该类包含的功能有:
 * addNodeAscById  添加节点时,会按照节点的 id 属性升序排序插入节点
 * addNodeTail  添加节点在单向链表的尾部
 * addNodeHead  从链表头部插入节点
 * deleteNodeById  根据 id 删除节点
 * updateNameById  根据 id 修改学生节点中的姓名
 * printLinkedList  输出整个链表
 */
public class SingleLinkedList {
    // 先初始化一个头节点,头节点不要动,不存放具体数据
    private StudentNode headNode = new StudentNode(0, "");

    /**
     * 添加节点时,会按照节点的 id 属性升序排序插入节点
     * 思路:
     * 1、创建一个辅助节点(tempNode)来遍历链表
     * 2、若辅助接点的下一个节点的 id 大于新节点的 id,则在辅助节点和辅助节点的下一个节点之间插入新节点
     * @param newStudentNode 要插入的新节点
     */
    public void addNodeAscById(StudentNode newStudentNode) {
        // 头节点不能动,所以定义一个辅助变量来帮助我们找到插入新节点的位置
        StudentNode tempNode = headNode;
        while (true) {
            if (tempNode.getNextStudent() == null) {
                // 已经到达了链表尾部还没有退出循环,说明中间没有位置插入,直接在尾部插入新节点
                tempNode.setNextStudent(newStudentNode);
                break;
            }
            if (tempNode.getNextStudent().getId() == newStudentNode.getId()) break;  // 链表中已经存在该 id 的节点了
            if (tempNode.getNextStudent().getId() > newStudentNode.getId()) {
                // tempNode 节点的下一个节点的 id 大于新节点的 id,那么插入的位置就在 tempNode 节点和 tempNode.nextStudent() 节点之间
                // 插入节点
                newStudentNode.setNextStudent(tempNode.getNextStudent());
                tempNode.setNextStudent(newStudentNode);
                break;
            }
            tempNode = tempNode.getNextStudent();  // tempNode 后移一位
        }
    }

    /**
     * 添加节点在单向链表的尾部
     * 思路:
     * 1、找到当前链表的最后节点
     * 2、将最后这个节点的 nextStudent 指向新的节点
     * 其实如果要在链表尾部插入节点,最好给链表添加一个尾指针
     * @param newStudentNode 要添加的新节点
     */
    public void addNodeTail(StudentNode newStudentNode) {
        // 由于头节点不能动,所以定义一个辅助遍历的节点
        StudentNode tempNode = headNode;
        // 遍历链表,找到最后一个节点
        while (true) {
            if (tempNode.getNextStudent() == null) break;
            // 将 tempNode 后移
            tempNode = tempNode.getNextStudent();
        }
        // 当退出 while 循环时,tempNode 就指向了链表的最后
        // 将最后这个节点的 nextStudent 指向新的节点
        tempNode.setNextStudent(newStudentNode);
    }

    /**
     * 从链表头部插入节点
     * 如果链表带头指针,最好使用头插法
     * @param newStudentNode
     */
    public void addNodeHead(StudentNode newStudentNode) {
        // 先判断链表是否为空
        if (headNode.getNextStudent() == null) {
            // 如果链表为空,则直接在头结点后面添加
            headNode.setNextStudent(newStudentNode);
        } else {
            // 链表不为空
            // 先将头节点的 nextStudent 赋值给新节点的 nextStudent
            newStudentNode.setNextStudent(headNode.getNextStudent());
            // 再将新节点赋值给头节点的 nextStudent
            headNode.setNextStudent(newStudentNode);
        }
    }

    /**
     * 根据 id 删除节点
     * 思路:
     * 1、定义一个辅助节点(tempNode)遍历链表,找到要删除节点的前一个节点
     * 2、删除节点:tempNode.setNextStudent(tempNode.getNextStudent().getNextStudent())
     * 被删除的节点将不会有其它引用再指向它,将被垃圾回收机制回收
     * @param id 要删除的节点的 id
     */
    public void deleteNodeById(int id) {
        StudentNode tempNode = headNode;
        while (true) {
            if (tempNode.getNextStudent() == null) {
                // 已经到链表最后了,还没有找到,说明链表中不存在要找的节点
                System.out.println("要删除的节点不存在");
                break;
            }
            if (tempNode.getNextStudent().getId() == id) {
                // 找到要删除的节点了
                tempNode.setNextStudent(tempNode.getNextStudent().getNextStudent());
                break;
            }
            tempNode = tempNode.getNextStudent();
        }
    }

    /**
     * 根据 id 修改学生节点中的姓名
     * 思路:遍历链表,找到对应 id 的节点,然后修改姓名
     * @param newStudentNode 修改过姓名后的新节点
     */
    public void updateNameById(StudentNode newStudentNode) {
        // 判断链表是否为空
        if (headNode.getNextStudent() == null) {
            System.out.println("链表为空");
            return;
        }
        // 根据新节点的 id 找到链表中对应的节点
        StudentNode tempNode = headNode;
        while (true) {
            if (tempNode.getNextStudent() == null) {
                // 已经到链表尾部了,还没有找到对应 id 的节点,说明链表中没有该 id 的节点
                System.out.println("不存在id为:" + newStudentNode.getId() + "的学生");
                break;
            }
            if (tempNode.getId() == newStudentNode.getId()) {
                // 找到了,修改姓名
                tempNode.setName(newStudentNode.getName());
                break;
            }
            tempNode = tempNode.getNextStudent();  // tempNode 后移一位
        }
    }

    /**
     * 输出整个链表
     */
    public void printLinkedList() {
        // 先判断链表是否为空
        if (headNode.getNextStudent() == null) {
            System.out.println("链表为空");
        } else {
            // 因为头结点不能动,所以我们定义一个辅助变量来遍历
            StudentNode tempNode = headNode.getNextStudent();
            while (true) {
                // 判断链表是否到最后
                if (tempNode == null) break;
                // 输出节点信息
                System.out.println(tempNode);
                // 将 tempNode 后移
                tempNode = tempNode.getNextStudent();
            }
        }
    }
}

测试单向链表

package com.syj.linkedlist;

public class SingleLinkedListDemo {
    // 测试
    public static void main(String[] args) {
        // 创建节点
        StudentNode student1 = new StudentNode(1, "小白");
        StudentNode student2 = new StudentNode(2, "小黑");
        StudentNode student3 = new StudentNode(3, "小红");

        // 创建链表
        SingleLinkedList studentLinkedList = new SingleLinkedList();
        // 从尾部添加节点
        System.out.println("===========从尾部添加节点============");
        studentLinkedList.addNodeTail(student1);
        studentLinkedList.addNodeTail(student2);
        studentLinkedList.addNodeTail(student3);
        studentLinkedList.printLinkedList();
        studentLinkedList.deleteNodeById(3);
        studentLinkedList.deleteNodeById(2);
        studentLinkedList.deleteNodeById(1);

        // 从头部添加节点
        System.out.println("===========从头部添加节点============");
        studentLinkedList.addNodeHead(student1);
        studentLinkedList.addNodeHead(student2);
        studentLinkedList.addNodeHead(student3);
        studentLinkedList.printLinkedList();
        studentLinkedList.deleteNodeById(1);
        studentLinkedList.deleteNodeById(2);
        studentLinkedList.deleteNodeById(3);

        // 添加节点,并按照节点的 id 属性升序排序添加节点
        System.out.println("===========按照节点的 id 属性升序排序添加节点============");
        studentLinkedList.addNodeAscById(student3);
        studentLinkedList.addNodeAscById(student2);
        studentLinkedList.addNodeAscById(student2);  // 重复插入
        studentLinkedList.addNodeAscById(student1);
        studentLinkedList.printLinkedList();

        // 修改链表中 id = 1 的节点的姓名为“小黄”
        System.out.println("===========修改节点============");
        StudentNode newStudent1 = new StudentNode(1, "小黄");
        studentLinkedList.updateNameById(newStudent1);
        studentLinkedList.printLinkedList();

        // 删除 id = 2 的节点
        System.out.println("===========删除节点============");
        studentLinkedList.deleteNodeById(2);
        studentLinkedList.printLinkedList();
    }
}

image-20221107190433567

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值