---
链表
---

#### 目录

1. 链表分类
   - 单链表
   - 循环链表
   - 双向链表
   - 双向循环链表
2. 常见链表算法题
   - 单链表反转
   - 链表中环的检测
   - 两个有序链表的合并
   - 删除链表倒数第 n 个结点
   - 求链表的中间结点
3. 总结

#### 链表分类

##### 单链表

![](https://i.loli.net/2019/02/25/5c73a3d48b8f4.jpg)

##### 循环链表

![](https://i.loli.net/2019/02/25/5c73a4202fc70.jpg)

##### 双向链表

![](https://i.loli.net/2019/02/25/5c73a435a1dd8.jpg)

##### 双向循环链表

![](https://i.loli.net/2019/02/25/5c73a44c30c40.jpg)

#### 常见链表算法题

1. 单链表的反转

   ```java
   public class LinkedListDemo {
   
       private static Node head;
   
       public static void main(String[] args) {
           head = new Node(0);
           head.next = new Node(1);
           head.next.next = new Node(2);
           head.next.next.next = new Node(3);
   
           reverseLinkedList();
   
           while (head != null) {
               System.out.println(head.value);
               head = head.next;
           }
       }
   
       //单链表的反转
       private static void reverseLinkedList() {
           if (head == null || head.next == null) {
               return;
           }
           Node p1 = head;
           Node p2 = head.next;
           Node p3 = null;
           while (p2 != null) {
               p3 = p2.next;
               p2.next = p1;
               p1 = p2;
               p2 = p3;
           }
           head.next = null;
           head = p1;
       }
   
       static class Node {
           int value;
           Node next;
   
           Node(int value) {
               this.value = value;
           }
       }
   }
   ```

   参考：[漫画：如何将一个链表“逆序”？](https://mp.weixin.qq.com/s/MR_qAbonFqGF_ljeWUC26w)

2. 链表中环的检测

   这道题有三种解法：

   ```java
   public class LinkedListDemo {
   
       private static Node head;
   
       public static void main(String[] args) {
           head = new Node(0);
           head.next = new Node(1);
           head.next.next = new Node(2);
           head.next.next.next = new Node(3);
           head.next.next.next.next = head.next;
   
           boolean isHasCycleBySet = hasCycleBySet();
           System.out.println(isHasCycleBySet);
           boolean isHasCycleByRun = hasCycleByRun();
           System.out.println(isHasCycleByRun);
           boolean isHasCycleByMagic = hasCycleByMagic();
           System.out.println(isHasCycleByMagic);
       }
   
       //用 HashSet 判断
       private static boolean hasCycleBySet() {
           if (head == null || head.next == null) {
               return false;
           }
           Set<Node> set = new HashSet<>();
           while (head != null) {
               if (set.contains(head)) {
                   return true;
               } else {
                   set.add(head);
               }
               head = head.next;
           }
           return false;
       }
   
       //快慢指针
       private static boolean hasCycleByRun() {
           if (head == null || head.next == null) {
               return false;
           }
           Node p1 = head;
           Node p2 = head.next;
           while (p1 != p2) {
               if (p2 == null || p2.next == null) {
                   return false;
               }
               p1 = p1.next;
               p2 = p2.next.next;
           }
           return true;
       }
   
       //魔法数解法
       private static boolean hasCycleByMagic() {
           if (head == null || head.next == null) {
               return false;
           }
           while (head != null) {
               if (head.value == 2 << 30) {
                   return true;
               } else {
                   head.value = 2 << 30;
               }
               head = head.next;
           }
           return false;
       }
   
       static class Node {
           int value;
           Node next;
   
           Node(int value) {
               this.value = value;
           }
       }
   }
   ```

3. 两个有序链表的合并

   ```java
   
   ```

4. 删除链表倒数第 n 个结点

   ```java
   
   ```

5. 求链表的中间结点

   ```java
   
   ```

   

#### 总结

面试中手写算法题最多的就是链表相关的，链表算法到底难不难？难？难得话一个题再写十遍还难不难？（气急败坏中...

不过，链表中需要注意几个边界条件：

1. 如果链表为空
2. 如果链表只有一个结点时
3. 如果链表只有两个结点时
4. 代码逻辑在处理头结点和尾结点时