每日学习一个数据结构-链表

文章目录

    • 什么是链表?
    • 链表的Java实现
      • [1. 单向链表(Singly Linked List)](#1. 单向链表(Singly Linked List))
      • [2. 双向链表(Doubly Linked List)](#2. 双向链表(Doubly Linked List))
      • [3. 循环链表(Circular Linked List,这里以单向循环链表为例)](#3. 循环链表(Circular Linked List,这里以单向循环链表为例))
      • [4. 带哨兵节点的链表(以单向链表为例)](#4. 带哨兵节点的链表(以单向链表为例))

什么是链表?

链表(Linked List)是一种常见的数据结构,由一系列节点(Node)组成,每个节点包含两个部分:一部分用于存储数据(称为数据域),另一部分用于存储指向下一个节点的指针(或链接、引用)。链表是线性数据结构的一种,但与数组不同的是,链表中的元素在内存中的存储位置不必连续。

链表有多种类型,主要包括以下几种:

  1. 单向链表(Singly Linked List):

    • 每个节点包含一个数据域和一个指向下一个节点的指针。
    • 最后一个节点的指针为 null 或 None,表示链表的结束。
  2. 双向链表(Doubly Linked List):

    • 每个节点包含一个数据域、一个指向下一个节点的指针和一个指向前一个节点的指针。
    • 第一个节点的指向前一个节点的指针为 null 或 None,最后一个节点的指向下一个节点的指针为 null 或 None。
  3. 循环链表(Circular Linked List):

    • 最后一个节点的指针指向第一个节点,形成一个循环。
    • 可以是单向循环链表或双向循环链表。
  4. 带哨兵节点(Sentinel Node)的链表:

    • 在链表头部添加一个哨兵节点,简化链表操作中的边界条件处理。
    • 哨兵节点不存储实际数据,只是起到标记作用。

链表的主要操作包括:

  • 插入(Insert):在链表的某个位置插入一个新的节点。
  • 删除(Delete):删除链表中的某个节点。
  • 查找(Search):查找链表中是否包含某个值或某个节点。
  • 遍历(Traverse):从头到尾遍历链表中的每个节点。

链表的优点:

  • 插入和删除操作高效,不需要移动大量元素。
  • 动态性强,可以方便地调整链表的大小。

链表的缺点:

  • 需要额外的存储空间来存储指针。
  • 访问某个特定元素(非头节点)需要从头节点开始遍历,效率较低。

链表在多种应用场景中都有广泛应用,如实现队列、栈、哈希表等数据结构,以及在一些算法中如图的遍历、路径搜索等。

链表的Java实现

在Java中,我们可以使用类和对象来实现上文提到的四种链表:单向链表、双向链表、循环链表和带哨兵节点的链表。以下是每种链表的简单实现:

1. 单向链表(Singly Linked List)

java 复制代码
class SinglyLinkedListNode {
    int data;
    SinglyLinkedListNode next;

    public SinglyLinkedListNode(int data) {
        this.data = data;
        this.next = null;
    }
}

class SinglyLinkedList {
    private SinglyLinkedListNode head;

    // Add methods to insert, delete, search, and traverse the list
}

2. 双向链表(Doubly Linked List)

java 复制代码
class DoublyLinkedListNode {
    int data;
    DoublyLinkedListNode next;
    DoublyLinkedListNode prev;

    public DoublyLinkedListNode(int data) {
        this.data = data;
        this.next = null;
        this.prev = null;
    }
}

class DoublyLinkedList {
    private DoublyLinkedListNode head;

    // Add methods to insert, delete, search, and traverse the list
}

3. 循环链表(Circular Linked List,这里以单向循环链表为例)

java 复制代码
class CircularLinkedListNode {
    int data;
    CircularLinkedListNode next;

    public CircularLinkedListNode(int data) {
        this.data = data;
        this.next = null;
    }
}

class CircularLinkedList {
    private CircularLinkedListNode tail; // Using tail for simplicity in insertion

    public CircularLinkedList() {
        this.tail = null;
    }

    // Special insertion method to handle circular nature
    public void insert(int data) {
        CircularLinkedListNode newNode = new CircularLinkedListNode(data);
        if (tail == null) {
            tail = newNode;
            newNode.next = newNode; // Point to itself to form a circle with one node
        } else {
            newNode.next = tail.next; // Point to the head (which is tail.next in a circular list)
            tail.next = newNode; // Insert newNode before the "head" (which is actually the second node in the circle)
            tail = newNode; // Move the tail pointer to the newly inserted node
        }
    }

    // Add methods to delete, search, and traverse the list (considering the circular nature)
}

注意 :上面的循环链表实现为了简化插入操作而使用了tail指针。在实际应用中,你可能需要维护一个head指针来方便地访问链表的开始,并相应地调整插入和删除操作。

4. 带哨兵节点的链表(以单向链表为例)

java 复制代码
class SentinelLinkedListNode {
    int data;
    SentinelLinkedListNode next;

    public SentinelLinkedListNode() {
        this.data = Integer.MIN_VALUE; // Use a special value to denote a sentinel node
        this.next = null;
    }

    public SentinelLinkedListNode(int data) {
        this.data = data;
        this.next = null;
    }
}

class SentinelLinkedList {
    private SentinelLinkedListNode sentinel; // Sentinel node acts as the head

    public SentinelLinkedList() {
        this.sentinel = new SentinelLinkedListNode(); // Initialize with the sentinel node
    }

    // Insertion method considering the sentinel node
    public void insert(int data) {
        SentinelLinkedListNode newNode = new SentinelLinkedListNode(data);
        SentinelLinkedListNode current = sentinel;
        while (current.next != null && current.next != sentinel) { // Traverse to the last non-sentinel node
            current = current.next;
        }
        current.next = newNode;
        newNode.next = sentinel; // Point to the sentinel to maintain the circular reference (optional, depending on design)
        // Alternatively, you can keep it non-circular and set newNode.next to null if you don't need circular behavior
    }

    // Note: You might want to adjust the traversal method to skip the sentinel node when displaying the list
    // Add other methods to delete, search, and traverse the list (considering the sentinel node)
}

注意 :在带哨兵节点的链表中,哨兵节点通常不存储有效数据,而是用作链表的开始或结束标记。上面的实现中,我使用了Integer.MIN_VALUE来标记哨兵节点,但在实际应用中,你可能需要定义一个更明确的标记方式,比如使用null值配合特定的逻辑来处理哨兵节点。另外,上面的循环引用(newNode.next = sentinel)是可选的,取决于你是否需要链表保持循环结构。如果不需要循环结构,可以将newNode.next设置为null。

请注意,上述代码只是每种链表的基本框架,并没有包含完整的插入、删除、搜索和遍历方法。你需要根据具体需求来实现这些方法。

相关推荐
鬓戈40 分钟前
Rust 语言与 AI 应用生态调研及学习路径
人工智能·学习·rust
传奇开心果编程1 小时前
【SwiftUI娓娓道来】第3课:让界面活起来——交互与动画指南
学习·macos·ui·ios·swiftui·swift
xian_wwq1 小时前
【学习笔记】等保合规安全基线核查实战
笔记·学习
传奇开心果编程2 小时前
【Jetpack Compose进阶学与练】第14课:系列收尾复习总结;Compose项目常见坑点汇总;学习路线与后续学习方向
android·学习·ui·kotlin·android jetpack
yangmu32032 小时前
发动机是怎么把油变成动力的:四冲程内燃机原理拆解
学习
m4Rk_2 小时前
【论文阅读】Agent 记忆机制(81):EMR——用情景记忆避免 Agent 在多步推理中反复绕圈
论文阅读·人工智能·学习·开源·github
海海不掉头发2 小时前
阶段五_实验34-38_学习总结
深度学习·神经网络·学习·机器学习
M78佐菲2 小时前
ARM学习笔记(11)
linux·arm开发·笔记·嵌入式硬件·学习
M78佐菲2 小时前
ARM学习笔记(10)
linux·arm开发·笔记·嵌入式硬件·学习
dadaobusi3 小时前
学习:PCIe原子操作
学习