java链表知识点合集

链表(Linked List)是 Java 中一种重要的数据结构,广泛应用于各种场景中。以下是关于链表的全面介绍,包括基本概念、实现方式、常用操作、优缺点以及实际应用。


1. 链表的基本概念

链表是一种线性数据结构,由一系列节点(Node)组成,每个节点包含两部分:

  1. 数据域:存储数据。

  2. 指针域:指向下一个节点的引用。

链表的特点:

  • 动态内存分配:链表的大小可以动态增长或缩小。

  • 非连续存储:节点在内存中不一定是连续存储的。


2. 链表的类型

(1)单链表(Singly Linked List)
  • 每个节点只有一个指针,指向下一个节点。

  • 最后一个节点的指针指向 null

  • 示例

    class Node {
        int data;
        Node next;
    
        Node(int data) {
            this.data = data;
            this.next = null;
        }
    }
(2)双链表(Doubly Linked List)
  • 每个节点有两个指针,分别指向前一个节点和后一个节点。

  • 示例

    class Node {
        int data;
        Node prev;
        Node next;
    
        Node(int data) {
            this.data = data;
            this.prev = null;
            this.next = null;
        }
    }
(3)循环链表(Circular Linked List)
  • 单链表或双链表的变种,最后一个节点的指针指向头节点。

  • 示例

    class Node {
        int data;
        Node next;
    
        Node(int data) {
            this.data = data;
            this.next = this; // 指向自身
        }
    }

3. LinkedList 的常用方法

以下是 LinkedList 的常用方法及其示例:

(1)添加元素
方法描述
add(E e)在链表末尾添加元素。
add(int index, E element)在指定位置插入元素。
addFirst(E e)在链表头部添加元素。
addLast(E e)在链表尾部添加元素。

示例

LinkedList<String> list = new LinkedList<>();
list.add("Apple"); // [Apple]
list.addFirst("Banana"); // [Banana, Apple]
list.addLast("Cherry"); // [Banana, Apple, Cherry]
list.add(1, "Orange"); // [Banana, Orange, Apple, Cherry]
(2)删除元素
方法描述
remove()删除并返回链表头部元素。
remove(int index)删除指定位置的元素。
remove(Object o)删除第一个匹配的元素。
removeFirst()删除并返回链表头部元素。
removeLast()删除并返回链表尾部元素。

示例

LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C", "D"));
list.remove(); // 删除 "A",剩余 [B, C, D]
list.remove(1); // 删除 "C",剩余 [B, D]
list.remove("B"); // 删除 "B",剩余 [D]
list.removeLast(); // 删除 "D",剩余 []
(3)获取元素
方法描述
get(int index)返回指定位置的元素。
getFirst()返回链表头部元素。
getLast()返回链表尾部元素。

示例

LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));
System.out.println(list.get(1)); // 输出: B
System.out.println(list.getFirst()); // 输出: A
System.out.println(list.getLast()); // 输出: C
(4)修改元素
方法描述
set(int index, E element)修改指定位置的元素。

示例

LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));
list.set(1, "X"); // [A, X, C]
(5)遍历链表
方法描述
iterator()返回链表的迭代器。
listIterator()返回链表的列表迭代器。

示例

LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));

// 使用迭代器遍历
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.print(iterator.next() + " ");
}

// 使用增强 for 循环遍历
for (String item : list) {
    System.out.print(item + " ");
}
(6)其他常用方法
方法描述
size()返回链表的元素个数。
clear()清空链表。
contains(Object o)判断链表是否包含指定元素。
indexOf(Object o)返回指定元素第一次出现的索引。
lastIndexOf(Object o)返回指定元素最后一次出现的索引。

示例

LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C", "B"));
System.out.println(list.size()); // 输出: 4
System.out.println(list.contains("B")); // 输出: true
System.out.println(list.indexOf("B")); // 输出: 1
System.out.println(list.lastIndexOf("B")); // 输出: 3
list.clear(); // 清空链表

4. LinkedList 作为队列和栈

LinkedList 实现了 Deque 接口,因此可以作为队列或栈使用。

(1)作为队列
方法描述
offer(E e)在队列尾部添加元素。
poll()删除并返回队列头部元素。
peek()返回队列头部元素(不删除)。

示例

LinkedList<String> queue = new LinkedList<>();
queue.offer("A"); // [A]
queue.offer("B"); // [A, B]
System.out.println(queue.poll()); // 输出: A,剩余 [B]
System.out.println(queue.peek()); // 输出: B,剩余 [B]
(2)作为栈
方法描述
push(E e)在栈顶添加元素。
pop()删除并返回栈顶元素。

示例

LinkedList<String> stack = new LinkedList<>();
stack.push("A"); // [A]
stack.push("B"); // [B, A]
System.out.println(stack.pop()); // 输出: B,剩余 [A]

5. 性能分析

操作时间复杂度
插入/删除(头部或尾部)O(1)
插入/删除(中间位置)O(n)
随机访问O(n)
遍历O(n)

4. 链表的常用操作

(1)插入
  • 头插法:在链表头部插入节点。

  • 尾插法:在链表尾部插入节点。

  • 中间插入:在指定位置插入节点。

(2)删除
  • 删除头节点

  • 删除尾节点

  • 删除指定值的节点

(3)查找
  • 按值查找:查找链表中是否存在某个值。

  • 按索引查找:查找链表中第 k 个节点。

(4)遍历
  • 从头到尾遍历:输出链表的所有节点值。


6. 链表的优缺点

优点
  1. 动态大小:链表的大小可以动态调整。

  2. 插入/删除高效:在已知节点位置的情况下,插入和删除操作的时间复杂度为 O(1)。

  3. 内存利用率高:节点在内存中非连续存储,适合内存碎片较多的场景。

缺点
  1. 随机访问效率低:访问第 k 个节点需要从头遍历,时间复杂度为 O(n)。

  2. 额外内存开销:每个节点需要存储指针,占用额外内存。


7. 链表的应用场景

  1. 实现栈和队列

    • 栈:使用单链表实现,头插法和头删法。

    • 队列:使用双链表实现,尾插法和头删法。

  2. LRU 缓存

    • 使用双向链表和哈希表实现最近最少使用(LRU)缓存淘汰算法。

  3. 多项式运算

    • 使用链表表示多项式,实现加法、乘法等运算。

  4. 图的邻接表表示

    • 使用链表存储图的邻接表。


8. 常见面试题

  1. 反转链表

    • 将单链表或双链表反转。

    • 示例代码:

      public Node reverseList(Node head) {
          Node prev = null;
          Node current = head;
          while (current != null) {
              Node next = current.next;
              current.next = prev;
              prev = current;
              current = next;
          }
          return prev;
      }

  2. 检测环

    • 判断链表中是否存在环。

    • 示例代码(快慢指针法):

      public boolean hasCycle(Node head) {
          if (head == null) return false;
          Node slow = head;
          Node fast = head;
          while (fast != null && fast.next != null) {
              slow = slow.next;
              fast = fast.next.next;
              if (slow == fast) return true;
          }
          return false;
      }

  3. 合并两个有序链表

    • 将两个有序链表合并为一个有序链表。

    • 示例代码:

      public Node mergeTwoLists(Node l1, Node l2) {
          Node dummy = new Node(0);
          Node current = dummy;
          while (l1 != null && l2 != null) {
              if (l1.data < l2.data) {
                  current.next = l1;
                  l1 = l1.next;
              } else {
                  current.next = l2;
                  l2 = l2.next;
              }
              current = current.next;
          }
          current.next = (l1 != null) ? l1 : l2;
          return dummy.next;
      }


9. 总结

  • 链表是一种灵活的数据结构,适合动态插入和删除操作。

  • Java 提供了 LinkedList 类,可以直接使用。

  • 链表的常见操作包括插入、删除、查找和遍历。

  • 链表在栈、队列、LRU 缓存等场景中有广泛应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值