【力扣】hot100系列(三)链表(一)(图示+多解法+时间复杂度分析)
本专栏文章持续更新,新增内容使用蓝色表示。
160. 相交链表 - 力扣(LeetCode)
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
整个链式结构中不存在环,函数返回结果后,链表必须保持其原始结构 。
自定义评测:
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,则正确 。
解法一:双指针
注意理解本题,逻辑和物理上后续的节点都是共同的。
一开始没申清题目时以为是逻辑上后续节点相同,物理上不同,想着用反转链表尝试解题。
至于双指针的解法,直接看代码可能并不能直观理解,可以看一组图:
有交点双指针过程图示:
最终两节点相遇,退出循环,返回ha或者hb都可以。
无交点双指针过程图示:
两个指针最终都走到了对方的末尾,依旧是返回任意一个都有效。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {ListNode *hA = headA, *hB = headB;while (hA != hB) {hA = hA ? hA->next : headB;hB = hB ? hB->next : headA;}return hA;}
};
时间复杂度:O(m+n) ,设链表 A 的长度为 m,链表 B 的长度为 n。
空间复杂度:O(1)。
解法二:哈希
注意一个误区,此处存储的是地址,不是值,值有可能会有值相同但不是节点的情况。
class Solution {
public:ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {unordered_set<ListNode*> check;ListNode* ha = headA;ListNode* hb = headB;while (ha != nullptr) {check.insert(ha);ha = ha->next;}while (hb != nullptr) {if (check.find(hb) != check.end()) {return hb;}hb = hb->next;}return hb;}
};
时间复杂度:O(m+n) ,设链表 A 的长度为 m,链表 B 的长度为 n。
空间复杂度:O(m)。
206. 反转链表 - 力扣(LeetCode)
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例:
输入:head = [4,1,2,7]
输出:[7,2,1,4]
图示过程:
解法一
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode* pre = nullptr;ListNode* cur = head;while (head != nullptr) {head = head->next;cur->next = pre;pre = cur;cur = head;}return pre;}
};
时间复杂度:O(n)。
空间复杂度:O(1)。
解法二
感兴趣的朋友可以尝试使用递归解答。
【后续如果感兴趣了,会在这里添加】
看的出来我不太感兴趣。
234. 回文链表 - 力扣(LeetCode)
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:head = [1,2,2,1]
输出:true示例 2:
输入:head = [1,2]
输出:false
解法一
第一个想法是直接将链表翻转,对比是否相同,翻转后与原链表相同则为回文链表。这种需要借助额外的空间,不能直接在原链表上更改。
class Solution {
public:bool isPalindrome(ListNode* head) {if (!head || !head->next)return true;ListNode* copied = copyList(head);ListNode* reversed = reverseList(copied);ListNode* p1 = head;ListNode* p2 = reversed;while (p1 && p2) {if (p1->val != p2->val) {return false;}p1 = p1->next;p2 = p2->next;}return true;}ListNode* copyList(ListNode* head) {ListNode* dummy = new ListNode(0);ListNode* curr = dummy;ListNode* orig = head;while (orig) {curr->next = new ListNode(orig->val);curr = curr->next;orig = orig->next;}return dummy->next;}ListNode* reverseList(ListNode* head) {ListNode* pre = nullptr;ListNode* cur = head;while (head != nullptr) {head = head->next;cur->next = pre;pre = cur;cur = head;}return pre;}
};
时间复杂度:O(n)。
空间复杂度:O(n)。
解法二:数组+双指针
将链表的值存入数组中,数组支持随机访问,就好办了很多。
class Solution {
public:bool isPalindrome(ListNode* head) {vector<int> list;while (head != nullptr) {list.push_back(head->val);head = head->next;}for (int pre = 0, back = list.size() - 1; pre < list.size() / 2;pre++, back--) {if (list[pre] != list[back]) {return false;}}return true;}
};
时间复杂度:O(n)。
空间复杂度:O(n)。
如有问题或建议,欢迎在评论区中留言~