🤗C++菜鸡刷leetcode之每日两道题🤗
题目1:160. 相交链表
方法1:链表A单独的部分为a【绿色】,链表B单独的部分为b【蓝色】,相交之后的部分为c【红色】。
两个指针,一个从链A开始,走完就从B开始;一个从链B开始,走完就从A开始。
因此 绿 + 红 + 蓝 + 红 = 蓝 + 红 + 绿 + 红 -> 最后两个指针都走了红部分,可以去掉 不影响结果,则得出:
绿 + 红 + 蓝 = 蓝 + 红 + 绿,因此当两个指针分别走完自己的蓝与自己的红时,会在红色部分相遇。
a1 -> a2 -> c1 -> c2 -> c3 -> b1 -> b2 -> b3 -> c1
b1 -> b2 -> b3 -> c1 -> c2 -> c3 -> a1 -> a2 -> c1
若一个链表长度为n,另一链表长度为m。时间复杂度O(n+m)
,空间复杂度O(1)
。代码如下:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *p = headA;
ListNode *q = headB;
while(p != q){
p = (p == nullptr ? headB : p->next);
q = (q == nullptr ? headA : q->next);
}
return q;
}
};
方法2:
链表A长度为5,链表B长度为6。把长度对齐后,开始一起向后遍历,指针相同的地方则为相交节点
若一个链表长度为n,另一链表长度为m。时间复杂度O(n+m+2n/2m)
,空间复杂度O(1)
。代码如下:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB){
ListNode *p = headA, *q = headB;
int lena = 0, lenb = 0;
// 求出链表A 和 链表B 的长度
while(p != nullptr) lena++, p=p->next;
while(q != nullptr) lenb++, q=q->next;
// 对齐
p = headA, q = headB;
if(lena >= lenb){
while(lena > lenb) p = p->next,lena--;
}else{
while(lenb > lena) q = q->next,lenb--;
}
// 查找相交节点
while(q != p) p = p->next,q =q->next;
return q;
}
};
题目2:234. 回文链表
234. 回文链表
方法1:利用数组的数据结构判断是否回文,遍历链表将链表的所有节点的值放在数组中,反转数组,看数组反转前后是否相同,若相同则链表为回文链表。该方法时间复杂度O(n)
,空间复杂度O(n)
。代码如下:
class Solution {
public:
bool isPalindrome(ListNode* head) {
bool ans;
vector<int> nums;
while(head != nullptr){
nums.push_back(head->val);
head = head -> next;
}
vector<int> nums1 = nums;
reverse(nums.begin(), nums.end());
if(nums == nums1) ans = true;
else ans = false;
return ans;
}
};
方法2:快慢指针找中间节点,反转链表一半链表,最后看是两链表否相同。可以分为三个步骤:
① 利用快慢指针fast和slow,找中间的节点。此时如果节点数为奇数,停止while循环时 fast指针不为nullptr;若节点数为偶数,停止while循环时 fast指针为nullptr。
② 对 [head, slow) 这一段链表进行反转,反转后的链表头为left指针
③ 一个链表头是left,另一个链表头若 节点数为奇数为slow->next
,偶数则为slow
。两个链表一起从头开始遍历,若值有不同则返回false,否则都相同则返回true
该方法时间复杂度O(n)
,没有用到其它的空间复杂度O(1)
。代码如下:
class Solution {
public:
bool isPalindrome(ListNode* head) {
// 1.快慢指针找中间节点,如果fast!=nullptr,链表节点数为奇数;否则为偶数
ListNode *fast = head, *slow = head;
// 如果使用fast->next->next为判断条件,当链表为空或只有一个节点时,可能会引发空指针异常
while(fast && fast->next){
slow = slow -> next;
fast = fast -> next -> next;
}
// 2.对前面的链表进行reverse
ListNode *cur = head, *left=nullptr, *right;
while(cur && cur != slow){
right = cur->next;
cur->next = left;
left = cur;
cur = right;
}
// 3.两个链表比较 值是否相同
slow = (fast) ? slow->next : slow; //确定开始比较的节点
while(left && slow){
if(left->val != slow->val){
return false;
}
left = left -> next;
slow = slow -> next;
}
return true;
}
};
方法3:回溯。先使用指针right通过dfs()函数从链表头部开始递归遍历到链表尾部。此时left在链表头,right在链表尾,如果left和right相等则将left指针右移,回溯的过程回让right左移,则可以进行判断。该方法时间复杂度O(n)
,递归用到了栈因此空间复杂度O(n)
。
代码如下:
class Solution {
public:
int ans = true;
ListNode *left;
void dfs(ListNode* right){
if(right == nullptr) return; // 如果right到链表末尾,则停止递归开始回溯
dfs(right->next); // 递归
// 开始回溯,比较节点的值并根据需要移动左指针
if(ans){
if(left->val != right->val) ans = false;
else left = left->next;
}
}
bool isPalindrome(ListNode* head) {
left = head;
dfs(head);
return ans;
}
};