Java实现单链表

2023-09-21 22:30:14

目录

一.单链表

二.单链表基本操作的实现

1.单链表类、属性的定义

2.求链表长度

3.链表是否包含值为key的节点

4.添加元素

5.删除节点

6.清空链表

三、完整代码


一.单链表

链表是一种在物理存储结构非连续的存储结构,数据元素的逻辑顺序通过链表中的引用链接次序实现。链表的结构多样,我们通过实现无头单向非循环链表,来进一步理解链表。

从图中可以看出,链表在逻辑上连续的,但在物理存储结构上不一定是连续的

由于链表是单向的,可以通过前一个节点访问后一个节点,不能通过后一个节点访问前一个结点

二.单链表基本操作的实现

1.单链表类、属性的定义

要创建链表首先要有节点,我们可以将节点定义为内部类

public class MySingleList{
    static class ListNode{
        //存放当前结点的值
        private int val;
        //存放下一节点的地址
        private ListNode next;
        public ListNode(){}
        public ListNode(int val){
            this.val = val;
        }
    }
}

由于链表是通过前一个节点访问下一个节点的,因此我们需要知道链表的第一个节点,因此我们需要定义一个头节点

private ListNode head;//第一个节点

2.求链表长度

 定义变量count用以计算,遍历链表,统计链表节点个数

//求链表长度
public int size() {
    int count = 0;
    //定义节点cur,用于遍历链表
    ListNode cur = head;
    while(cur != null){
        count++;
        //指向下一节点
        cur = cur.next;
    }
    return count;
}

3.链表是否包含值为key的节点

遍历链表,判断节点的值是否等于key

public boolean contains(int key) {
    ListNode cur = head;
    while(cur != null){
        if(cur.val == key){
            return true;
        }
        cur = cur.next;
    }
    return false;
}

4.添加元素

链表添加元素的方式通常有三种,头插、尾插和在指定位置添加元素。

头插:在链表的第一个节点前插入元素

 实现头插,就是让要插入的节点node指向原本链表的头节点head,再更新链表的第一个节点,让head指向新插入的节点

//头插
public void addFirst(int data) {
    //创建新节点
    ListNode listNode = new ListNode(data);
    listNode.next = head;
    head = listNode;
}

尾插:在链表的最后插入元素

实现尾插,首先要找到链表的最后一个节点,再将最后一个节点的next更新为新节点即可

//尾插
public void addLast(int data) {
    ListNode newNode = new ListNode(data);
    //若链表为空,直接将head更新为插入节点
    if(head == null){
        head = newNode;
    }else{
        //通过遍历找到最后一个节点
        ListNode cur = head;
        while(cur.next != null){
            cur = cur.next;
        }
        cur.next = newNode;
    }
}

在指定位置添加元素

 在指定位置添加元素,首先要判断指定位置index是否合法,若index < 0 或 index > size,则不能添加元素,然后通过遍历找到节点插入的位置,由于链表为单链表,不能通过后一节点找到前一节点,因此,我们需要找到插入位置的前驱节点,然后通过前驱节点实现链表在index位置的插入

//在任意位置添加元素
public void addIndex(int index, int data) {
    //index不正确,无法添加元素
    if(index < 0 || index > size()){
        throw new RuntimeException("输入位置不正确,无法添加元素!");
    }else if(index == 0){//在0位置插入,即为头插
        addFirst(data);
    }else{
        ListNode cur = head;//用cur遍历链表,找到前驱节点
        ListNode listNode = new ListNode(data);
        int count = 0;
        while(count != index-1){
            count++;
            cur = cur.next;
        }
        //添加元素
        listNode.next = cur.next;
        cur.next = listNode;
    }
}

5.删除节点

删除第一个值为key的节点:与在指定位置添加元素相同,我们需要找到第一个值为key的节点node的前驱节点,然后才能删除值为key的节点。由于JVM会将未被任何引用指向的对象回收掉,我们只需让node的前驱节点指向node的下一节点,即可实现删除

//删除第一个值为key的节点
public boolean remove(int key) {
    ListNode cur = head;
    //链表第一个节点的值即为key,头删
    if(head.val == key){
        head = head.next;
        return true;
    }//通过遍历找到第一个值为key的节点的前驱节点
    while(cur.next != null){
        if(cur.next.val == key){//找到了,删除节点
            cur.next = cur.next.next;
            return true;
        }
        cur = cur.next;
    }
    return false;//未找到值未key的节点,返回false
}

删除所有值为key的节点:首先要找到值为key的节点node,将node的上一节点指向node的下一节点,即可删除值为key的节点,注意考虑头节点值为key的情况,要删除链表中所有值为key的节点,利用while循环即可实现。

//删除所有值为key的节点
public void removeAllKey(int key) {
    //若链表为空,则直接返回null
    if(head == null){
        return;
    }
    ListNode cur = head;
    while(cur.next != null){
        if(cur.next.val == key){
            cur.next = cur.next.next;
        }else{
            cur = cur.next;
        }
    }
    //单独考虑头节点的值是否等于val
    if(head.val == key){
        head = head.next;
    }
}

6.清空链表

由于JVM会将未被任何引用指向的对象回收掉,清空链表可以将每个节点的指向都置为null,即切断节点之间的链接,也可以直接将head置为null

将每个节点的指向都置为null

//清空链表
public void clear() {
    if(head == null){//链表本身为空
        return;
    }
    ListNode cur = head;
    while(cur != null){
        ListNode next = cur.next;//记录下一节点
        cur.next = null;
        cur = next;
    }
}

直接将head置为null

//清空链表
public void clear() {
    head = null;
}

三、完整代码

public class MySingleList {
    static class ListNode{
        //存放当前结点的值
        private int val;
        //存放下一节点的地址
        private ListNode next;
        public ListNode(){}
        public ListNode(int val){
            this.val = val;
        }
    }
    private ListNode head;//第一个节点
    //求链表长度
    public int size() {
        int count = 0;
        //定义节点cur,用于遍历链表
        ListNode cur = head;
        while(cur != null){
            count++;
            //指向下一节点
            cur = cur.next;
        }
        return count;
    }
    //链表是否包含值为key的节点
    public boolean contains(int key) {
        ListNode cur = head;
        while(cur != null){
            if(cur.val == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    //头插
    public void addFirst(int data) {
        //创建新节点
        ListNode listNode = new ListNode(data);
        listNode.next = head;
        head = listNode;
    }
    //尾插
    public void addLast(int data) {
        ListNode newNode = new ListNode(data);
        //若链表为空,直接将head更新为插入节点
        if(head == null){
            head = newNode;
        }else{
            //通过遍历找到最后一个节点
            ListNode cur = head;
            while(cur.next != null){
                cur = cur.next;
            }
            cur.next = newNode;
        }
    }
    //在任意位置添加元素
    public void addIndex(int index, int data) {
        //index不正确,无法添加元素
        if(index < 0 || index > size()){
            throw new RuntimeException("输入位置不正确,无法添加元素!");
        }else if(index == 0){//在0位置插入,即为头插
            addFirst(data);
        }else{
            ListNode cur = head;//用cur遍历链表,找到前驱节点
            ListNode listNode = new ListNode(data);
            int count = 0;
            while(count != index-1){
                count++;
                cur = cur.next;
            }
            //添加元素
            listNode.next = cur.next;
            cur.next = listNode;
        }
    }
    //删除第一个值为key的节点
    public boolean remove(int key) {
        ListNode cur = head;
        //链表第一个节点的值即为key,头删
        if(head.val == key){
            head = head.next;
            return true;
        }//通过遍历找到第一个值为key的节点的前驱节点
        while(cur.next != null){
            if(cur.next.val == key){//找到了,删除节点
                cur.next = cur.next.next;
                return true;
            }
            cur = cur.next;
        }
        return false;//未找到值未key的节点,返回false
    }
    //删除所有值为key的节点
    public void removeAllKey(int key) {
        //若链表为空,则直接返回null
        if(head == null){
            return;
        }
        ListNode cur = head;
        while(cur.next != null){
            if(cur.next.val == key){
                cur.next = cur.next.next;
            }else{
                cur = cur.next;
            }
        }
        //单独考虑头节点的值是否等于val
        if(head.val == key){
            head = head.next;
        }
    }
    //清空链表
    public void clear() {
        if(head == null){//链表本身为空
            return;
        }
        ListNode cur = head;
        while(cur != null){
            ListNode next = cur.next;//记录下一节点
            cur.next = null;
            cur = next;
        }
        //或直接将head置为null
        //head = null;
    }
}
更多推荐

word快捷键、conda一些安装问题、坐标转换初阶

整理一下今天所学吧!用博客来记录还是比较理想的,因为可以时常观看。首先是word:上下标:ctrl+=,ctrl+shift+=斜体:ctrl+ialt+=插入公式!!!选中:shift+方向矩阵公式可以直接选中进行添加和修改。接着是一些环境:anaconda中,在c盘不好用时,去d盘打开首先需要给它初始化了:cond

etcd之读性能主要影响因素

1、Raft模块-线性读ReadIndex-节点之间的RTT延时、磁盘IO线性读时Follower节点首先会向Raft模块发送ReadIndex请求,此时Raft模块会先向各节点发送心跳确认,一半以上节点确认Leader身份后由leader节点将已提交日志索引(committedindex)封装成ReadState结构

初识Java 10-1 集合

目录泛型和类型安全的集合基本概念添加一组元素打印集合ListIterator(迭代器)本笔记参考自:《OnJava中文版》在进行程序设计时我们会发现,程序总是会根据某些在运行时才能知道的条件来创建新的对象。这意味着,我们必须能够随时随地创建任意数量的对象。Java提供了几种方法来持有对象(的引用),其中之一就是数组。数

华为云云耀云服务器L实例评测|华为云上安装监控服务Prometheus三件套安装

文章目录华为云云耀云服务器L实例评测|华为云上试用监控服务Prometheus一、监控服务Prometheus三件套介绍二、华为云主机准备三、Prometheus安装四、Grafana安装五、alertmanager安装六、三个服务的启停管理1.Prometheus、Alertmanager和Grafana启动顺序2.

华为云HECS云服务器docker环境下安装redis

当前有个华为云HECS云服务器,已经安装了docker环境,准备下docker环境下安装redis。一、HECS云服务器安装docker登录华为HECS云服务器,安装docker环境。安装docker参考如下文章:华为云HECS安装docker并安装mysql-CSDN博客二、拉取redis镜像1、查询redis镜像d

Mybatis SQL构建器

上一篇我们介绍了在Mybatis映射器中使用@SelectProvider、@InsertProvider、@UpdateProvider、@DeleteProvider进行对数据的增删改查操作;本篇我们介绍如何使用SQL构建器在Provider中优雅的构建SQL语句。如果您对在Mybatis映射器中使用@Select

JVM——10.对象的内存布局

这篇文章,我们来了解一下对象在内存中的布局是什么样的。解释:前面有一篇文章我们讲了JVM中类的结构,那里讲的是一个java类,被编译成二进制字节码后,它的结构是什么样的,或者说按照jvm的标准,一个.class文件中类的结构是什么样的。而这里我们要讲的是根据类在堆中创建出来的对象,它在堆中是如何布局,即它在堆中的结构是

计算机操作系统 (王道考研)笔记(二)

重点知识点1内存1.1内存的基础知识1.1.1内存定义、作用1.1.2指令的工作原理1.1.3三种装入策略1.1.4从写程序到程序运行1.1.5链接的三种方式1.1.6总结1.2内存管理1.2.1内存空间的分配与回收a)连续分配管理b)非连续分配管理1)基本分页存储管理2)基本分段存储管理3)段页式存储管理1.2.2内

[LLM+AIGC] 01.应用篇之中文ChatGPT初探及利用ChatGPT润色论文对比浅析(文心一言 | 讯飞星火)

近年来,人工智能技术火热发展,尤其是OpenAI在2022年11月30日发布ChatGPT聊天机器人程序,其使用了Transformer神经网络架构(GPT-3.5),能够基于在预训练阶段所见的模式、统计规律和知识来生成回答,还能根据聊天的上下文进行互动,真正像人类一样来聊天交流以及完成复杂的NLP任务。基于此,为更好

Flutter粒子生成演示

演示:直接上代码:import'dart:math';import'dart:ui';import'package:flutter/material.dart';import'package:kq_flutter_widgets/widgets/chart/ex/extension.dart';classParticl

上传项目到github上

在github上先创建一个空仓库在github上新建一个仓库,点击你的头像,然后在出来的侧边栏选择Yourrepositories点击New创建一个新的仓库,即repository输入你的仓库名称,选择public或者private.尽量不要勾选README如果你的本地项目有readme文件的话,你在push的时候可能

热文推荐