链表知识大全

68 阅读5分钟

前言. 链表概述

  • 定义:链表是一种线性数据结构,其中每个元素是一个对象,称为节点。每个节点包含两个部分:数据和一个指向下一个节点的引用(或指针)。

  • 类型

    • 单向链表(Singly Linked List)
    • 双向链表(Doubly Linked List)
    • 循环链表(Circular Linked List)

1. 链表概述

比喻:把链表想象成一条队伍,队伍中的每个人就是一个节点,每个人都知道下一个人的信息(单向链表)或者知道前后两个人的信息(双向链表)。

2. 单向链表(Singly Linked List)

比喻:这是一条单向队伍,每个人只能知道下一个人是谁,不能知道前一个人是谁。

结构

  • 节点:队伍中的人,包含自己的信息(数据)和指向下一个人的引用(指针)。
  • 头节点:队伍的第一个人。
  • 尾节点:队伍的最后一个人,指向空(null)。

基本操作

  • 创建链表:创建一个空的队伍。
  • 插入节点:在队伍的开头或结尾增加一个人。
  • 删除节点:从队伍中移除某个人。
  • 遍历链表:从队伍的第一个人开始,一个接一个地数过去。
  • 搜索节点:找到队伍中某个特定的人。

模型展示

class Node {
    int data; // 个人信息
    Node next; // 下一个人的指针
    Node(int data) {
        this.data = data;
        this.next = null;
    }
}

class SinglyLinkedList {
    Node head; // 队伍的第一个人

    // 插入到队伍开头
    public void insertAtHead(int data) {
        Node newNode = new Node(data);
        newNode.next = head; // 新人排在第一个人前面
        head = newNode; // 新人变成队伍的第一个人
    }

    // 插入到队伍尾部
    public void insertAtEnd(int data) {
        Node newNode = new Node(data);
        if (head == null) { // 如果队伍没有人
            head = newNode; // 新人变成第一个人
            return;
        }
        Node temp = head;
        while (temp.next != null) { // 找到队伍的最后一个人
            temp = temp.next;
        }
        temp.next = newNode; // 新人排在最后一个人后面
    }

    // 删除指定值的节点
    public void deleteNode(int key) {
        Node temp = head, prev = null;

        // 如果第一个人就是要删除的人
        if (temp != null && temp.data == key) {
            head = temp.next; // 第一个人离开队伍
            return;
        }

        // 搜索要删除的人
        while (temp != null && temp.data != key) {
            prev = temp;
            temp = temp.next;
        }

        // 如果找不到要删除的人
        if (temp == null) return;

        // 断开连接
        prev.next = temp.next;
    }

    // 遍历队伍
    public void traverse() {
        Node temp = head;
        while (temp != null) {
            System.out.print(temp.data + " -> ");
            temp = temp.next;
        }
        System.out.println("null");
    }
}

3. 双向链表(Doubly Linked List)

比喻:这是一条双向队伍,每个人不仅知道下一个人是谁,还知道前一个人是谁。

结构

  • 节点:队伍中的人,包含自己的信息(数据)、指向下一个人的引用(指针)和指向前一个人的引用(指针)。
  • 头节点:队伍的第一个人。
  • 尾节点:队伍的最后一个人,指向空(null)。

基本操作

  • 创建链表:创建一个空的队伍。
  • 插入节点:在队伍的开头或结尾增加一个人。
  • 删除节点:从队伍中移除某个人。
  • 遍历链表:从队伍的第一个人开始,一个接一个地数过去,或者从最后一个人开始,倒着数回去。
  • 搜索节点:找到队伍中某个特定的人。

模型展示

class DoublyNode {
    int data; // 个人信息
    DoublyNode next; // 下一个人的指针
    DoublyNode prev; // 上一个人的指针
    DoublyNode(int data) {
        this.data = data;
        this.next = null;
        this.prev = null;
    }
}

class DoublyLinkedList {
    DoublyNode head; // 队伍的第一个人

    // 插入到队伍开头
    public void insertAtHead(int data) {
        DoublyNode newNode = new DoublyNode(data);
        if (head != null) {
            head.prev = newNode;
        }
        newNode.next = head; // 新人排在第一个人前面
        head = newNode; // 新人变成队伍的第一个人
    }

    // 插入到队伍尾部
    public void insertAtEnd(int data) {
        DoublyNode newNode = new DoublyNode(data);
        if (head == null) { // 如果队伍没有人
            head = newNode; // 新人变成第一个人
            return;
        }
        DoublyNode temp = head;
        while (temp.next != null) { // 找到队伍的最后一个人
            temp = temp.next;
        }
        temp.next = newNode; // 新人排在最后一个人后面
        newNode.prev = temp;
    }

    // 删除指定值的节点
    public void deleteNode(int key) {
        DoublyNode temp = head;

        // 搜索要删除的人
        while (temp != null && temp.data != key) {
            temp = temp.next;
        }

        // 如果找不到要删除的人
        if (temp == null) return;

        // 断开连接
        if (temp.prev != null) {
            temp.prev.next = temp.next;
        } else {
            head = temp.next;
        }
        if (temp.next != null) {
            temp.next.prev = temp.prev;
        }
    }

    // 正向遍历队伍
    public void traverseForward() {
        DoublyNode temp = head;
        while (temp != null) {
            System.out.print(temp.data + " -> ");
            temp = temp.next;
        }
        System.out.println("null");
    }

    // 反向遍历队伍
    public void traverseBackward() {
        DoublyNode temp = head;
        while (temp != null && temp.next != null) {
            temp = temp.next;
        }
        while (temp != null) {
            System.out.print(temp.data + " -> ");
            temp = temp.prev;
        }
        System.out.println("null");
    }
}

在index = 3处添加33

add.gif

删除index = 2的元素 del.gif

4. 循环链表(Circular Linked List)

比喻:这是一条循环队伍,每个人不仅知道下一个人是谁,队伍的最后一个人还知道第一个人是谁,形成一个环。

结构

  • 节点:队伍中的人,包含自己的信息(数据)和指向下一个人的引用(指针)。
  • 头节点:队伍的第一个人。
  • 尾节点:队伍的最后一个人,指向头节点。

基本操作

  • 创建链表:创建一个空的队伍。
  • 插入节点:在队伍的开头或结尾增加一个人。
  • 删除节点:从队伍中移除某个人。
  • 遍历链表:从队伍的第一个人开始,一个接一个地数过去,直到回到头部。
  • 搜索节点:找到队伍中某个特定的人。

模型展示

class CircularNode {
    int data; // 个人信息
    CircularNode next; // 下一个人的指针
    CircularNode(int data) {
        this.data = data;
        this.next = null;
    }
}

class CircularLinkedList {
    CircularNode head; // 队伍的第一个人

    // 插入到队伍开头
    public void insertAtHead(int data) {
        CircularNode newNode = new CircularNode(data);
        if (head == null) {
            head = newNode;
            newNode.next = head;
        } else {
            CircularNode temp = head;
            while (temp.next != head) {
                temp = temp.next;
            }
            temp.next = newNode;
            newNode.next = head;
            head = newNode;
        }
    }

    // 插入到队伍尾部
    public void insertAtEnd(int data) {
        CircularNode newNode = new CircularNode(data);
        if (head == null) {
            head = newNode;
            newNode.next = head;
        } else {
            CircularNode temp = head;
            while (temp.next != head) {
                temp = temp.next;
            }
            temp.next = newNode;
            newNode.next = head;
        }
    }

    // 删除指定值的节点
    public void deleteNode(int

 key) {
        if (head == null) return;

        CircularNode temp = head, prev = null;

        // 如果第一个人就是要删除的人
        if (temp.data == key) {
            while (temp.next != head) {
                temp = temp.next;
            }
            temp.next = head.next;
            head = head.next;
            return;
        }

        // 搜索要删除的人
        while (temp.next != head && temp.data != key) {
            prev = temp;
            temp = temp.next;
        }

        // 如果找不到要删除的人
        if (temp.data != key) return;

        // 断开连接
        prev.next = temp.next;
    }

    // 遍历队伍
    public void traverse() {
        if (head == null) return;
        CircularNode temp = head;
        do {
            System.out.print(temp.data + " -> ");
            temp = temp.next;
        } while (temp != head);
        System.out.println("(head)");
    }
}

5. 时间复杂度和空间复杂度

  • 时间复杂度
    • 插入操作:O(1)(头部插入),O(n)(尾部插入)
    • 删除操作:O(1)(头部删除),O(n)(搜索删除)
    • 搜索操作:O(n)
    • 遍历操作:O(n)
  • 空间复杂度:O(n),其中n是节点数。

6. 总结

链表就像一条队伍,单向链表是单向队伍,双向链表是双向队伍,循环链表是一条环形队伍。通过这些比喻和建模,希望你能更容易理解链表的结构和操作。