首页 > 编程语言 >数据结构之链表(Java)

数据结构之链表(Java)

时间:2023-10-24 22:01:20浏览次数:35  
标签:Node index Java next 链表 数据结构 节点 指针

一:概述

数组是严格的正规军,那么链表就是灵活多变的地下党

链表是一种在物理上非连续、非顺序的数据结构,由若干节点(node) 所组成 单向链表的每一个节点又包含两部分,一部分是存放数据变量的data,另一部分是指向下一节点的指针next.

二:链表的具体说明

<1>链表的基本操作总括

*    链表的基本操作:
     *    1. 查找结点
     *    在查找元素时,链表不像数组那样可以通过下标快速定位,只能从头结点开始向后一个节点
     *    一个一个节点逐一查找
     *    例如给出一个链表,需要查找从头节点开始的第3个节点。
     *    (1) 第一步,将查找的指针定位到头节点
     *    (2) 第二步:根据头节点的next指针,定位到第2个节点。
     *    (3) 第三步:根据第2个节点的next指针,定位到第3个节点
     *     链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n)
     *
     *
     *     2. 更新节点
     *     如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,
     *     直接把旧数据换成新数据即可。
     *
     *     3 .插入节点
     *     与数组类似
     *     尾部插入
     *     头部插入
     *     中间插入
     *
     *     尾部插入是最简单的一种情况,把最后一个节点的next指针指向新的插入节点即可。
     *
     *     头部插入,可以分成两个步骤:
     *     第一步:把新结点的next指针指向原先的头节点
     *     第二步:把新结点变为链表的头节点
     *
     *     中间插入同样可以分为两个步骤:
     *     第一步:新节点的next指针,指向插入位置的节点。
     *     第二步:插入位置前置节点的next指针,指向新节点
     *     只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考虑
     *     扩容问题
     *
     *    4. 删除元素
     *    链表的删除操作同样分为三种情况:
     *    尾部删除
     *    头部删除
     *    中间删除
     *
     *    尾部删除是最简单的一种情况,把倒数第二个节点的next指针指向空即可
     *    头部删除:也很简单,把链表的头节点设为原先节点的next指针即可
     *    中间删除:同样很简单,把删除节点的前置节点的next指针,指向
     *    要删除元素的下一个节点即可。
     *    这里需要注意的是,许多的高级语言,如JAVA,拥有自动化的
     *    垃圾回收机制,所以我们不用刻意的去删除节点,只要没有外部引用指向它们,
     *    被删除的节点会自动被回收
     *
     *    如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作
     *    时间复杂度都是O(1)
     * */

<2>插入元素

// 头节点指针
    private Node head;
    // 尾节点指针
    private Node last;
    // 链表的实际长度
    private int size;

    /**
     * @param data   :插入元素
     * @param index: 插入位置
     */
    public void insert(int data, int index) throws Exception {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("超出链表的范围");
        }
        Node insertedNode = new Node(data);
        if (size == 0) {
            // 空链表
            head = insertedNode;
            last = insertedNode;
        } else if (index == 0) {
            // 插入头部
            insertedNode.next = head;
            head = insertedNode;
        } else if (size == index) {
            // 插入尾部
            insertedNode.next = last;
            last = insertedNode;
        } else {
            //插入中间
            Node prevNode = get(index - 1);
            insertedNode.next = prevNode.next;
            prevNode.next = insertedNode;
        }
        size++;
    }

<3>删除元素

// 头节点指针
    private Node head;
    // 尾节点指针
    private Node last;
    // 链表的实际长度
    private int size;  
/**
     * 删除链表的元素
     *
     * @param index: 删除位置
     */
    public Node remove(int index) throws Exception {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("超出链表节点范围");
        }
        Node removedNode = null;
        if (index == 0) {
            // 删除头节点
            removedNode = head;
            head = head.next;
        } else if (index == size - 1) {
            // 删除尾节点
            Node preNode = get(index - 1);
            removedNode = preNode.next;
            preNode.next = null;
            last = preNode;
        } else {
            // 删除中间节点
            Node prevNode = get(index - 1);
            Node nextNode = prevNode.next.next;
            removedNode = prevNode;
            prevNode.next = nextNode;
        }
        size--;
        return removedNode;
    }

<4>查找元素

// 头节点指针
    private Node head;
    // 尾节点指针
    private Node last;
    // 链表的实际长度
    private int size;
/**
     * 链表查找元素
     *
     * @param index: 查找的位置
     */
    public Node get(int index) throws Exception {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("超出链表节点范围!");
        }
        Node temp = head;
        for (int i = 0; i < index; i++) {
            temp = temp.next;
        }
        return temp;
    }

<5>输出,查找节点以及打印

/**
     * 输出链表
     */
    public void output() {
        Node temp = head;
        while (temp != null) {
            System.out.print(temp.data);
            temp = temp.next;
        }


    }

    /**
     * 链表节点
     */
    private static class Node {
        int data;
        Node next;

        Node(int data) {
            this.data = data;
        }
    }

public static  void main(String[] args) throws Exception {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.insert(3,0);
        myLinkedList.insert(7,1);
        myLinkedList.insert(9,2);
        myLinkedList.insert(5,3);
        myLinkedList.insert(6,1);
        myLinkedList.remove(0);
        myLinkedList.output();

    }

以上是对单链表相关操作的代码实现。为了尾部插入的方便,代码中额外增加了

 指向链表尾节点的last

数据结构之链表(Java)_链表

三:链表拓展

<1>链表总结

链表的第一个节点被称为头节点,最后一个节点被称为尾节点,尾节点的next指针指向空

 与数组按照下标来随机访问寻找元素不同,对于链表的其中一个节点A,只能根据节点A的

  next指针来找到该节点的下一个节点B,再根据节点B得next指针找到下一个节点C

  这正如地下党得联络方式一样,一级一级,单线传递

<2>双向链表

双向链表

* 双向链表比单向链表稍微复杂一点,它的每一个节点除了又有data和next指针之外,还拥有指向前

* 置节点的prev指针。

<3>链表的存储方式

链表的存储方式 数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储                                       

 随机存储:数组在内存中的占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表  的每一个节点分布在内存中的不同位置,依靠next指针关联起来。这样可以灵活的有效利用 零散的碎片空间

<4>数组与链表的性能对比

* 数组 VS 链表

* 数组和链表都属于线性的数据结构

* 数据结构没有绝对的好坏

* 数组和链表各有千秋

* 数组和链表相关操作性能,对比:

*       查找       更新      插入    删除

* 数组   O(1)      O(1)      O(n)   O(n)

* 链表   O(n)      O(1)      O(1)   O(1)

*

* 从表格中可以看出,数组的优势在于能够快速定位元素,

* 对于读操作多、写操作少的场景来说,用数组更合适一些

* 相反地,链表的优势在于能够灵活地进行插入和删除操作

* 如果需要在尾部频繁的插入、删除元素,用链表比较合适

* 一些

















































标签:Node,index,Java,next,链表,数据结构,节点,指针
From: https://blog.51cto.com/u_15912723/8010560

相关文章

  • Java内部类
    Java内部类详解详细解释内部内的一些使用规则的原因概览定义:在一个类的内部定义的类。它的定义位于另一个类的内部,并且可以访问外部类的成员,包括私有成员。为什么要用我觉得一个是为了符合OOP的封装原则,因为毕竟也可以直接把内部类函数和成员放到外面写。另外就是既然可......
  • javaweb学习每日总结-第四天
    第四天学习Mybatis 今天在昨天大概学习完mybatis的概念之后,今天跟着案例敲了一边代码,自己亲自操作了一边数据库,过程相对比较顺利,下面我说说自己的感悟把,首先敲代码之前要配置好自己的mybatis.xml文件,然后创建Java类来写方法和对象,创建xml文件,然后用mapper接口将两个文件连接......
  • Java基础 缓冲流为什么能提高性能?
    缓冲流为什么能提高性能?知识点:1个字节=1B缓冲流自带长度为8192的缓冲区,字节缓冲流的缓冲区是byte类型的,是长度为8192的字节数组,为8K;而字符缓冲流的缓冲区是char类型的,是长度为8192的字符数组,为16K,因为 Java中一个字符占两个字节通过缓冲区可以显著提高字节流......
  • java复习
    内部类有哪些分类?在Java中,可以将一个类的定义放在另外一个类的定义内部,这就是内部类。内部类本身就是类的一个属性,与其他属性定义方式一致。内部类的分类一般主要有四种:⚫成员内部类⚫局部内部类⚫匿名内部类⚫静态内部类静态内部类就是定义在类内部的静态类,静态内部......
  • Java基础 字符缓冲流
      字符流的基本流本身其实已经有缓冲区了,所以字符缓冲流提高的效率不是很明显。 字符缓冲流的构造方法:字符缓冲输入流:public BufferedReader(Reader r)  →  把基本流变成高级流字符缓冲输出流:public BufferedWriter(Writer r)  →  把基本流变成......
  • Java EasyExcel 随记
    JAR<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.7</version></dependency>入口EasyExcel.write(response.getOutputStream(),导出实体类.class).sheet("......
  • Java基础 字节缓冲流、字节缓冲流拷贝文件
    字节缓冲流:原理:底层自带了长度为8192的缓冲区。利用缓冲区可以一次读写8192个字节,从而提高性能public BufferedInputStream(InputStream is)  →  把基本流包装成高级流,提高读取数据的性能public BufferedOutputStream(OutputStream os)  →  把基本......
  • 【Java 进阶篇】JavaScript 自动跳转首页案例
    在这篇博客中,我们将创建一个JavaScript案例,演示如何自动跳转到网站的首页。这种自动跳转通常用于欢迎页面或广告页面等场景。我们将从头开始创建这个案例,逐步介绍相关的JavaScript知识,让初学者也能理解并实现这个功能。1.什么是自动跳转?自动跳转是指当用户访问一个网页时,页面会自......
  • 【Java 进阶篇】创建 JavaScript 轮播图:让网页焕发生机
    欢迎大家来到本篇博客,今天我们将一起探讨如何使用JavaScript创建一个精美的轮播图。轮播图是现代网站设计的关键元素之一,它能够使网页更加吸引人,提高用户体验。无需担心,本文将面向基础小白,从头开始解释每一步。我们将详细介绍如何构建一个轮播图,涵盖以下内容:什么是轮播图?创建HTML......
  • 【Java 进阶篇】JavaScript BOM(浏览器对象模型)详解
    BOM,即浏览器对象模型(BrowserObjectModel),是JavaScript与浏览器之间的接口,它允许JavaScript与浏览器进行交互,实现访问和控制浏览器窗口、文档和其他浏览器功能的功能。本文将详细介绍BOM的各个方面,包括窗口对象、定时器、历史记录、位置信息等,并提供示例代码来帮助您更好地理解和运......