相交链表
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal
- 相交的起始节点的值。如果不存在相交节点,这一值为0
listA
- 第一个链表listB
- 第二个链表skipA
- 在listA
中(从头节点开始)跳到交叉节点的节点数skipB
- 在listB
中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA
和 headB
传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。
示例 2:
输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:No intersection
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。
提示:
listA
中节点数目为m
listB
中节点数目为n
1 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果
listA
和listB
没有交点,intersectVal
为0
如果
listA
和listB
有交点,intersectVal == listA[skipA] == listB[skipB]
解题思路
这段代码用于查找两个单链表的交点(intersection node)。其核心逻辑是使用两个指针 a
和 b
分别遍历链表 headA
和 headB
,通过让指针在到达链表末尾时切换到另一个链表的头部,从而消除两个链表的长度差。如果两个链表相交,指针最终会相遇;否则,指针会同时变为 null
,返回 null
。
代码逻辑步骤
初始检查:如果
headA
或headB
为null
,直接返回null
(因为无交点)。初始化指针:
a
指向headA
,b
指向headB
。循环遍历:
循环条件:
a != null || b != null
(只要其中一个指针不为null
,就继续循环)。在循环内:
首先检查
a
和b
是否指向同一个节点(即a == b
)。如果是,返回该节点(交点)。然后移动指针
a
:如果a
不为null
,则a
指向a.next
;否则,a
指向headB
(切换到链表B的头部)。类似地移动指针
b
:如果b
不为null
,则b
指向b.next
;否则,b
指向headA
(切换到链表A的头部)。
循环结束:如果循环退出(即两个指针都变为
null
),说明没有交点,返回null
。
为什么这样工作?
如果两个链表相交,指针
a
和b
在遍历过程中会同时到达交点(因为切换链表后,路径长度相同)。如果不想交,指针最终会同时变为
null
(即都遍历了两个链表的全部节点),循环退出。
示例模拟
假设链表A: 1->2->3->null
,链表B: 4->5->null
,无交点。
指针
a
和b
的遍历路径:a
: 1 → 2 → 3 → null → 切换为 headB (4) → 5 → nullb
: 4 → 5 → null → 切换为 headA (1) → 2 → 3 → null
当
a
和b
都变为null
时,循环退出,返回null
。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null){
return null;
}
ListNode a = headA;
ListNode b = headB;
while(a != null || b != null){
if(a==b){
return a;
}
if(a!=null){
a = a.next;
}else{
a = headB;
}
if(b!=null){
b = b.next;
}else{
b = headA;
}
}
return null;
}
}
无重复字符的最长子串
给定一个字符串 s
,请你找出其中不含有重复字符的 最长 子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成
解题思路(滑动窗口)
代码实现了使用滑动窗口(双指针)和哈希集合来寻找最长无重复字符子串的长度。
让我们逐步梳理逻辑:
初始化:使用
set
来记录当前窗口中的字符,left
和right
指针表示窗口的左右边界,maxSize
记录最大长度。扩张窗口(右指针移动):如果当前字符不在
set
中,将其加入set
,更新maxSize
,然后right++
。收缩窗口(左指针移动):如果当前字符在
set
中(重复),则不断移动left
指针,并移除left
对应的字符,直到重复字符被移除(即窗口不再包含当前right
字符)。
class Solution {
public int lengthOfLongestSubstring(String s) {
HashSet<Character> set = new HashSet();
int maxSize = 0;
int left = 0;
int right = 0;
char[] ss = s.toCharArray();
while(right < ss.length){
if(!set.contains(ss[right])){
set.add(ss[right]);
maxSize = Math.max(maxSize,right - left +1);
right++;
}else{
while(set.contains(ss[right])){
set.remove(ss[left]);
left++;
}
}
}
return maxSize;
}
}