链表高频面试算法题
剑指offer52
两个链表第一个公共子节点
输入两个链表,找出它们的一个公共节点。两个链表的头节点是已知的,相交之后成为一个单链表,但是相交的位置未知,并且相交之前的节点数也是未知的,请设计算法找到两个链表的合并点。
思路:
- 对于单链表而言,每一个节点都会存放下一个节点的地址,重点在于只存放一个节点。**所以说明每一个节点只会有一个后继,但每一个节点可能有多个前驱。**可以得出结论,该表的结构应该是类似图1而非图2。
- 当知道链表的头节点时候,就可以遍历所有节点,这道题目两个链表的头节点已知,那么我们可以遍历两个链表,取出两个链表所有的值和地址,通过比较判定则可得出合并点。
解题:
- 哈希表和集合:先后遍历两个链表,然后将两个链表的值存到set中。在遍历第二个同时,检测第一个链表是否存在当前的节点,如果有交点即可检测出来。
package lukasy.chapter1_linklist.level2.topic1第一个公共节点;
import lukasy.chapter1_linklist.level1.ListNode;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author LukAsy_
* @date 2023/11/28
*/
public class FindFirstCommonNode {
/**
* method1:通过Hash辅助查找
*
* @param pHead1
* @param pHead2
* @return
*/
public static ListNode findFirstCommonNodeByMap(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
ListNode cur1 = pHead1;
ListNode cur2 = pHead2;
HashMap<ListNode, Integer> hashMap = new HashMap<ListNode, Integer>();
while (cur1 != null) {
hashMap.put(cur1, null);
cur1 = cur1.next;
}
while (cur2 != null) {
if (hashMap.containsKey(cur2))
return cur2;
cur2 = cur2.next;
}
return null;
}
/**
* 方法2:通过集合来辅助查找
*
* @param headA
* @param headB
* @return
*/
public static ListNode findFirstCommonNodeBySet(ListNode headA, ListNode headB) {
Set<ListNode> set = new HashSet<>();
while (headA != null) {
set.add(headA);
headA = headA.next;
}
while (headB != null) {
if (set.contains(headB))
return headB;
headB = headB.next;
}
return null;
}
}
LeetCode234
判断链表是否为回文序列题
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false
思路:
- 首先明确一个概念,怎么样的数据才是回文序列。回文序列指的是正读和反读都相同的序列。换句话说,如果一个序列从左到右读和从右到左读是一样的,那么它就是回文的。
- 我们要要判断一个链表是否是回文序列,其实就是比较前半段和后半段的内容是否一样,那使用栈的先进后出的特性可以得到反向排序的链表,将其与正向排序的链表做比较是否每个值都一样就可以判断出来了。
- 综上思路就是,先遍历一遍链表,然后将链表压入栈中,再出栈,出栈同时再遍历链表并比较出栈和遍历的元素是否相同。
public class IsPalindromic {
public static boolean isPalindromeByAllStack(ListNode head) {
ListNode cur = head;
Stack<Integer> stack = new Stack<>();
while (cur != null) {
stack.push(cur.val);
cur = cur.next;
}
while (head != null) {
if (head.val != stack.pop()) {
return false;
}
head = head.next;
}
return true;
}
}
LeetCode21
合并两个有序链表
将两个链表按顺序合并。
思路:
- 新建一个链表,将两个链表分别遍历,对比两个链表每个值,较小的则连入新链表,重复这个过程知道两个链表都遍历完为止。
package lukasy.chapter1_linklist.level2.topic3合并有序链表;
import lukasy.chapter1_linklist.level1.ListNode;
/**
* @author LukAsy_
* @date 2023/12/2
*/
public class MergeList {
/**
* @param l1
* @param l2
* @return
*/
public static ListNode mergeTwoListsMoreSimple(ListNode l1, ListNode l2) {
ListNode prehead = new ListNode(-1);
ListNode cur = prehead;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
cur.next = l1;
l1 = l1.next;
} else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1 == null ? l2 : l1;
return cur.next;
}
}
拓展合并链表
合并K个链表
思路: 有个合并两个链表的思路,那合并多个链表无非就是先将前两个链表进行对比合并,然后再将后面的链表和合并后的链表对比合并。
/**
* 合并K个链表
*
* @param lists
* @return
*/
public static ListNode mergeKLists(ListNode[] lists) {
ListNode cur = null;
for (ListNode list : lists) {
cur = mergeTwoListsMoreSimple(cur, list);
}
return cur;
}
LeetCode1669
给你两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。
请你将 list1 中下标从 a 到 b 的全部节点都删除,并将list2 接在被删除节点的位置。
思路:
- 找出list1中需要删除部分的头节点的前一个节点和尾节点a-1、b。
- 找出删除部分头节点的前一个节点(a-1)是因为我们需要将该节点和list2的头节点相连接。
- 将list2的尾节点和list1后面的部分相连接。
/**
* @param list1
* @param list2
* @param a 待删除链表位置的头
* @param b 待删除链表位置的尾
* @return
*/
public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
ListNode pre1 = list1, post1 = list1, post2 = list2;
int i = 0, j = 0;
//遍历l1并找出需要删除部分的头尾
while (pre1 != null && post1 != null && j < b) {
if (i != a - 1) {
pre1 = pre1.next;
i++;
}
if (j != b) {
post1 = post1.next;
j++;
}
}
//遍历l2
while (list2.next != null) {
post2 = post2.next;
}
//将l2的尾部接到l1需要删除部分头节点的前一个节点,l2头接l1后半部分的头
pre1.next = list2;
post2.next = post1.next;
return list1;
}