Java数据结构之链表详解

Java数据结构之链表详解

什么是链表?

链表是一种基本的动态数据结构,它的基本思想是利用指针将一些零散的内存块串联起来,形成一个逻辑上的整体。链表由一些称为节点的元素组成,每个节点保存两个部分:数据和指向下一个节点的指针。相比于数组这种静态数据结构,链表具有动态性,我们可以通过动态的增加或删除节点来改变链表的大小。

链表的分类

  1. 单向链表:每个节点只有一个指向下一个节点的指针。
  2. 双向链表:每个节点既有一个指向下一个节点的指针,也有一个指向前一个节点的指针。这样做可以使得链表具备双向遍历的能力。
  3. 循环链表:形式上是一个环形结构的链表,也就是最后一个节点指向第一个节点,或者说第一个节点的前驱节点是最后一个节点。
  4. 双向循环链表:结合前面两种链表的特点,它既可以双向遍历,也可以形成一个闭合的环形结构。

链表的基本操作

插入操作

链表插入操作可以分为两类:头插法和尾插法,它们的区别在于插入节点的位置不同。

头插法

头插法是将新增节点插入到链表的头部,即成为新的头节点。具体步骤如下:

  1. 创建一个新的节点,将数据存入其中。
  2. 将这个节点的next指针指向原链表的头节点。
  3. 更新链表的头节点指针指向这个新节点。

代码示例如下:

public void insertAtBeginning(int data) {
    Node newNode = new Node(data);
    newNode.next = head;
    head = newNode;
}

尾插法

与头插法不同,尾插法是将新增节点插入到链表的尾部,成为链表的最后一个节点。具体步骤如下:

  1. 创建一个新的节点,将数据存入其中。
  2. 遍历链表,找到最后一个节点。
  3. 将最后一个节点的next指针指向新节点。

代码示例如下:

public void insertAtEnd(int data) {
    Node newNode = new Node(data);
    if (head == null) {
        head = newNode;
    } else {
        Node last = head;
        while (last.next != null) {
            last = last.next;
        }
        last.next = newNode;
    }
}

删除操作

链表删除操作主要有两种方式:删除头节点和删除指定节点。具体操作如下:

删除头节点

  1. 更新链表的头节点指针指向下一个节点。
  2. 释放原头节点的空间。

代码示例如下:

public void deleteAtBeginning() {
    if (head != null) {
        Node temp = head;
        head = head.next;
        temp.next = null;
        temp = null;
    }
}

删除指定节点

  1. 遍历链表,找到要删除节点的前驱节点。
  2. 更新前驱节点的next指针。

代码示例如下:

public void deleteByValue(int data) {
    if (head == null) {
        return;
    }
    if (head.data == data) {
        head = head.next;
        return;
    }
    Node temp = head;
    while (temp.next != null && temp.next.data != data) {
        temp = temp.next;
    }
    if (temp.next == null) {
        return;
    }
    temp.next = temp.next.next;
}

链表的应用示例

单向链表实现栈

栈是一种先进后出(FILO)的数据结构,我们可以使用单向链表来实现栈。具体操作如下:

  1. 创建一个空的单向链表,它的头节点为null。
  2. 将新元素插入到链表的头部。
  3. 将链表的头节点作为栈顶,返回它的数据。

代码示例如下:

public class StackByLinkedList {

    private Node head = null;

    public void push(int data) {
        Node newNode = new Node(data);
        newNode.next = head;
        head = newNode;
    }

    public int pop() {
        if (head == null) {
            throw new RuntimeException("Stack is empty");
        }
        int data = head.data;
        head = head.next;
        return data;
    }

    public int peek() {
        if (head == null) {
            throw new RuntimeException("Stack is empty");
        }
        return head.data;
    }

    public boolean isEmpty() {
        return head == null;
    }

    private static class Node {
        int data;
        Node next;

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

双向循环链表实现定时器

定时器是一种常见的应用场景,我们可以使用双向循环链表来实现定时器。具体操作如下:

  1. 创建一个双向循环链表,它的头节点为null。
  2. 将新元素插入到链表尾部,并且根据任务定时时间排序。
  3. 使用线程循环遍历链表,若发现任务到达定时时间,将该任务从链表中删除,并执行任务。

代码示例如下:

public class TimerByLinkedList {

    private TimerTask head = null;

    public synchronized void addTimerTask(TimerTask task) {
        // 略去排序逻辑
        if (head == null) {
            head = task;
            head.prev = head.next = head; // 自己构成转换素环形的节点
        } else {
            TimerTask tail = head.prev;
            tail.next = task;
            task.prev = tail;
            task.next = head;
            head.prev = task;
        }
        notify();
    }

    public synchronized void start() {
        while (true) {
            while (head == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            long delay = head.executeTime - System.currentTimeMillis();
            if (delay <= 0) {
                head.run();
                head = head.next;
                if (head == head.next) {
                    head = null;
                } else {
                    head.prev.next = head; //删除已经执行的任务
                    head.prev = head.prev.prev;
                }
            }
            else {
                try {
                    wait(delay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static abstract class TimerTask implements Runnable, Comparable<TimerTask> {

        long executeTime;
        TimerTask prev;
        TimerTask next;

        public TimerTask(long executeTime) {
            this.executeTime = executeTime;
        }

        @Override
        public int compareTo(TimerTask o) {
            return Long.compare(executeTime, o.executeTime);
        }
    }
}

总结

链表是一种经典的动态数据结构,其应用场景非常广泛。本文介绍了链表的基本操作,以及一些常见的链表应用示例。需要注意的是,在链表插入和删除操作中要注意避免出现空指针异常,以及链表插入时要注意区分头插法和尾插法。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java数据结构之链表详解 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 一、对系统理论的认识

           经过一周的时间学习,我们知道了系统的定义:是一个由一组相互连接的要素构成的,能够实现某个目标的整体,任何一个系统都包括三种构成要件:要素连接,功能或目标。       1.系统的连接使得系统呈现特定的结构,使得系统的各个部件连接而产生系统特有的功能—相关性导新功能涌现。连接的媒介—“三流”(信息流,能量流,物质流)。       2.系统的静态…

    算法与数据结构 2023年4月18日
    00
  • 带头节点的单链表的思路及代码实现

    带头节点的单链表的思路及代码实现(JAVA) 一、什么是的单链表 ①标准定义 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) +指针(指示后继元素存储位置,元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。) 以上是标准定义不太好让人对单链表有直观…

    算法与数据结构 2023年4月17日
    00
  • 数据结构之堆详解

    数据结构之堆详解 什么是堆? 堆(Heap)是一种特殊的树形数据结构。堆具有以下两个特点: 堆是一颗完全二叉树; 堆中每个节点的值都必须大于等于或小于等于其左右子节点的值,分别称作大根堆和小根堆。 上述的大根堆和小根堆其实是两种不同的堆的实现方式。对于大根堆,每个节点的值都比其左右子节点的值要大;小根堆则相反,每个节点的值都比其左右子节点的值要小。 堆的基本…

    数据结构 2023年5月17日
    00
  • 数据结构 双向链表的创建和读取详解及实例代码

    下面我为你详细讲解“数据结构 双向链表的创建和读取详解及实例代码”的完整攻略。 什么是双向链表? 双向链表是一种常见的线性数据结构,与单向链表相比,它可以在节点之间建立双向连接,使得在需要反向遍历链表时效率更高。每个节点同时保存了指向前一个节点和后一个节点的指针,因此双向链表也叫做双链表。 双向链表的创建 定义节点类 首先,我们需要定义一个表示节点的类,该类…

    数据结构 2023年5月16日
    00
  • InputStream数据结构示例解析

    InputStream数据结构示例解析 InputStream是Java中一个重要的数据结构,它表示可以从其中读取数据的输入流。通常情况下,它表示的是用来读取字节流数据的输入流。在本篇攻略中,我们将会详细解释如何使用InputStream数据结构来读取字节流数据,并且给出两条具体的读取示例。 InputStream类的继承结构 InputStream类是一个…

    数据结构 2023年5月17日
    00
  • C++ 数据结构之kmp算法中的求Next()函数的算法

    C++ 数据结构之kmp算法中的求Next()函数的算法 什么是KMP算法和Next()函数 KMP算法(Knuth-Morris-Pratt算法)是一种字符串匹配算法,能够解决的问题是,在一个文本串S中查找一个模式串P是否出现并且返回第一次出现的位置。而Next()函数则是在KMP算法中使用的一个关键的子函数,用于计算模式串P中每个前缀的最长相同真前缀和后…

    数据结构 2023年5月17日
    00
  • [Week 19]每日一题(C++,数学,并查集,动态规划)

    目录 [Daimayuan] T1 倒数第n个字符串(C++,进制) 输入格式 输出格式 样例输入 样例输出 解题思路 [Daimayuan] T2 排队(C++,并查集) 输入格式 输出格式 样例输入1 样例输出1 样例输入2 样例输出2 样例输入3 样例输出3 数据规模 解题思路 [Daimayuan] T3 素数之欢(C++,BFS) 数据规模 输入格…

    算法与数据结构 2023年5月4日
    00
  • 数据结构用两个栈实现一个队列的实例

    下面我将详细讲解“数据结构用两个栈实现一个队列的实例”的完整攻略。 一、背景 在队列和栈这两种数据结构中,都可以实现数据的存储和操作。栈是一种后进先出(LIFO)的数据结构,而队列则是一种先进先出(FIFO)的数据结构。在实际应用中,很多场景需要同时具备队列和栈的特性,且要求效率较高,这时候就需要用两个栈实现一个队列的问题来解决了。 二、解决方案 考虑采用两…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部