链表专题
链表翻转
//递归链表反转
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);
}