This commit is contained in:
Sainnhe Park 2022-12-02 19:12:03 +08:00
parent 4099f80edb
commit acc4f2169d
6 changed files with 198 additions and 2 deletions

View File

@ -0,0 +1,24 @@
#ifndef S0142_LINKED_LIST_CYCLE_HPP
#define S0142_LINKED_LIST_CYCLE_HPP
#include <unordered_map>
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
struct IterResult {
ListNode *node;
bool meet;
};
class S0142 {
public:
std::unordered_map<ListNode *, int> footprint;
IterResult iter(ListNode *fast, ListNode* slow, bool startUp);
ListNode *detectCycle(ListNode *head);
};
#endif

View File

@ -10,6 +10,7 @@
# 链表
- [总结](./linked-list.md)
- [环形链表](./linked_list_cycle.md)
# 字符串

View File

@ -8,14 +8,18 @@
```cpp
void iter(ListNode *node) {
// 终止条件
if (node == nullptr) {
return;
}
/*
your
condition
从前往后遍历
*/
iter(node->next);
/*
从后往前遍历
*/
return;
}
```

View File

@ -0,0 +1,40 @@
# 环形链表
[Leetcode](https://leetcode.com/problems/linked-list-cycle-ii/)
可以用回溯法解这道题。
首先递归遍历链表的一般结构如下:
```cpp
void iter(ListNode *node) {
// 终止条件
if (node == nullptr) {
return;
}
/*
从前往后遍历
*/
iter(node->next);
/*
从后往前遍历
*/
return;
}
```
而查找链表中是否有环的思路是快慢指针,如果相遇则说明有环。
所以我们可以这样:
终止条件是快指针走到链表末尾或者快慢指针相遇。
从前往后遍历不需要做什么额外操作。
从后往前遍历的时候先把快指针的足迹记录到一个哈希表中,键值是节点地址,值是快指针经过的次数。
当出现以下两种情况的时候就说明找到了环的入口:
1. footprint[fast] == 1 && footprint[fast->next] > 1
2. footprint[fast->next] == 1 && footprint[fast->next->next] > 1

View File

@ -0,0 +1,70 @@
#include "s0142_linked_list_cycle.hpp"
IterResult S0142::iter(ListNode *fast, ListNode *slow, bool startUp) {
// 终止条件
if (fast == nullptr || slow == nullptr) {
return {nullptr, false};
}
if (fast->next == nullptr) {
return {nullptr, false};
}
if (slow->next == slow) {
return {slow, true};
}
// 当两指针相遇时同样达到终止条件
if (fast == slow && !startUp) {
return {nullptr, true};
}
// 递归
IterResult result = iter(fast->next->next, slow->next, false);
// 从后往前遍历
// 达到了终止条件,但是没有两个指针遇见过
// 这说明没有环
if (!result.meet) {
return {nullptr, false};
} else {
// 有环的情况
// 我们把 fast 和 fast-next 节点所经过的足迹全部记录下来
// key 是所经过的节点
// value 是所经过的次数
if (footprint.count(fast) == 0) {
footprint[fast] = 1;
} else {
footprint[fast] += 1;
}
if (footprint.count(fast->next) == 0) {
footprint[fast->next] = 1;
} else {
footprint[fast->next] += 1;
}
// 什么情况下是环的入口呢?
// 1. footprint[fast] == 1 && footprint[fast->next] > 1
// 2. footprint[fast->next] == 1 && footprint[fast->next->next] > 1
if (footprint.count(fast) == 1 && footprint.count(fast->next) == 1) {
if (footprint[fast] == 1 && footprint[fast->next] > 1) {
return {fast->next, true};
}
}
if (footprint.count(fast->next) == 1 &&
footprint.count(fast->next->next) == 1) {
if (footprint[fast->next] == 1 && footprint[fast->next->next] > 1) {
return {fast->next->next, true};
}
}
// 返回
return result;
}
}
ListNode *S0142::detectCycle(ListNode *head) {
if (head == nullptr) {
return nullptr;
}
if (head->next == nullptr) {
return nullptr;
}
// 递归函数无法处理所有节点都在环里的情况,所以在这里加一个哑节点
ListNode dummy = ListNode(0);
dummy.next = head;
return iter(&dummy, &dummy, true).node;
}

View File

@ -0,0 +1,57 @@
#include "s0142_linked_list_cycle.hpp"
#include <gtest/gtest.h>
TEST(Problem142, Case1) {
ListNode node1 = ListNode(3);
ListNode node2 = ListNode(2);
ListNode node3 = ListNode(0);
ListNode node4 = ListNode(-4);
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
node4.next = &node2;
S0142 solution;
EXPECT_EQ(solution.detectCycle(&node1), &node2);
}
TEST(Problem142, Case2) {
ListNode node1 = ListNode(3);
ListNode node2 = ListNode(2);
ListNode node3 = ListNode(0);
ListNode node4 = ListNode(-4);
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
node4.next = &node1;
S0142 solution;
EXPECT_EQ(solution.detectCycle(&node1), &node1);
}
TEST(Problem142, Case3) {
ListNode node1 = ListNode(3);
ListNode node2 = ListNode(2);
ListNode node3 = ListNode(0);
ListNode node4 = ListNode(-4);
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
S0142 solution;
EXPECT_EQ(solution.detectCycle(&node1), nullptr);
}
TEST(Problem142, Case4) {
ListNode node1 = ListNode(1);
ListNode node2 = ListNode(2);
node1.next = &node2;
node2.next = &node1;
S0142 solution;
EXPECT_EQ(solution.detectCycle(&node1), &node1);
}
TEST(Problem142, Case5) {
ListNode node1 = ListNode(1);
node1.next = &node1;
S0142 solution;
EXPECT_EQ(solution.detectCycle(&node1), &node1);
}