java数据结构,线性表链式存储(单链表)的实现

简介: 文章讲解了单链表的基本概念和Java实现,包括头指针、尾节点和节点结构。提供了实现代码,包括数据结构、接口定义和具体实现类。通过测试代码演示了单链表的基本操作,如添加、删除、更新和查找元素,并总结了操作的时间复杂度。

单链表

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据域) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

在这里插入图片描述

头指针head和终端结点

单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。链表由头指针唯一确定,单链表可以用头指针的名字来命名。
终端结点无后继,故终端结点的指针域为空,即NULL。

单链表的实现代码

项目目录结构

  • 结构体 ListNode.java
  • 接口类 MyList.java
  • 实现类 SingleLinkedList.java
    在这里插入图片描述

基本的结构体

ListNode.java:

package day01线性表;
/*单链表的实现*/
public class ListNode {
   
    private Object data;//数据域
    private Object next;//指针域

    public ListNode(Object data) {
   
        this.data = data;
    }

    // getter 和 setter 方法,对应着c语言中的结构体指针指向
    // 也可以修改 data 和 next 两个成员变量的访问控制权限 ,直接通过 对象.成员变量来做
    public Object getData() {
   
        return data;
    }

    public void setData(Object data) {
   
        this.data = data;
    }

    public Object getNext() {
   
        return next;
    }

    public void setNext(Object next) {
   
        this.next = next;
    }
}

接口中的抽象方法

MyList.java:

package day01线性表;

public interface MyList {
   
    /**
     * 添加元素
     * @param element
     */
    void add(Object element);

    /**
     * 在指定位置插入元素
     * @param target
     * @param element
     */
    void add(int target,Object element);

    /**
     * 根据元素的值来删除
     * @param element
     */
    void delete(Object element);

    /**
     * 根据索引来删除元素
     * @param index
     */
    void delete(int index);

    /**
     * 根据指定的target索引位置来更新元素
     * @param traget
     * @param element
     */
    void update(int traget,Object element);

    /**
     * 返回某一元素第一次在线性表中出现的位置
     * @param element
     * @return
     */
    int indexOf(Object element);

    /**
     * 返回在指定target位置的元素
     * @param target
     * @return
     */
    Object elementAt(int target);

    /**
     * 取指定索引位置元素的前驱元素
     * @param index
     * @return
     */
    Object elementPrior(int index);

    /**
     * 取指定索引位置元素的后继元素
     * @param index
     * @return
     */
    Object elementNext(int index);

    /**
     * 是否包含某一元素
     * @param element
     * @return
     */
    boolean contains(Object element);
}

实现类

SingleLinkedList.java:

package day01线性表;

import java.util.LinkedList;

public class SingleLinkedList implements MyList{
   
    private ListNode head;//头节点
    private ListNode last;//尾节点
    private int size;//记录节点个数
    @Override
    public void add(Object element) {
   
        if(head==null){
   
            //一开始,头尾结点指向同一处
            head = new ListNode(element);
            last = head;
        }else{
   
            //尾插法
            last.setNext(new ListNode(element));
            //将 last 移动到自己的后继元素上
            last = (ListNode) last.getNext();
        }
        size++;
    }

    @Override
    public void add(int target, Object element) {
   
        //这里这个指定位置插入,对于单链表意义不大,所以就忽略不写了
    }

    @Override
    public void delete(Object element) {
   
        //同样还是先记录头结点
        ListNode p = head;
        ListNode pre = null;//前驱节点一开始为空,方便后来的删除
        // 因为单链表中的删除,就是将要删除的节点的前驱,直接指向要删除节点的后继节点
        while (p!=null){
   
            if(p.getData().equals(element)){
   
                //如果删除的是头节点的值,那么就直接修改head指向即可
                if(p==head){
   
                    head = (ListNode) head.getNext();
                    size--;
                    return;
                }else {
   
                    // 删除节点的前驱指向删除节点的后继
                    pre.setNext(p.getNext());
                    size--;
                    return;
                }
            }
            //没找到就一直往后面移动 pre 和 p
            pre = p;
            p = (ListNode) p.getNext();
        }
        //如果就是整个链表中都没找到则打印输出
        System.out.println("当前链表中中不存在值为"+element.toString()+"的元素,无法删除!");
    }

    @Override
    public void delete(int index) {
   
        //index越界判断
        if(index<0||index>=size){
   
            System.out.println("要访问的索引已经越界!无法进行删除");
            return;
        }
        int i = 0;//一开始头节点的索引为0
        ListNode p = head;
        ListNode pre = null;//头节点的前驱节点为空
        while (p!=null){
   
            if(i==index){
   
                //如果删除的是头节点的值,那么就直接修改head指向即可
                if(p==head){
   
                    head = (ListNode) head.getNext();
                    size--;
                    return;
                }else {
   
                    // 删除节点的前驱指向删除节点的后继
                    pre.setNext(p.getNext());
                    size--;
                    return;
                }
            }
            //没找到就一直往后面移动 pre 和 p
            pre = p;
            p = (ListNode) p.getNext();
            i++;
        }
    }

    @Override
    public void update(int traget, Object element) {
   
        //index越界判断
        if(traget<0||traget>=size){
   
            System.out.println("要访问的索引已经越界!无法进行修改");
            return;
        }
        int i = 0;//一开始头节点的索引为0
        ListNode p = head;
        while (p!=null){
   
            if(i==traget){
   
                p.setData(element);
                return;
            }
            //没找到就一直往后面移动 p
            p = (ListNode) p.getNext();
            i++;
        }
    }

    @Override
    public int indexOf(Object element) {
   
        int i = 0;
        ListNode p = head;
        while(p!=null){
   
            if(p.getData().equals(element)){
   
                return i;
            }
            p = (ListNode) p.getNext();
            i++;
        }
        return -1;//整个链表中并没有对应的元素,则返回-1
    }

    @Override
    public Object elementAt(int target) {
   
        if(target<0||target>=size){
   
            System.out.println("索引越界,已经超过链表的元素个数");
            return null;
        }
        int i = 0;
        ListNode p = head;
        while (p!=null) {
   
            if (i == target) {
   
                return p.getData();
            }
            p = (ListNode) p.getNext();
            i++;
        }
        return null;
    }

    @Override
    public Object elementPrior(int index) {
   
        //索引越界
        if(index-1<0||index>size){
   
            return null;
        }
        int i =0;
        ListNode p = head;
        while (p!=null){
   
            if (i==index-1){
   
                return p.getData();
            }
            p = (ListNode) p.getNext();
            i++;
        }
        return null;
    }

    @Override
    public Object elementNext(int index) {
   
        //索引越界
        if(index<0||index+1>size){
   
            return null;
        }
        int i =0;
        ListNode p = head;
        while (p!=null){
   
            if (i==index+1){
   
                return p.getData();
            }
            p = (ListNode) p.getNext();
            i++;
        }
        return null;
    }

    @Override
    public boolean contains(Object element) {
   
        ListNode p = head;
        while (p!=null){
   
            if(p.getData().equals(element)){
   
                return true;
            }
            p = (ListNode) p.getNext();
        }
        return false;
    }

    @Override
    public String toString() {
   
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        ListNode p = head;
        while (p!=null){
   
            stringBuilder.append(p.getData());
            if(p.getNext()!=null){
   
                stringBuilder.append(",");
            }
            p = (ListNode) p.getNext();
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }


    public static void main(String[] args) {
   
        SingleLinkedList list = new SingleLinkedList();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        System.out.println(list);
        list.delete("c");
        System.out.println(list);
        list.delete("a");
        System.out.println(list);
        list.delete("g");
        list.update(0,"x");
        System.out.println(list);
        list.delete(0);
        System.out.println(list);
        list.delete(10);
        list.update(3,"a");
        list.add("a");
        System.out.println(list);
        list.contains("a");
        System.out.println(list.elementAt(2));
        System.out.println(list.indexOf(1));
        System.out.println(list.indexOf("d"));
        System.out.println(list.indexOf("e"));
        list.add("b");
        list.add("g");
        System.out.println(list);
        System.out.println(list.elementPrior(1));
        System.out.println(list.elementNext(3));
        System.out.println(list.elementNext(5));
    }
}

这里mian方法最后的输出为:
在这里插入图片描述

线性表的总结

结合上一篇的顺序存储的总结java数据结构,线性表顺序存储(数组)的实现

  • 对于顺序存储,按顺序放在一起,相邻元素通过内存地址相邻产生联系,”随机存取“。
  • 而链式存储,元素随机放置在内存中任意位置,每个元素除了存放数据,也保存了其相邻元素的内存地址,来实现线性关系,“随机存取”。
  • 单链表的存储空间会比相同元素个数的线性表多,原因是单链表中包括了数据域data和指针域next,所以其存储密度是小于1的。

对与线性表中的基本操作的时间复杂度

1.查找元素,按值查找。

  • 给定长度为 n 的线性表,查找值为 v 的元素。(最坏)从头到尾遍历 => 时间复杂度O(n)

2.新增或者删除元素

  • 对于顺序表来说,新增或者删除元素,需要对应的右移或者左移,最坏的情况是挪动所有元素的位置,时间复杂度为O(n),空间复杂度为O(1).
  • 对于单链表来说,新增或者删除元素,只需要修改前驱元素的next即可。因线性链表在插⼊或删除时,不需要移动元素,只要修改指针,⼀般情况下时间复杂度为O(1)。但是,如果要在单链表中进⾏前插或删除操作,由于要从头查找前驱结点,所耗时间复杂度为O(n)。进⾏插⼊和删除的操作是常数即便,但是寻找前驱才要O(n)。空间复杂度为O(1).

3.更新元素

  • 顺序表更新元素可以直接访问数组下标去进行修改,时间复杂度为O(1).
  • 单链表则需从头节点开始遍历,最坏的情况是遍历整个链表,时间复杂度为O(n).

到此结束啦!java数据结构代码实现持续更新中!萌新学习笔记!
在这里插入图片描述

相关文章
|
4月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
59 1
|
4月前
|
存储 Java API
深入剖析Java Map:不只是存储数据,更是设计艺术的体现!
【10月更文挑战第17天】在Java编程中,Map是一种重要的数据结构,用于存储键值对,并展现了设计艺术的精髓。本文深入剖析了Map的设计原理和使用技巧,包括基本概念、设计艺术(如哈希表与红黑树的空间时间权衡)、以及使用技巧(如选择合适的实现类、避免空指针异常等),帮助读者更好地理解和应用Map。
134 3
|
4月前
|
存储 Java
告别混乱!用Java Map优雅管理你的数据结构
【10月更文挑战第17天】在软件开发中,随着项目复杂度增加,数据结构的组织和管理至关重要。Java中的Map接口提供了一种优雅的解决方案,帮助我们高效、清晰地管理数据。本文通过在线购物平台的案例,展示了Map在商品管理、用户管理和订单管理中的具体应用,有效提升了代码质量和维护性。
108 2
|
19天前
|
存储 分布式计算 Hadoop
基于Java的Hadoop文件处理系统:高效分布式数据解析与存储
本文介绍了如何借鉴Hadoop的设计思想,使用Java实现其核心功能MapReduce,解决海量数据处理问题。通过类比图书馆管理系统,详细解释了Hadoop的两大组件:HDFS(分布式文件系统)和MapReduce(分布式计算模型)。具体实现了单词统计任务,并扩展支持CSV和JSON格式的数据解析。为了提升性能,引入了Combiner减少中间数据传输,以及自定义Partitioner解决数据倾斜问题。最后总结了Hadoop在大数据处理中的重要性,鼓励Java开发者学习Hadoop以拓展技术边界。
38 7
|
24天前
|
存储 算法 C++
【C++数据结构——图】图的邻接矩阵和邻接表的存储(头歌实践教学平台习题)【合集】
本任务要求编写程序实现图的邻接矩阵和邻接表的存储。需掌握带权有向图、图的邻接矩阵及邻接表的概念。邻接矩阵用于表示顶点间的连接关系,邻接表则通过链表结构存储图信息。测试输入为图的顶点数、边数及邻接矩阵,预期输出为Prim算法求解结果。通关代码提供了完整的C++实现,包括输入、构建和打印邻接矩阵与邻接表的功能。
44 10
|
2月前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
52 5
|
2月前
|
存储 Java
Java 11 的String是如何优化存储的?
本文介绍了Java中字符串存储优化的原理和实现。通过判断字符串是否全为拉丁字符,使用`byte`代替`char`存储,以节省空间。具体实现涉及`compress`和`toBytes`方法,前者用于尝试压缩字符串,后者则按常规方式存储。代码示例展示了如何根据配置决定使用哪种存储方式。
|
3月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
65 6
|
3月前
|
存储 缓存 安全
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见。本文介绍了使用 `File.createTempFile` 方法和自定义创建临时文件的两种方式,详细探讨了它们的使用场景和注意事项,包括数据缓存、文件上传下载和日志记录等。强调了清理临时文件、确保文件名唯一性和合理设置文件权限的重要性。
253 2
|
3月前
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。

热门文章

最新文章