数据结构之链表(Java)
  kIM7GUNpnV3x 2023年11月02日 70 0

一:概述

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

链表是一种在物理上非连续、非顺序的数据结构,由若干节点(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)

*

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

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

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

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

* 一些

















































【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  zLxnEsMLk4BL   2023年11月19日   29   0   0 数组字符串数组名
  gBkHYLY8jvYd   2023年11月19日   23   0   0 #include数组ci
  X5zJxoD00Cah   2023年11月19日   18   0   0 数组单引号字符串
  gBkHYLY8jvYd   2023年12月10日   22   0   0 #include数组i++
kIM7GUNpnV3x