LeetCode 热题 100 | 链表(中上)

目录

[1 141. 环形链表](#1 141. 环形链表)

[1.1 哈希表](#1.1 哈希表)

[1.2 快慢指针](#1.2 快慢指针)

[2 142. 环形链表 II](#2 142. 环形链表 II)

[2.1 哈希表](#2.1 哈希表)

[2.2 快慢指针](#2.2 快慢指针)

[3 21. 合并两个有序链表](#3 21. 合并两个有序链表)

[4 2. 两数相加](#4 2. 两数相加)


菜鸟做题第三周,语言是 C++

1 141. 环形链表

1.1 哈希表

解题思路:遍历链表,在哈希表中查询当前节点地址是否已经存在。若存在,则证明该链表有环,返回 True;若不存在,则将当前节点地址存入哈希表中。如果能够遍历完毕,则返回 False 。

cpp 复制代码
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode * p = head;
        unordered_set<ListNode *> set;

        while (p) {
            if (set.find(p) != set.end()) return true;
            set.insert(p);
            p = p->next;
        }

        return false;
    }
};
1.2 快慢指针

解题思路:

  1. 设置一快一慢指针
  2. 快指针每次移动两格,慢指针每次移动一格
  3. 如果快指针能够遇上慢指针,那么链表中存在环

思路说明图:

一开始我会想,兔子一定会在同一节点追上乌龟吗?会不会存在,虽然兔子从后面追上了乌龟,但是直接把乌龟超过了。那循环终止的条件怎么写啊?答案是,不存在这种情况。

巧妙之处就在,兔子虽然快,但是也只是每次移动两格。兔子和乌龟每次产生的差距都为 1,而 1 又是所有整数的因子。因此,假设兔子要从后面追上乌龟,且现在的距离为 N,那么经过 N 个时刻后兔子一定追上乌龟,即它们会相遇。

但如果兔子每次移动的不是两格,它和乌龟每次产生的差距为 d,那么就要考虑 N 能不能被 d 整除了。所以兔子每次移动两格不是随便设置的,而是有一定科学依据的。

cpp 复制代码
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if (head == nullptr) return false;

        ListNode * slow = head;
        ListNode * fast = head->next;

        while (slow != fast) {
            if (fast == nullptr || fast->next == nullptr) return false;
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }
};

2 142. 环形链表 II

2.1 哈希表

和上一题的代码几乎一模一样,只是返回的内容不同。

cpp 复制代码
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_set<ListNode *> set;
        ListNode * p = head;

        while (p) {
            if (set.find(p) != set.end()) return p;
            set.insert(p);
            p = p->next;
        }

        return nullptr;
    }
};
2.2 快慢指针

虽然用哈希表直接秒了,但是用快慢指针也是能做的。。。

请参考官方题解

3 21. 合并两个有序链表

解题思路:

  1. 设置 p 和 q 指针分管两条链
  2. 比较 p 和 q 所指向的节点的值
  3. 按照从小到大的顺序重新连接

注意:不是发现 p->val 小于 q->val 就赶紧重新连接,而是要让 p 继续往后找,找到最后一个小于 q->val 的 p->val,然后才重新连接。否则,不仅违背了从小到大的排序规则,还丢失了节点之间原本的连接。对于 q->val 小于 p->val 的情况同理。

思路说明图:这四幅图的顺序是从左到右,从上到下。

对于 p->val == q->val 的情况,哪边连哪边都无所谓。

cpp 复制代码
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if (list1 == nullptr && list2 == nullptr) return nullptr;
        if (list1 == nullptr) return list2;
        if (list2 == nullptr) return list1;

        ListNode * p = list1, * q = list2;
        while (p && q) {
            if (p->val <= q->val) {
                while (p->next && p->next->val <= q->val) p = p->next;
                ListNode * temp = p->next;
                p->next = q;
                p = temp;
            } else if (p->val >= q->val) {
                while (q->next && p->val >= q->next->val) q = q->next;
                ListNode * temp = q->next;
                q->next = p;
                q = temp;
            }
        }
        return list1->val <= list2->val ? list1 : list2;
    }
};

说明:这两行代码就是我说的 "要让 p 或 q 继续往后找"

cpp 复制代码
while (p->next && p->next->val <= q->val) p = p->next;
while (q->next && p->val >= q->next->val) q = q->next;

4 2. 两数相加

这道题比我想象的简单,我以为要算最后的总和,结果只需要算每一位的结果

解题思路:

  • 设置进位 carry
  • 每一位结果 = (p->val + q->val + carry) % 10
  • 如果 p->val + q->val + carry > 10,则新的 carry = 1

最重要的其实是 l1 和 l2 可能不一样长,因此需要考虑一条链表已经遍历完毕,而另一条链表还没有遍历完毕的情况。因此进行了如下处理:

cpp 复制代码
int n1 = p ? p->val : 0;
int n2 = q ? q->val : 0;
// ...
if (p) p = p->next;
if (q) q = q->next;

如果 p 或者 q 遍历完了,那么用 0 参加加法运算即可。

cpp 复制代码
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode * head = new ListNode(0), * tail = head;

        int carry = 0;
        ListNode * p = l1, * q = l2;
        while (p || q) {
            int n1 = p ? p->val : 0;
            int n2 = q ? q->val : 0;
            int sum = n1 + n2 + carry;
            tail->next = new ListNode(sum % 10);
            tail = tail->next;
            carry = sum / 10;
            if (p) p = p->next;
            if (q) q = q->next;
        }

        if (carry) {
            tail->next = new ListNode(carry);
        }

        return head->next;
    }
};
相关推荐
王中阳Go10 小时前
从超市收银到航空调度:贪心算法如何破解生活中的最优决策谜题?
java·后端·算法
故事挺秃然11 小时前
中文分词:机械分词算法详解与实践总结
算法·nlp
车队老哥记录生活13 小时前
【MPC】模型预测控制笔记 (3):无约束输出反馈MPC
笔记·算法
地平线开发者13 小时前
BEV 感知算法评价指标简介
算法·自动驾驶
不过四级不改名67714 小时前
用c语言实现简易c语言扫雷游戏
c语言·算法·游戏
C++ 老炮儿的技术栈15 小时前
手动实现strcpy
c语言·开发语言·c++·算法·visual studio
倔强的石头_16 小时前
【数据结构与算法】利用堆结构高效解决TopK问题
后端·算法
倔强的石头_16 小时前
【数据结构与算法】详解二叉树下:实践篇————通过链式结构深入理解并实现二叉树
后端·算法
哎写bug的程序员16 小时前
leetcode复盘(1)
算法·leetcode·职场和发展
风靡晚16 小时前
用于汽车毫米波雷达的四维高分辨率点云图像
人工智能·算法·机器学习·计算机视觉·汽车·信息与通信·信号处理