链表专题

链表专题

链表翻转

//递归链表反转
public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode last = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return last;
    }
    
//迭代链表反转
public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode next = null;
        while(head != null){
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

从尾到头打印链表

//输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
//递归
    ArrayList result = new ArrayList();
    public ArrayList printListFromTailToHead(ListNode listNode) {
        if(listNode == null) return result;
        printListFromTailToHead(listNode.next);
        result.add(listNode.val);
        return result;
    }

递归反转链表前N个节点

ListNode next = null;

 public ListNode reverse(ListNode head, int n) {
  if (n == 1) {
   next = head.next;
   return head;
  }
  ListNode last = reverse(head.next, n - 1);
  head.next.next = head;
  head.next = next;
  return last;
 }

递归反转链表区间

public ListNode reverseBetween(ListNode head, int m, int n) {
  ListNode result = new ListNode(0);
  result.next = head;
  
  ListNode pre = result;
  ListNode cur = result.next;

  int step = 0;
  while (step < m - 1) {
   pre = pre.next;
   cur = cur.next;
   step++;
  }

  for (int i = 0; i < n - m; i++) {
   ListNode temp = cur.next;
   cur.next = cur.next.next;
   
   temp.next = pre.next;
   pre.next = temp;
  }
  return result.next;
 }

链表翻转两两翻转

//递归
public ListNode swapPairs(ListNode head) {
  if ((head == null) || (head.next == null)) {
    return head;
  }

  ListNode firstNode = head;
  ListNode secondNode = head.next;

  firstNode.next  = swapPairs(secondNode.next);
  secondNode.next = firstNode;

  return secondNode;
}

//迭代
public ListNode swapPairs(ListNode head) {
  ListNode dummy = new ListNode(-1);
  dummy.next = head;
  ListNode prevNode = dummy;

  while ((head != null) && (head.next != null)) {
    ListNode firstNode = head;
    ListNode secondNode = head.next;

    prevNode.next = secondNode;
    firstNode.next = secondNode.next;
    secondNode.next = firstNode;

    prevNode = firstNode;
    head = firstNode.next; 
  }
  return dummy.next;
}

链表翻转k个一组

public ListNode reverseKGroup(ListNode head, int k) {
   ListNode dummy = new ListNode(-1);
   dummy.next = head;
   ListNode start = dummy;
   ListNode end = dummy;

   while (head != null) {
      for (int i = 0; i < k && end != null; i++) end = end.next;
      if (end == null) break;
      ListNode pre = start.next;
      ListNode last = end.next;
      end.next = null;
      start.next = reverseNode(pre);
      pre.next = last;
      start = pre;
      end = pre;
   }
   return dummy.next;
}

public ListNode reverseNode(ListNode head) {
   if (head == null) return null;
   ListNode pre = null;
   ListNode cur = head;
   while (cur != null) {
      ListNode temp = cur.next;
      cur.next = pre;
      pre = cur;
      cur = temp;
   }
   return pre;
}

删除重复节点

 public ListNode deleteDuplication(ListNode pHead) {
         ListNode result;
         ListNode temp = pHead;
         ListNode index = new ListNode(1);
         index.next = pHead;
         result = index;
         while (temp != null) {
            if (temp.next != null && temp.next.val == temp.val) {
               while (temp.next != null && temp.next.val == temp.val) {
                  temp = temp.next;
               }
               temp = temp.next;
               index.next = temp;
            } else {
               index = index.next;
               temp = temp.next;
            }
         }
         return result.next;
      }

删除重复保留一个

public ListNode deleteDuplicates(ListNode head) {
   ListNode result = head;
   if(head == null) return result;
   while (head.next != null) {
      if (head.val == head.next.val) {
         head.next = head.next.next;
      } else {
         head = head.next;
      }
   }
   return result;
}

复制复杂链表

//HashMap
      public RandomListNode Clone(RandomListNode pHead) {
         if (pHead == null) return null;
         HashMap result = new HashMap();
         RandomListNode head = pHead;
         while (head != null) {
            result.put(head, new RandomListNode(head.label));
            head = head.next;
         }
         head = pHead;
         while (head != null) {
            result.get(head).next = result.get(head.next);
            result.get(head).random = result.get(head.random);
            head = head.next;
         }
         return result.get(pHead);
      }
      
//链表后拆开
   public RandomListNode Clone(RandomListNode pHead) {
         if (pHead == null) {
            return null;
         }
         RandomListNode currentNode = pHead;
         RandomListNode temp;
         //1.复制每个节点,并将复制的节点插在原节点之后。
         while (currentNode != null) {
            temp = new RandomListNode(currentNode.label);
            temp.next = currentNode.next;
            currentNode.next = temp;
            currentNode = currentNode.next.next;
         }
         currentNode = pHead;
         //2.根据旧节点的random属性给新节点的random属性赋值(新节点的random属性是旧节点的下一个,此处注意判断是否为空)
         while (currentNode != null) {
            if (currentNode.random == null) {
               currentNode.next.random = null;
            } else {
               currentNode.next.random = currentNode.random.next;
            }
            currentNode = currentNode.next.next;
         }
         currentNode = pHead;
         RandomListNode root = pHead.next;
         //拆分新旧链表(奇数为旧链表,偶数为新链表)
         while (currentNode != null) {
            temp = currentNode.next;
            currentNode.next = currentNode.next.next;
            if (temp.next == null) {
               temp.next = null;
            } else {
               temp.next = temp.next.next; 
            }
            currentNode = currentNode.next;
         }
         return root;
      }

查找倒数第k个节点

      public ListNode FindKthToTail(ListNode head, int k) {
         if (head == null || k < 0) return null;
         ListNode first = head;
         ListNode second = head;
         for (int i = 0; i < k; i++) {
            if (first != null) {
               first = first.next;
            } else {
               return null;
            }
         }
         while (first != null) {
            first = first.next;
            second = second.next;
         }
         return second;
      }

查找链表的第一个公共节点

//先走距离差
      public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         if (pHead1 == null || pHead2 == null) {
            return null;
         }
         int count1 = 0;
         ListNode p1 = pHead1;
         while (p1 != null) {
            p1 = p1.next;
            count1++;
         }
         int count2 = 0;
         ListNode p2 = pHead2;
         while (p2 != null) {
            p2 = p2.next;
            count2++;
         }
         int flag = count1 - count2;
         if (flag > 0) {
            while (flag > 0) {
               pHead1 = pHead1.next;
               flag--;
            }
            while (pHead1 != pHead2) {
               pHead1 = pHead1.next;
               pHead2 = pHead2.next;
            }
            return pHead1;
         }
         if (flag <= 0) {
            while (flag < 0) {
               pHead2 = pHead2.next;
               flag++;
            }
            while (pHead1 != pHead2) {
               pHead2 = pHead2.next;
               pHead1 = pHead1.next;
            }
            return pHead1;
         }
         return null;
      }
      
//栈
      public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         Stack stack1 = new Stack();
         Stack stack2 = new Stack();
         ListNode p1 = null, p2 = null, result = null;
         while (pHead1 != null) {
            stack1.push(pHead1);
            pHead1 = pHead1.next;
         }
         while (pHead2 != null) {
            stack2.push(pHead2);
            pHead2 = pHead2.next;
         }
         while (!stack1.empty() && !stack2.empty()) {
            p1 = stack1.pop();
            p2 = stack2.pop();
            if (p1 == p2) {
               result = p1;
            } else {
               break;
            }
         }
         return result;
      }

合并两个排序链表

//递归合并排序链表
      public ListNode Merge(ListNode list1, ListNode list2) {
         if (list1 == null) return list2;
         if (list2 == null) return list1;
         if (list1.val < list2.val) {
            list1.next = Merge(list1.next, list2);
            return list1;
         } else {
            list2.next = Merge(list1, list2.next);
            return list2;
         }
      }
      
//迭代合并排序链表
      public ListNode Merge(ListNode list1, ListNode list2) {
         ListNode head = new ListNode(-1);
         head.next = null;
         ListNode root = head;
         //注意这个条件:如果两个链表等长可以使其中一个扫描完
         while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
               head.next = list1;
               head = list1;
               list1 = list1.next;
            } else {
               head.next = list2;
               head = list2;
               list2 = list2.next;
            }
         }
         if (list1 != null) {
            head.next = list1;
         }
         if (list2 != null) {
            head.next = list2;
         }
         return root.next;
      }

合并k个排序链表

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

//使用小根堆对 1 进行优化,每次 O(logK)O(logK) 比较 K个指针求 min, 时间复杂度:O(NlogK)O(NlogK)
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        Queue pq = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);
        for (ListNode node: lists) {
            if (node != null) {
                pq.offer(node);
            }
        }

        ListNode dummyHead = new ListNode(0);
        ListNode tail = dummyHead;
        while (!pq.isEmpty()) {
            ListNode minNode = pq.poll();
            tail.next = minNode;
            tail = minNode;
            if (minNode.next != null) {
                pq.offer(minNode.next);
            }
        }

        return dummyHead.next;
    }
}

//两两合并 - 迭代
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        }
        int k = lists.length;
        while (k > 1) {
            int idx = 0;
            for (int i = 0; i < k; i += 2) {
                if (i == k - 1) {
                    lists[idx++] = lists[i];
                } else {
                    lists[idx++] = merge2Lists(lists[i], lists[i + 1]);
                }
            }
            k = idx;
        }
        return lists[0];
    }
}
//两两合并 - 递归
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        }
        return merge(lists, 0, lists.length - 1);
    }

    private ListNode merge(ListNode[] lists, int lo, int hi) {
        if (lo == hi) {
            return lists[lo];
        }
        int mid = lo + (hi - lo) / 2;
        ListNode l1 = merge(lists, lo, mid);
        ListNode l2 = merge(lists, mid + 1, hi);
        return merge2Lists(l1, l2);
    }
}

作者:sweetiee
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/4-chong-fang-fa-xiang-jie-bi-xu-miao-dong-by-sweet/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

判断是否回文链表

public boolean isPalindrome(ListNode head) {
   // 要实现 O(n) 的时间复杂度和 O(1) 的空间复杂度,需要翻转后半部分
   if (head == null || head.next == null) {
      return true;
   }
   ListNode fast = head;
   ListNode slow = head;
   // 根据快慢指针,找到链表的中点
   while (fast.next != null && fast.next.next != null) {
      fast = fast.next.next;
      slow = slow.next;
   }
   slow = reverse(slow.next);
   while (slow != null) {
      if (head.val != slow.val) {
         return false;
      }
      head = head.next;
      slow = slow.next;
   }
   return true;
}

private ListNode reverse(ListNode head) {
   // 递归到最后一个节点,返回新的新的头结点
   if (head.next == null) {
      return head;
   }
   ListNode newHead = reverse(head.next);
   head.next.next = head;
   head.next = null;
   return newHead;
}

判断是否有环

//哈希法
public boolean hasCycle(ListNode head) {
  Set nodesSeen = new HashSet<>();
  while (head != null) {
    if (nodesSeen.contains(head)) {
      return true;
    } else {
      nodesSeen.add(head);
    }
    head = head.next;
  }
  return false;
}

//双指针
public boolean hasCycle(ListNode head) {
  if (head == null || head.next == null) {
    return false;
  }
  ListNode slow = head;
  ListNode fast = head.next;
  while (slow != fast) {
    if (fast == null || fast.next == null) {
      return false;
    }
    slow = slow.next;
    fast = fast.next.next;
  }
  return true;
}

有环找入口节点

//哈希法
public ListNode detectCycle(ListNode head) {
  Set visited = new HashSet();

  ListNode node = head;
  while (node != null) {
    if (visited.contains(node)) {
      return node;
    }
    visited.add(node);
    node = node.next;
  }
  return null;
}

//双指针
public ListNode detectCycle(ListNode head) {
  if (head == null || head.next == null) return null;
  ListNode fast = head.next;
  ListNode slow = head;
  while (fast != slow) {
    if (fast == null || fast.next == null) return null;
    fast = fast.next.next;
    slow = slow.next;
  }
  fast = head;
  slow = slow.next;
  while (fast != slow) {
    fast = fast.next;
    slow = slow.next;
  }
  return fast;
}

//链表中环的路口节点
//给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
--------------------------------------------------------------------------------
   public class ListNode {
      int val;
      ListNode next = null;

      ListNode(int val) {
         this.val = val;
      }
   }
--------------------------------------------------------------------------------
      public ListNode EntryNodeOfLoop(ListNode pHead) {
         Set set = new HashSet();
         while (pHead.next != null) {
            if (!set.contains(pHead)) {
               set.add(pHead);
               pHead = pHead.next;
            } else {
               return pHead;
            }
         }
         return null;
      }

约瑟夫环

//每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。
// 其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。
// 每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,
// 继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
//机灵1
      public int LastRemaining_Solution(int n, int m) {
         if (n == 0 || m == 0) return -1;
         int s = 0;
         for (int i = 2; i <= n; i++) {
            s = (s + m) % i;
         }
         return s;
      }
//机灵2
      public int LastRemaining_Solution(int n, int m) {
         if (n <= 0 || m <= 0) {
            return -1;
         }
         LinkedList list = new LinkedList();
         for (int i = 0; i < n; i++) {
            list.add(i);
         }
         int index = 0;
         for (int i = 0; i < n - 1; i++) {
            //可以直接这样是因为移除了一个元素后size减1,所以当index不变时,相当于自动向后移动了一位
            //另外注意下标不能越界所以要对size取模
            index = (m - 1 + index) % list.size();
            list.remove(index);
         }
         return list.get(0);
      }

//孩子们的游戏(约瑟夫环)
//每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。
// 其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。
// 每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,
// 继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

//【约瑟夫环】
// 每次会有一个小朋友死掉,n个小朋友最后只有一个存活,那么只需要死掉n-1个。
// 注意:死亡位置是动态改变的,所以要注意用取模的方法处理。
--------------------------------------------------------------------------------
      public int LastRemaining_Solution(int n, int m) {
         if (n == 0 || m == 0) return -1;
         int s = 0;
         for (int i = 2; i <= n; i++) {
            s = (s + m) % i;
         }
         return s;
      }
--------------------------------------------------------------------------------
      public int LastRemaining_Solution(int n, int m) {
         if (n <= 0 || m <= 0) {
            return -1;
         }
         LinkedList list = new LinkedList();
         for (int i = 0; i < n; i++) {
            list.add(i);
         }
         int index = 0;
         for (int i = 0; i < n - 1; i++) {
            //可以直接这样是因为移除了一个元素后size减1,所以当index不变时,相当于自动向后移动了一位
            //另外注意下标不能越界所以要对size取模
            index = (m - 1 + index) % list.size();
            list.remove(index);
         }
         return list.get(0);
      }

你可能感兴趣的:(算法,ACM)