1面试题 02.07. 链表相交
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Intersected at '8' 解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。 在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Intersected at '2' 解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [0,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 输出:null 解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。 由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。 这两个链表不相交,因此返回 null 。
提示:
listA
中节点数目为m
listB
中节点数目为n
0 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
- 如果
listA
和listB
没有交点,intersectVal
为0
- 如果
listA
和listB
有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]
思路:
本题就是求两个链表交点节点的指针
首先遍历两个链表,分别计算它们的长度。然后将指针重新指向链表头部,并计算长度差异。接下来,将较长链表的指针向前移动,使两个链表剩余的长度相等。最后,同时遍历两个链表,直到找到相交的节点或者到达链表末尾。
代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
// 初始化两个指针,用于遍历链表
ListNode* curA = headA;
ListNode* curB = headB;
// 用于记录链表 A 和 B 的长度
int lenA = 0, lenB = 0;
// 计算链表 A 的长度
while(curA != NULL){
lenA++;
curA = curA->next;
}
// 将 curA 指针重新指向链表头部
curA = headA;
// 计算链表 B 的长度
while(curB != NULL){
lenB++;
curB = curB->next;
}
// 将 curB 指针重新指向链表头部
curB = headB;
// 计算链表长度差异
int gap = abs(lenA - lenB);
// 移动较长的链表指针,使两个链表的长度相同
if(lenA > lenB){
while(gap--)
curA = curA->next;
} else {
while(gap--)
curB = curB->next;
}
// 同时遍历两个链表,寻找交点
while(curA != NULL && curB != NULL){
if(curA == curB)
return curA;
curA = curA->next;
curB = curB->next;
}
// 若不存在交点,则返回 NULL
return NULL;
}
};
2同构字符串
给定两个字符串 s
和 t
,判断它们是否是同构的。
如果 s
中的字符可以按某种映射关系替换得到 t
,那么这两个字符串是同构的。
每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。
示例 1:
输入:s ="egg"
, t ="add"
输出:true
示例 2:
输入:s ="foo"
, t ="bar"
输出:false
示例 3:
输入:s ="paper"
, t ="title"
输出:true
提示:
1 <= s.length <= 5 * 104
t.length == s.length
s
和t
由任意有效的 ASCII 字符组成
思路:
通过创建两个哈希表 map1 和 map2,分别用来保存字符串 s 到 t 和 t 到 s 的字符映射关系。然后遍历字符串 s 和 t,逐个检查字符的映射关系是否匹配。如果存在不匹配的映射关系,则返回 false;否则,最终返回 true。
代码:
class Solution {
public:
bool isIsomorphic(string s, string t) {
// 使用两个哈希表分别记录 s 到 t 和 t 到 s 的字符映射关系
unordered_map<char, char> map1; // map1 保存 s[i] 到 t[j] 的映射
unordered_map<char, char> map2; // map2 保存 t[j] 到 s[i] 的映射
// 遍历字符串 s 和 t
for (int i = 0, j = 0; i < s.size(); i++, j++) {
// 如果当前字符 s[i] 在 map1 中不存在映射,则将其映射到 t[j]
if (map1.find(s[i]) == map1.end()) {
map1[s[i]] = t[j];
}
// 如果当前字符 t[j] 在 map2 中不存在映射,则将其映射到 s[i]
if (map2.find(t[j]) == map2.end()) {
map2[t[j]] = s[i];
}
// 如果发现映射关系不匹配,则立即返回 false
if (map1[s[i]] != t[j] || map2[t[j]] != s[i]) {
return false;
}
}
// 如果能够遍历完整个字符串 s 和 t,并且映射关系匹配,则返回 true
return true;
}
};
3查找共用字符
给你一个字符串数组 words
,请你找出所有在 words
的每个字符串中都出现的共用字符( 包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。
示例 1:
输入:words = ["bella","label","roller"] 输出:["e","l","l"]
示例 2:
输入:words = ["cool","lock","cook"] 输出:["c","o"]
提示:
1 <= words.length <= 100
1 <= words[i].length <= 100
words[i]
由小写英文字母组成
思路:
通过遍历所有单词,统计每个单词中每个字符出现的次数,并将这些次数记录在 count
数组中。然后,遍历 count
数组,更新 result
数组,使其保存所有单词中每个字符出现的最小频率。最后,根据 result
数组中每个字符出现的最小频率,构造结果数组 ret
,将字符转为字符串存入 ret
中,以表示这些字符在所有单词中出现的公共部分。
代码:
class Solution {
public:
vector<string> commonChars(vector<string>& words) {
vector<int> result(26,101); // 统计所有单词中字符出现的最小频率,初始值设为一个较大的数,101表示不可能出现的次数
for(auto& word: words){
vector<int> count(26); // 统计当前单词中字符的出现次数
for(auto& c:word){
count[c-'a']++; // 统计当前字符出现次数
}
for(int i=0;i<26;i++){
result[i]=min(result[i],count[i]); // 更新所有字符的最小出现次数
}
}
vector<string> ret; // 存放最终结果
for(int i=0;i<26;i++){
while(result[i]!=0){
ret.push_back(string(1,i+'a')); // 将字符转为字符串存入结果中
result[i]--;
}
}
return ret; // 返回结果
}
};
4长按键入
你的朋友正在使用键盘输入他的名字 name
。偶尔,在键入字符 c
时,按键可能会被长按,而字符可能被输入 1 次或多次。
你将会检查键盘输入的字符 typed
。如果它对应的可能是你的朋友的名字(其中一些字符可能被长按),那么就返回 True
。
示例 1:
输入:name = "alex", typed = "aaleex" 输出:true 解释:'alex' 中的 'a' 和 'e' 被长按。
示例 2:
输入:name = "saeed", typed = "ssaaedd" 输出:false 解释:'e' 一定需要被键入两次,但在 typed 的输出中不是这样。
提示:
1 <= name.length, typed.length <= 1000
name
和typed
的字符都是小写字母
思路:
同时遍历name和typed字符串,保持两个指针i、j来记录当前比较的位置。在每轮循环中,会按照以下三种情况进行处理:
- 如果name和typed当前字符相同,则将两个指针分别向后移动;
- 如果typed中的当前字符和前一个字符相同,则只移动typed指针,继续比较下一个字符;
- 若以上两种情况都不满足,则说明name和typed存在不匹配的情况,直接返回false。
最终,若能顺利遍历完name和typed字符串,则说明匹配成功,返回true。
代码:
class Solution {
public:
bool isLongPressedName(string name, string typed) {
int i = 0, j = 0;
while (i < name.size() || j < typed.size()) {
if (i < name.size() && name[i] == typed[j]) { // 情况一:name和typed当前字符相同
i++; // name中的字符向后移动
j++; // typed中的字符向后移动
} else if (j > 0 && j < typed.size() && typed[j] == typed[j - 1]) { // 情况二:typed中的当前字符和前一个字符相同
j++; // typed中的字符向后移动
} else { // 其他情况,包括name和typed当前字符不同以及typed中的字符不是重复字符
return false; // 返回false,表示name和typed无法匹配
}
}
return true; // 若能顺利遍历完name和typed,则返回true,表示name和typed匹配成功
}
};
5每个产品在不同商店的价格
表:Products
+-------------+---------+ | Column Name | Type | +-------------+---------+ | product_id | int | | store1 | int | | store2 | int | | store3 | int | +-------------+---------+ 在 SQL 中,这张表的主键是 product_id(产品Id)。 每行存储了这一产品在不同商店 store1, store2, store3 的价格。 如果这一产品在商店里没有出售,则值将为 null。
请你重构 Products
表,查询每个产品在不同商店的价格,使得输出的格式变为(product_id, store, price)
。如果这一产品在商店里没有出售,则不输出这一行。
输出结果表中的 顺序不作要求 。
查询输出格式请参考下面示例。
示例 1:
输入: Products table: +------------+--------+--------+--------+ | product_id | store1 | store2 | store3 | +------------+--------+--------+--------+ | 0 | 95 | 100 | 105 | | 1 | 70 | null | 80 | +------------+--------+--------+--------+ 输出: +------------+--------+-------+ | product_id | store | price | +------------+--------+-------+ | 0 | store1 | 95 | | 0 | store2 | 100 | | 0 | store3 | 105 | | 1 | store1 | 70 | | 1 | store3 | 80 | +------------+--------+-------+ 解释: 产品 0 在 store1、store2、store3 的价格分别为 95、100、105。 产品 1 在 store1、store3 的价格分别为 70、80。在 store2 无法买到。
思路:
首先,从Products表中选择product_id和store1列的值,并将"store1"作为store列的值。然后,它筛选出store1列不为空的行。
接着,使用UNION关键字将其与另外两个类似的查询结果合并起来:
- 第二个查询选择product_id和store2列的值,并将"store2"作为store列的值。同样,筛选出store2列不为空的行。
- 第三个查询选择product_id和store3列的值,并将"store3"作为store列的值。同样,筛选出store3列不为空的行。
代码:
select product_id, 'store1' as store, store1 as price
from Products
where store1 is not null
union
select product_id, 'store2' as store, store2 as price
from Products
where store2 is not null
union
select product_id, 'store3' as store, store3 as price
from Products
where store3 is not null