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日

相关文章

  • C++数据结构关于栈迷宫求解示例

    C++数据结构关于栈迷宫求解示例攻略 在本篇攻略中,我们将使用C++数据结构中的栈来解决迷宫问题,具体将通过两个示例来详细讲解该方法。首先介绍一下栈的概念。 栈的概念 栈是一种“后入先出”的数据结构,即最后压入栈中的元素会首先被弹出,而最早压入栈中的元素会最后被弹出。栈的基本操作有入栈(push)、出栈(pop)、判断是否为空以及读取栈顶元素等。 迷宫问题 …

    数据结构 2023年5月17日
    00
  • C语言编程简单却重要的数据结构顺序表全面讲解

    C语言编程简单却重要的数据结构顺序表全面讲解 什么是顺序表? 顺序表是一种线性表,指的是一组有限元素的有限序列,其元素的逻辑顺序与它们在分配到的内存地址上的物理顺序相同或者等价。也就是说,顺序表中的元素按照其在内存中的位置依次存放。 顺序表的实现方式 顺序表的实现方式一般是使用数组,数组中的每一个元素对应着顺序表中的一个元素,位置相对应。 顺序表的优点 支持…

    数据结构 2023年5月17日
    00
  • C#数据结构揭秘一

    C#数据结构揭秘一攻略 C#数据结构是每个C#程序员必须熟练掌握的技能之一。本攻略将介绍常见的C#数据结构,包括数组、列表、栈、队列、散列表和字典。我们将会深入了解它们的特点、使用场景和使用方法,并附带代码示例加深理解。 数组 数组是存储单一类型元素的固定大小的集合结构。在C#中,可以使用以下方式声明和初始化一个数组: int[] nums1 = new i…

    数据结构 2023年5月17日
    00
  • JS中的算法与数据结构之二叉查找树(Binary Sort Tree)实例详解

    JS中的算法与数据结构:二叉查找树(Binary Sort Tree) 什么是二叉查找树 二叉查找树(Binary Sort Tree),又称二叉搜索树或二叉排序树,是一种特殊的二叉树结构。它具有以下性质: 每个结点最多只有两个子结点。 左子树中的所有结点的值均小于它的根结点的值。 右子树中的所有结点的值均大于它的根结点的值。 没有相同节点值出现 因为具备以…

    数据结构 2023年5月17日
    00
  • 数据结构之哈夫曼树与哈夫曼编码

    一、背景 编码是信息处理的基础(重新表示信息)。 普通的编码是等长编码,例如7位的ASCIL编码,对出现频率不同的字符都使用相同的编码长度。但其在传输和存储等情况下编码效率不高。 可使用不等长编码,来压缩编码:高频字符编码长度更短,低频字符编码长度更长。   [例] 将百分制的考试成绩转换成五分制的成绩 按顺序分别编码。 按频率分别编码(高频短编码,类似于香…

    算法与数据结构 2023年4月17日
    00
  • 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解

    解析从源码分析常见的基于Array的数据结构动态扩容机制的详解 什么是动态扩容机制 动态扩容机制是指,当一个数据结构达到其容量限制时,自动增加容量大小以继续存储新的数据。在动态扩容时,需要考虑到时间和空间的平衡,因为扩容需要分配新的内存空间,在处理大量数据时,需要尽可能减少空间浪费和分配内存的时间消耗。 基于Array的数据结构 Array是一种连续存储的数…

    数据结构 2023年5月17日
    00
  • Codeforces Round 866 (Div. 2)

    A. Yura’s New Name 题意: 给出一个仅由_或^组成的字符串,你可以在任意位置添加_或^字符,使得字符串满足:任意字符要么属于^_^的一部分,要么属于^^的一部分。求最少添加的字符数量。 分析: 对于_我们只需处理没有组成^_^的_: ①如果_在首位置且左边没有^则添加^ ②如果_在尾位置且右边没有^则添加^ ③如果_在中间部分且右边没有^则…

    算法与数据结构 2023年4月25日
    00
  • Java数据结构与算法学习之循环链表

    Java数据结构与算法学习之循环链表 本文将详细介绍Java数据结构中的一种链表类型——循环链表,并讲解如何使用Java实现循环链表。同时,本文也提供了两个示例,方便读者更好地理解和运用循环链表。 什么是循环链表 循环链表是一种链表,它与单向链表和双向链表不同之处在于它的最后一个结点指向第一个结点。这就形成了一个循环链,因此称之为循环链表。 如何实现循环链表…

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