①相交链表
编写一个程序,找到两个单链表相交的起始节点。
例如,下面的两个链表:
A: a1 → a2 ↘ c1 → c2 → c3 ↗ B: b1 → b2 → b3
在节点 c1 开始相交。
注意:
- 如果两个链表没有交点,返回
null
. - 在返回结果后,两个链表仍须保持原有的结构。
- 可假定整个链表结构中没有循环。
- 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
看到这道题目描述时,有点懵逼。。。没太看懂什么意思,后寻找大佬的算法才总算明白了。其实只要理解了同一个节点不可能有两个next这一点就比较好做,比如A[1,2,3,4,5,6,7,11,12,13,14], B[0,3,4,5,9,10]两个list,5节点后不可能有两个next,所以题目不可能出现这种测试用例。所以只可能出现在节点相交之后后面的每个节点都相同,利用这点可以解决该题!
1.计算两个链表的长度,然后将长链表遍历到与短链表相等长度的地方,然后两个链表进行比较,若节点相同且不为空则返回该节点,否则返回NULL:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (!headA || !headB) //如果有链表为空则返回NULL,没有交点
return NULL;
int lenA = getlen(headA), lenB = getlen(headB); //调用getlen函数得到链表长度
if (lenA > lenB) { //将长链表遍历到与短链表相同的长度
for (int i = 0;i < lenA-lenB; ++i)
{
headA = headA->next;
}
}
else {
for (int i = 0; i < lenB-lenA; ++i)
{
headB = headB->next;
}
}
while (headA && headB && headA != headB) { //进行判断,如果某个节点相等、节点为空则退出
headA = headA->next;
headB = headB->next;
}
return (headA && headB) ? headA : NULL; //再进行判断,如果不是尾节点则返回上一层循环得到的节点,否则返回NULL
}
int getlen(ListNode *p) //得到链表的长度
{
int cnt = 0;
while (p)
{
++cnt;
p = p->next;
}
return cnt;
}
};
2.可以将它当成有环进行思考,让两链表同时从头节点开始遍历,如果某个链表遍历到末尾时,则从另一个链表的头节点开始遍历。两个指针最终一定会相等,从而跳出循环,而且只有两种情况,一种是在交点处相等,另一种是遍历到链表末尾相等。为什么说一定会相等呢?因为两个链表节点所走的路程恰好是两个链表的长度,所以一定会相等。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (!headA || !headB) return NULL; //如果其中有一个链表为空则返回NULL
ListNode *a = headA, *b = headB;
while (a != b) { //遍历两个链表
a = a ? a->next : headB;
b = b ? b->next : headA;
}
return a;
}
};
3.使用set存储节点,将其中一个链表headA的节点存入set,然后遍历另一个链表headB,然后在set查找与当前headB相等的节点,若存在则返回该节点,若不存在则继续遍历:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode*> st; //保存headA节点
while (headA)
{
st.insert(headA);
headA = headA->next;
}
while (headB)
{
if (st.find(headB) != st.end()) //在headA节点中查找是否存在当前headB节点,如果存在则返回该节点,否则继续遍历
return headB;
headB = headB->next;
}
return NULL;
}
};
②两数之和Ⅱ-输入有序数组
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
- 返回的下标值(index1 和 index2)不是从零开始的。
- 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9 输出: [1,2] 解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
1.与第一题两数之和类似的做法,可以回顾一下两数之和,基本代码都类似:
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
unordered_map<int,int> mp;
vector<int> vec;
for (int i = 0; i < numbers.size(); ++i)
{
mp[numbers[i]] = i;
}
for (int i = 0; i < numbers.size(); ++i)
{
if (mp[target - numbers[i]] && mp[target - numbers[i]] > i) //因为升序数组,所以大的数的下标肯定大于小的数
{
vec.push_back(i + 1);
vec.push_back(mp[target-numbers[i]] + 1);
}
}
return vec;
}
};