Java 单链表

news2024/11/18 9:28:02

链表基本介绍

链表在内存中的实际存储结构

链表的逻辑结构 

 单链表应用实例

代码实现

// 英雄节点,存储了英雄的信息
class HeroNode {
    public int id; // 英雄编号
    public String name; // 英雄名字
    public String nickName; // 英雄昵称
    public HeroNode next; // 指针域,指向下一个节点

    public HeroNode(int id, String name, String nickName) {
        this.id = id;
        this.name = name;
        this.nickName = nickName;
        this.next = null; // 初始指针指向 null
    }

    // 重写 toString() ,方便遍历链表的操作
    public String toString() {
        return "[" + id + " " + name + " " + nickName + "]";
    }
}

// 单链表
class SingleLinkedList {
    // 创建一个头结点并初始化,头结点用于指向一个链表,不存储数据
    HeroNode head = new HeroNode(0, "", "");

    // 向链表添加节点
    // 直接把要插入的节点放在链表的末尾
    public void add(HeroNode node) {
        // 定义一个临时变量指向链表的头结点
        HeroNode t = head;
        // 找到链表的最后一个节点的位置
        while (true) {
            // 当节点 t 的 next 为 null ,表示 t 所在位置是最后一个节点
            if (t.next == null) {
                break;
            }
            // 否则还不是最后一个节点,继续后移指向下一个节点
            t = t.next;
        }
        // 退出循环表示临时变量指向的是链表的最后一个节点
        // 在链表的末尾插入新添加的元素
        // 让最后一个节点的 next 指向新添加的节点,即可实现插入链表
        t.next = node;
    }

    // 向链表添加节点
    // 按照节点编号从小到大插入链表,即编号小的在链表前面,大的在链表后面
    public void addById(HeroNode node) {
        // 定义一个临时变量指向链表的头结点
        HeroNode t = head;
        // 找到比新节点ID值大的节点的位置
        while (true) {
            // 如果节点的 ID 值相等,表示已经存在节点,给出提示并返回
            if (t.next != null && node.id == t.next.id) {
                System.out.println("链表已经存在该节点" + node);
                return;
            }
            // 当节点 t 的 next 为 null ,表示 t 所在位置是最后一个节点
            // 找到了比新节点 ID 值大的节点或到达链表末尾
            if (t.next == null || node.id < t.next.id) {
                break;
            }
            // 没有找到合适位置的节点,继续后移指向下一个节点
            t = t.next;
        }
        // 存储 t 节点的下一个节点
        HeroNode p = t.next;
        // 让 t 节点的 next 指向新节点
        t.next = node;
        // 让新节点的 next 指向 p 节点
        node.next = p;
    }

    // 修改节点信息
    // 以节点的 id 值为查找依据,所以 id 值不能修改
    public void update(HeroNode newNode) {
        HeroNode t = head.next;
        // 首先判断列表是否为空
        if (t == null) {
            System.out.println("链表为空。。。");
            return;
        }

        // 遍历链表查找 id 值和 newNode 的 id 值相同的节点
        boolean flag = false;
        while (true) {
            if (t == null) {
                break;
            } else if (t.id == newNode.id) { // 找到了要修改的节点
                flag = true;
                break;
            }
            t = t.next;
        }

        if (flag) { // 修改对应的节点信息
            t.name = newNode.name;
            t.nickName = newNode.nickName;
        } else {
            System.out.println("链表没有节点" + newNode);
        }
    }

    // 删除节点
    public void deleteHeroNode(int id) {
        HeroNode t = head;
        boolean flag = false;
        while (t.next != null) {
            if (t.next.id == id) { // 找到要删除节点(t.next)的前一个节点 t
                flag = true;
                break;
            }
            t = t.next;
        }

        if (flag) {
            // 让要删除节点的前一个节点 t 的 next 指向要删除节点的后一个节点
            t.next = t.next.next;
        } else {
            System.out.println("链表中没有节点 id 为:" + id);
        }
    }

    // 打印链表中的节点信息,即遍历链表
    public void showList() {
        // 从第一个数据节点开始
        HeroNode t = head.next;

        // 判断链表是否为空
        if (t == null) {
            System.out.println("链表为空...");
            return;
        }

        while (t != null) {
            if (t.next != null) {
                System.out.print(t + " -> ");
            } else {
                System.out.println(t);
            }

            t = t.next;
        }
    }

}

// 单链表测试类
class SingleLinkedListTest {
    public static void main(String[] args) {
        // 创建英雄节点
        HeroNode node1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode node2 = new HeroNode(2, "吴用", "及时雨");
        HeroNode node3 = new HeroNode(3, "卢俊义", "玉麒麟");
        HeroNode node4 = new HeroNode(4, "林冲", "豹子头");

        // 创建一个链表,把上述几个节点加入链表
        SingleLinkedList linkedList = new SingleLinkedList();
        linkedList.add(node1);
        linkedList.add(node2);
        linkedList.add(node3);
        linkedList.add(node4);

        // 遍历链表
        linkedList.showList();

        // 创建一个链表,测试按编号顺序插入节点的方法
        SingleLinkedList linkedList2 = new SingleLinkedList();
        linkedList2.addById(node4);
        linkedList2.addById(node1);
        linkedList2.addById(node3);
        linkedList2.addById(node2);
        linkedList2.addById(node3);
        linkedList2.showList();

        // 测试修改节点的代码
        HeroNode newNode = new HeroNode(3, "大红", "小红红");
        linkedList2.update(newNode);
        linkedList2.showList();

        // 测试修改节点的代码
        linkedList2.deleteHeroNode(6);
        linkedList2.deleteHeroNode(1);
        linkedList2.deleteHeroNode(2);
        linkedList2.deleteHeroNode(3);
        linkedList2.deleteHeroNode(4);
        linkedList2.deleteHeroNode(5);
        linkedList2.showList();
    }
}

单链表面试题

 问题一代码如下:

// 英雄节点,存储了英雄的信息
class HeroNode {
    public int id; // 英雄编号
    public String name; // 英雄名字
    public String nickName; // 英雄昵称
    public HeroNode next; // 指针域,指向下一个节点

    public HeroNode(int id, String name, String nickName) {
        this.id = id;
        this.name = name;
        this.nickName = nickName;
        this.next = null; // 初始指针指向 null
    }

    // 重写 toString() ,方便遍历链表的操作
    public String toString() {
        return "[" + id + " " + name + " " + nickName + "]";
    }
}

// 单链表
class SingleLinkedList {
    // 创建一个头结点并初始化,头结点用于指向一个链表,不存储数据
    HeroNode head = new HeroNode(0, "", "");

    // 向链表添加节点
    // 直接把要插入的节点放在链表的末尾
    public void add(HeroNode node) {
        // 定义一个临时变量指向链表的头结点
        HeroNode t = head;
        // 找到链表的最后一个节点的位置
        while (true) {
            // 当节点 t 的 next 为 null ,表示 t 所在位置是最后一个节点
            if (t.next == null) {
                break;
            }
            // 否则还不是最后一个节点,继续后移指向下一个节点
            t = t.next;
        }
        // 退出循环表示临时变量指向的是链表的最后一个节点
        // 在链表的末尾插入新添加的元素
        // 让最后一个节点的 next 指向新添加的节点,即可实现插入链表
        t.next = node;
    }

    // 打印链表中的节点信息,即遍历链表
    public void showList() {
        // 从第一个数据节点开始
        HeroNode t = head.next;

        // 判断链表是否为空
        if (t == null) {
            System.out.println("链表为空...");
            return;
        }

        while (t != null) {
            if (t.next != null) {
                System.out.print(t + " -> ");
            } else {
                System.out.println(t);
            }

            t = t.next;
        }
    }

    // 获取链表的有效节点数量,即如果有头结点,头结点不算有效节点数量
    public int getLength() {
        // 如果链表为空,返回 0
        if (head.next == null) {
            return 0;
        }

        int length = 1;
        HeroNode t = head.next;
        while (t.next != null) {
            length++;
            t = t.next;
        }

        return length;
    }

    // 获取链表倒数第 k 个节点
    // 先调用 getLength() 方法得到链表总长度 len
    // 然后从第一个有效数据节点开始遍历,遍历到第 (len - k) 个节点,就是所要求的那个
    public HeroNode findLastIndexNode(int k) {
        HeroNode t = head.next;

        if (t == null) {
            // 链表为空,则找不到节点,返回空
            return null;
        }
        // 获取链表的长度(有效数据的个数)
        int len = getLength();
        int count = 1;
        int flag = len - k;
        if (flag > 0) {
            while (count == flag) {
                t = t.next;
                count++;
            }
            // 使用 for 循环实现
            // for (int i = 0;i < flag; i++) {
            // t = t.next;
            // }
        } else {
            // 超出了链表的范围
            return null;
        }
        return t;
    }

    // 单链表的反转
    public void reverseLinkedList() {
        // 如果链表为空或只有一个节点,则不用反转
        if (head.next == null || head.next.next == null) {
            return;
        }
        // 创建一个新的头节点
        HeroNode newHead = new HeroNode(0, "", "");
        // 从头遍历链表,将链表中的每一个节点取出,依次将其插入新头节点的后面,其他数据节点的前面,即头插法
        // 指向当前节点
        HeroNode cur = head.next;
        // 指向当前节点的下一个节点
        HeroNode next = null;
        while (cur != null) {
            // 保存链表的下一个节点
            next = cur.next;
            // cur 是当前遍历链表取出来的节点
            // 让当前节点指向新头结点指向的第一个节点
            cur.next = newHead.next;
            // 再让头结点指向当前节点
            newHead.next = cur;
            // 遍历链表的下一个节点
            cur = next;
        }

        head = newHead;
    }

    // 反向遍历输出链表
    // 方式一:先将链表反转再输出,但是这样会破坏链表原本的结构顺序
    // 方式二:用栈先进后出的特点实现
    public void reversePrint1() {
        // 用数组模拟栈
        // 获取链表的长度(有效数据的个数)
        int len = getLength();
        if (len == 0) {
            System.out.println("链表为空...");
            return;
        }
        HeroNode[] arr = new HeroNode[len];
        HeroNode cur = head.next;
        int i = 0;
        while (cur != null) {
            arr[i++] = cur;
            cur = cur.next;
        }

        // 反向输出链表
        for (i = len - 1; i >= 0; i--) {
            System.out.println(arr[i]);
        }
    }

    public void reversePrint2() {
        // 用栈结构实现
        Stack<HeroNode> stack = new Stack<HeroNode>();
        HeroNode cur = head.next;
        while (cur != null) {
            stack.push(cur); // 入栈
            cur = cur.next;
        }

        // 反向输出链表,即输出栈元素
        while (stack.size() > 0) {
            System.out.println(stack.pop());
        }
    }
}

// 单链表测试类
class SingleLinkedListTest {
    public static void main(String[] args) {
        // 创建英雄节点
        HeroNode node1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode node2 = new HeroNode(2, "吴用", "及时雨");
        HeroNode node3 = new HeroNode(3, "卢俊义", "玉麒麟");
        HeroNode node4 = new HeroNode(4, "林冲", "豹子头");

        // 创建一个链表,把上述几个节点加入链表
        SingleLinkedList linkedList = new SingleLinkedList();
        linkedList.add(node1);
        linkedList.add(node2);
        linkedList.add(node3);
        linkedList.add(node4);

        // 遍历链表
        linkedList.showList();

        // 测试获取链表有效数据个数代码
        System.out.println("链表的长度为:" + linkedList.getLength());
        // 测试链表倒数第 k 个节点代码
        System.out.println("链表倒数第k个节点为:" + linkedList.findLastIndexNode(2));
        linkedList.showList();

        // 测试链表反转的代码
        linkedList.reverseLinkedList();
        linkedList.showList();

        // 测试反向输出链表的代码
        System.out.println("反向输出链表1:");
        linkedList.reversePrint1();
        System.out.println("反向输出链表2:");
        linkedList.reversePrint2();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/813891.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【系统软件01】devtoolset离线安装gcc

【系统软件01】devtoolset离线安装gcc 一、SCL简介二、SCL源安装三、离线下载devtoolset1、Developer Toolset2、下载devtoolset-93、压缩devtoolset-9 三、离线安装devtoolset-9(gcc9.3)1、解压devtoolset-9.tar.gz2、安装devtoolset-9 四、设置环境变量(使用gcc9.3)1、当前窗…

LeetCode547.Number-Of-Provinces<省份问题>

题目&#xff1a; 思路&#xff1a; 连通的部分加起来&#xff0c;然后总的 减去连通的部分。 但是很可惜 只能通过部分 似乎将st[i][j] st[j][i] 改变之后是可行的 但是实际上 1 2 连通后 2 1 确实是不会再加。 但是 2 3却还是在加一遍。 好吧。答案的思路是使用并查集。将…

lc209.长度最小的子数组

暴力破解&#xff1a;二次for循环遍历num[i]...num[j]&#xff0c;记录满足条件的最小长度 前缀和二分&#xff1a;前缀和降低计算num[i]...num[j]的时间复杂度 对前缀和数组中的每个数进行遍历&#xff0c;找到距离这个数满足条件的最小长度 前缀和数组单调递增&#xff0c;此…

蓝海卓越计费管理系统任意文件读取下载

……人哪&#xff0c;活着是这么的苦&#xff01;一旦你从幸福的彼岸被抛到苦难的此岸&#xff0c;你真是处处走头无路&#xff1b;而现在你才知道&#xff0c;在天堂与地狱之间原来也只有一步之遥&#xff01; 漏洞描述 蓝海卓越计费管理系统存在任意文件读取漏洞&#xff0…

pycharm写scrapy遇到的问题

目录 背景创建scrapy难受的开始指定类型修改模板并指定使用运行scrapy 背景 居然还有万能的pycharm解决不了的python程序&#xff1f;&#xff1f;&#xff1f; 创建scrapy 由于PyCharm中没有直接创建Scrapy项目的选项,所以使用命令行创建一个项目 安装scrapy pip install…

11、springboot项目启动时对容器中的bean进行延迟初始化

springboot项目启动时对容器中的bean进行延迟初始化 预初始化&#xff1a; Spring Boot在启动应用时&#xff0c;会启动Spring容器&#xff0c;当启动Spring容器时&#xff0c;Spring会自动初始化容器中所有的singleton Bean——这是默认行为 预初始化的好处&#xff1a; 1、项…

Qt 4. 发布exe

把ex2.exe放在H盘Ex2文件夹下&#xff0c;执行 H:\Ex2>windeployqt ex2.exe H:\Ex2>windeployqt ex2.exe H:\Ex2\ex2.exe 64 bit, release executable Adding Qt5Svg for qsvgicon.dll Skipping plugin qtvirtualkeyboardplugin.dll due to disabled dependencies (Qt5…

C++多线程的简单使用

一.引言 在C学习中&#xff0c;我们在初步学习的过程中写的程序都只可以在本地并且只可以被本主机访问。在此&#xff0c;我们会想要让别人也体验一下我们邪恶的程序的话&#xff0c;我们该怎么办呢&#xff1f; 首先我们介绍的时多线程编程&#xff0c;就相当于我们的登录qq&…

flask数据库操作

本文将详细介绍在Flask Web应用中如何设计数据库模型,并使用Flask-SQLAlchemy等扩展进行数据库操作的最佳实践。内容涵盖数据模型设计,ORM使用,关系映射,查询方法,事务处理等方面。通过本文,您可以掌握Flask数据库应用的基本知识。 Flask作为一个流行的Python Web框架,提供了高…

强化学习(PPO,DQN,A3C)

目录 1.强化学习和深度学习的区别 2. 强化学习思路 3.baseline 4.PPO 4.1on-policy和off-policy简单理解 4.2actotcritic 5.DQN&#xff08;回归问题&#xff09; 4.1公式 4.2Q表 参考文献 1.强化学习和深度学习的区别 强化学习和深度学习的区别&#xff1a;在深度学习中&a…

【*1900 图论+枚举思想】CF1328 E

Problem - E - Codeforces 题意&#xff1a; 思路&#xff1a; 注意到题目的性质&#xff1a;满足条件的路径个数是极少的&#xff0c;因为每个点离路径的距离<1 先考虑一条链&#xff0c;那么直接就选最深那个点作为端点即可 为什么&#xff0c;因为我们需要遍历所有点…

助你丝滑过度到 Vue3 其组合式API的应用 ②⑦

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; VUE3~TS &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f449;…

C++成神之路 | 第一课【步入C++的世界】

目录 一、认识C++ 1.1、关于 C++ 1.2、C++的前世今生 1.2.1、C+

【Tomcat---1】IDEA控制台tomcat日志输出乱码解决

一、修改IDEA的文件编码配置为UTF-8 二、修改IDEA的vmoptions文件&#xff0c;添加-Dfile.encodingUTF-8 到Tomcat目录/conf文件夹修改logging.properties 重启idea即可。采用统一的编码

ansible的脚本——playbook剧本

目录 一、playbook的组成 二、 playbook安装httpd服务 1.编写playbook剧本 2.运行playbook 三、定义、引用变量 四、 指定远程主机sudo切换用户 五、when条件判断 六、迭代 七、Templates 模块 1.先准备一个以 .j2 为后缀的 template 模板文件&#xff0c;设置引用的变…

微信小程序生成带参数的二维码base64转png显示

getQRCode() {var that this;wx.request({url: http://localhost:8080/getQRCode?ID 13,header: {content-type: application/json},method: POST,responseType: arraybuffer,//将原本按文本解析修改为arraybuffersuccess(res) {that.setData({getQRCode: wx.arrayBufferToB…

Java集合篇

前言&#xff1a;笔者参考了JavaGuide、三分恶等博主的八股文&#xff0c;结合Chat老师和自己的理解&#xff0c;整理了一篇关于Java集合的八股文。希望对各位读者有所帮助~~ 引言 常见集合有哪些&#xff1f; Java集合相关类和接口都在java.util包中&#xff0c;按照其存储…

JVM基础篇-虚拟机栈

JVM基础篇-虚拟机栈 定义 Java Virtual Machine Stacks &#xff08;Java 虚拟机栈&#xff09; 每个线程运行时所需要的内存&#xff0c;称为虚拟机栈每个栈由多个栈帧&#xff08;Frame&#xff09;组成&#xff0c;对应着每次方法调用时所占用的内存每个线程只能有一个活动…

Spring的创建及使用

文章目录 什么是SpringSpring项目的创建存储Bean对象读取Bean对象getBean()方法 更简单的读取和存储对象的方式路径配置使用类注解存储Bean对象关于五大类注解使用方法注解Bean存储对象Bean重命名 Bean对象的读取 使用Resource注入对象Resource VS Autowired同一类型多个bean对…