算法系列文章目前是跟着代码随想录 学习
链表基础 单链表、双链表(每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点)、循环链表
链表类型 :单链表、双链表、循环链表的基本构成和特点。操作方式 :直接修改原链表或使用虚拟头结点简化边界情况处理。存储方式 :链表通过非连续存储方式实现灵活的元素插入与删除。 
链表操作与实现 
设计链表  (707.设计链表)
实现一个链表类,包括添加、获取、删除节点的操作。 
代码示例(单链表):1 2 3 4 5 6 7 8 9 10 11 class  MyLinkedList  {    int  size;     ListNode head;      public  MyLinkedList ()  {         size = 0 ;         head = new  ListNode (0 );     }      } 
 
代码示例(双链表):1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  MyLinkedList  {    int  size;     ListNode head, tail;      public  MyLinkedList ()  {         size = 0 ;         head = new  ListNode (0 );         tail = new  ListNode (0 );         head.next = tail;         tail.prev = head;     }      } 
 
 
反转链表  (206.反转链表)
使用迭代和递归方法实现链表反转。 
代码示例(迭代方法):1 2 3 4 5 6 7 8 9 10 public  ListNode reverseList (ListNode head)  {    ListNode  prev  =  null , curr = head;     while  (curr != null ) {         ListNode  nextTemp  =  curr.next;         curr.next = prev;         prev = curr;         curr = nextTemp;     }     return  prev; } 
 
代码示例(递归方法):1 2 3 4 5 6 7 public  ListNode reverseList (ListNode head)  {    if  (head == null  || head.next == null ) return  head;     ListNode  p  =  reverseList(head.next);     head.next.next = head;     head.next = null ;     return  p; } 
 
 
删除链表的倒数第 N 个结点  (19.删除链表的倒数第 N 个结点)
使用快慢指针技巧找到倒数第 N 个节点并删除。 
代码示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  ListNode removeNthFromEnd (ListNode head, int  n)  {    ListNode  dummy  =  new  ListNode (0 );     dummy.next = head;     ListNode  fast  =  dummy, slow = dummy;     for  (int  i  =  0 ; i <= n; i++) {         fast = fast.next;     }     while  (fast != null ) {         slow = slow.next;         fast = fast.next;     }     slow.next = slow.next.next;     return  dummy.next; } 
 
 
链表相交  (面试题 02.07.链表相交)
使用双指针策略检测并返回两个链表的相交点。 
代码示例:1 2 3 4 5 6 7 8 9 public  ListNode getIntersectionNode (ListNode headA, ListNode headB)  {    if  (headA == null  || headB == null ) return  null ;     ListNode  a  =  headA, b = headB;     while  (a != b) {         a = a == null  ? headB : a.next;         b = b == null  ? headA : b.next;     }     return  a; } 
 
 
环形链表 II  (142.环形链表 II)
判断链表是否有环,并找到环的入口。 
代码示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  ListNode detectCycle (ListNode head)  {    ListNode  slow  =  head, fast = head;     while  (fast != null  && fast.next != null ) {         slow = slow.next;         fast = fast.next.next;         if  (slow == fast) {             ListNode  slow2  =  head;             while  (slow2 != slow) {                 slow2 = slow2.next;                 slow = slow.next;             }             return  slow;         }     }     return  null ; } 
 
 
 
优化建议与高级技巧 
虚拟头节点 :使用虚拟头节点简化对链表头部节点的操作,尤其是在插入和删除操作中。双指针技术 :广泛应用于链表中,如在快速找到中点、倒数第 N 个节点、检测环等问题中。 
707.设计链表 题意:
单链表版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 class  ListNode {    int  val;     ListNode next;     ListNode(){}     ListNode(int  val){         this .val = val;     } } class  MyLinkedList {    int  size;     ListNode head;     public  MyLinkedList () {         size = 0 ;         head = new  ListNode (0 );     }     public  int  get (int  index) {         if (index < 0  || index >= size) return  -1 ;         ListNode  cur  =  head;         for (int  i  =  0 ; i <= index; i++){             cur = cur.next;         }         return  cur.val;     }     public  void  addAtHead (int  val) {         ListNode  newNode  =  new  ListNode (val);         newNode.next = head.next;         head.next = newNode;         size++;     }     public  void  addAtTail (int  val) {         ListNode  newNode  =  new  ListNode (val);         ListNode  cur  =  head;         while (cur.next != null ){             cur = cur.next;         }         cur.next = newNode;         size++;     }     public  void  addAtIndex (int  index, int  val) {         if (index > size){             return ;         }         if (index < 0 ){             index = 0 ;         }         size++;         ListNode  pred  =  head;         for (int  i=0 ; i<index;i++){             pred = pred.next;         }         ListNode  toAdd  =  new  ListNode (val);         toAdd.next = pred.next;         pred.next = toAdd;     }     public  void  deleteAtIndex (int  index) {         if (index < 0  || index >= size){             return ;         }         size--;         ListNode pred= head;         for (int  i  =  0 ; i < index;i++){             pred = pred.next;         }         pred.next = pred.next.next;     } } 
双链表版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 class  ListNode {    int  val;     ListNode next,prev;     ListNode(){}     ListNode(int  val){         this .val = val;     } } class  MyLinkedList {    int  size;     ListNode head,tail;     public  MyLinkedList () {         size = 0 ;         head = new  ListNode (0 );         tail = new  ListNode (0 );         head.next = tail;         tail.prev = head;     }     public  int  get (int  index) {         if (index < 0  || index >= size) return  -1 ;         ListNode  cur  =  head;                  if (index >= size /2  ){             cur = tail;             for (int  i  =  0 ; i < size - index; i++){                 cur = cur.prev;             }         }else {             for (int  i  =  0 ; i <= index; i++){                 cur = cur.next;             }         }         return  cur.val;     }     public  void  addAtHead (int  val) {         addAtIndex(0 , val);     }     public  void  addAtTail (int  val) {        addAtIndex(size, val);     }     public  void  addAtIndex (int  index, int  val) {         if (index > size){             return ;         }         if (index < 0 ){             index = 0 ;         }         size++;         ListNode  pred  =  head;         for (int  i=0 ; i<index;i++){             pred = pred.next;         }         ListNode  toAdd  =  new  ListNode (val);         toAdd.next = pred.next;         pred.next.prev = toAdd;         toAdd.prev = pred;         pred.next = toAdd;     }     public  void  deleteAtIndex (int  index) {         if (index < 0  || index >= size){             return ;         }         size--;         ListNode pred= head;         for (int  i  =  0 ; i < index;i++){             pred = pred.next;         }         pred.next.next.prev = pred;         pred.next = pred.next.next;     } } 
206 反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {    public  ListNode reverseList (ListNode head)  {         if  (head == null )  return  null ;         ListNode  prev  =  null ;         ListNode  cur  =  head;         ListNode  tmp  =  null ;         while (cur != null ){             tmp = cur.next;             cur.next = prev;             prev = cur;             cur = tmp;         }         return  prev;     } } 
// 递归
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {    public  ListNode reverseList (ListNode head)  {         return  reverse(null , head);     }     public  ListNode reverse (ListNode prev, ListNode cur) {         if (cur == null ) return  prev;         ListNode  temp  =  null ;         temp = cur.next;         cur.next = prev;         return  reverse(cur, temp);     } } 
// 从后向前递归
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {    public  ListNode reverseList (ListNode head)  {                  if (head == null ) return  null ;         if (head.next == null ) return  head;                  ListNode  last  =  reverseList(head.next);                  head.next.next = head;                  head.next = null ;         return  last;     } } 
// 使用虚节点的头插法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {    public  ListNode reverseList (ListNode head)  {         ListNode  dummyHead  =  new  ListNode (-1 );         dummyHead.next = null ;         ListNode  cur  =  head;         while (cur != null ){             ListNode  temp  =  cur.next;                          cur.next = dummyHead.next;             dummyHead.next = cur;             cur = temp;         }         return  dummyHead.next;     } } 
// 使用栈解决反转链表问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class  Solution  {    public  ListNode reverseList (ListNode head)  {         if (head==null ) return  null ;         if (head.next == null ) return  null ;         Stack<ListNode> stack = new  Stack <>();         ListNode  cur  =  head;         while (cur != null ){             stack.push(cur);             cur = cur.next;         }         ListNode  pHead  =  new  ListNode (0 );         cur = pHead;         while (!stack.isEmpty()){             ListNode  node  =  stack.pop();             cur.next = node;             cur = cur.next;         }         cur.next = null ;         return  pHead.next;     } } 
这道题一开始没有让prev指向null出错了
24. 两两交换链表中的节点 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  Solution  {    public  ListNode swapPairs (ListNode head)  {         ListNode  dumyhead  =  new  ListNode (-1 );         dumyhead.next = head;         ListNode  cur  =  dumyhead;         ListNode temp;         ListNode firstnode;         ListNode secondnode;         while (cur.next != null  && cur.next.next != null ){             temp = cur.next.next.next;             firstnode = cur.next;             secondnode = cur.next.next;             cur.next = secondnode;             secondnode.next = firstnode;             firstnode.next = temp;             cur = firstnode;         }         return  dumyhead.next;     } } 
// 递归解决
1 2 3 4 5 6 7 8 9 10 class  Solution  {    public  ListNode swapPairs (ListNode head)  {         if (head == null  || head.next == null ) return  head;         ListNode  next  =  head.next;         ListNode  newNode  =  swapPairs(next.next);         next.next = head;         head.next = newNode;         return  next;     } } 
19. 删除链表的倒数第 N 个结点 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class  Solution  {    public  ListNode removeNthFromEnd (ListNode head, int  n)  {         ListNode  dummyhead  =  new  ListNode (0 );         dummyhead.next = head;         ListNode  fast  =  dummyhead;         ListNode  slow  =  dummyhead;         for (int  i=0 ;i<n;i++){             fast = fast.next;         }         while (fast.next != null ){             slow = slow.next;             fast = fast.next;         }         if (slow.next == fast){             fast = null ;         }else {             slow.next = fast;            }         return  dummyhead.next;     } } 
我上面的代码对于边界条件处理不好,下面是答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class  Solution  {    public  ListNode removeNthFromEnd (ListNode head, int  n)  {         ListNode  dummyhead  =  new  ListNode (0 );         dummyhead.next = head;         ListNode  fast  =  dummyhead;         ListNode  slow  =  dummyhead;         for (int  i=0 ;i<=n;i++){             fast = fast.next;         }         while (fast != null ){             slow = slow.next;             fast = fast.next;         }                                    if (slow.next != null ){             slow.next = slow.next.next;         }         return  dummyhead.next;     } } 
我又自己写了个,按照我原来的思路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  Solution  {    public  ListNode removeNthFromEnd (ListNode head, int  n)  {         ListNode  dummyhead  =  new  ListNode (0 );         dummyhead.next = head;         ListNode  fast  =  dummyhead;         ListNode  slow  =  dummyhead;         for (int  i=0 ;i<n;i++){             fast = fast.next;         }         while (fast.next != null ){             slow = slow.next;             fast = fast.next;         }                                    slow.next = slow.next.next;         return  dummyhead.next;     } } 
面试题 02.07. 链表相交 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public  class  Solution  {    public  ListNode getIntersectionNode (ListNode headA, ListNode headB)  {         ListNode  curA  =  headA;         ListNode  curB  =  headB;         int  lenA  =  0 , lenB = 0 ;         while (curA != null ){             lenA++;             curA = curA.next;         }         while (curB != null ){             lenB++;             curB = curB.next;         }         curA = headA;         curB = headB;         if (lenB > lenA){             int  tmpLen  =  lenA;             lenA = lenB;             lenB = tmpLen;             ListNode  tmpNode  =  curA;             curA = curB;             curB = tmpNode;         }                  int  gap  =  lenA - lenB;                  while (gap-- > 0 ){             curA = curA.next;         }         while (curA != null ){             if (curA == curB){                 return  curA;             }             curA = curA.next;             curB = curB.next;         }         return  null ;     } } 
下面的版本是同步走的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16   public  class  Solution  {    public  ListNode getIntersectionNode (ListNode headA, ListNode headB)  {                ListNode  p1  =  headA, p2 = headB;        while (p1 != p2){                  if (p1 == null ) p1 = headB;         else  p1 = p1.next;                  if (p2 == null ) p2 = headA;         else  p2 = p2.next;        }        return  p1;     } } 
142.环形链表II 判断链表是否有环
如果有环,如何找到这个环的入口
那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。
在推理过程中,大家可能有一个疑问就是:为什么第一次在环中相遇,slow的 步数 是 x+y 而不是 x + 若干环的长度 + y 呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public  class  Solution  {    public  ListNode detectCycle (ListNode head)  {         ListNode  slow  =  head;         ListNode  fast  =  head;         while (fast != null  && fast.next != null ){             slow = slow.next;             fast = fast.next.next;             if (slow == fast){                 ListNode  index1  =  fast;                 ListNode  index2  =  head;                 while (index1 != index2){                     index1 = index1.next;                     index2 = index2.next;                 }                 return  index1;             }         }         return  null ;     } } public  class  Solution  {    public  ListNode detectCycle (ListNode head)  {         ListNode  pos  =  head;         Set<ListNode> visited = new  HashSet <ListNode>();         while (pos != null ){             if (visited.contains(pos)){                 return  pos;             }else {                 visited.add(pos);             }             pos = pos.next;         }         return  null ;     } }