Skip to content

Commit

Permalink
added 61. Longest Consecutive Sequence
Browse files Browse the repository at this point in the history
  • Loading branch information
pezy committed Dec 14, 2014
1 parent cdf081e commit 3ab5d98
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 0 deletions.
44 changes: 44 additions & 0 deletions 61. Longest Consecutive Sequence/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
这道题定为 hard 级别的原因并非是解决思路有多难想, 而是难在了效率的瓶颈.

要求在 O(n) 的复杂度情况下解决. 要知道, 完整的迭代一次就需要 O(n), 这相当于要求在一次迭代过程中, 计算出最大连续序列的长度来.

比较直接的思路是, 先排个序, 然后完整迭代, 中途记录 max 连击. 这个思路非常简单, 但且不论排序, 那一次完整的迭代就几乎要超标.

这种情况下, 以空间换时间就是最佳的选择. 如果我们在迭代过程中, 尝试记录当前最大的连续序列长度呢? 这几乎是矛盾的, 因为原数组并非有序. 如何可以知道连续序列的存在?

这让我想到了生活中一个真实的用例: 整理书籍. 学生时代, 每当大考之后, 都会清理书籍, 入库或者变卖. 我们是如何分类清理的呢? 遇到一本书, 看其属于哪一类, 然后放在一个固定的位置. 当全部书籍翻过一遍之后, 就会很自然的出现一堆堆的书堆. 每一堆都是同一类书籍. 我们很清楚的看到哪一类的书籍最多. 最高的那个就是啦.

这里也是一样, 如果我们弄一个 hash 表(key 为数组值, value 为连续序列 size), 将数组里每一个元素都放入表中, 然后发现连续, 则进行 `+1` 操作. 具体来说, 就是:

- 若 map[i+1] 和 map[i-1] 存在, 那么 i-1, i, i+1 构成一个三元序列, size == 3;
- 若只有 map[i+1] 或者只有 map[i-1], 那么 size = 2;
- 若 map[i+1] 和 map[i-1] 都不存在, 那么 size = 1; (自己)

上面三种情况可以写成一个式子:

map[i] = map[i-1] + map[i+1] + 1; // 左边记录的序列长度 + 右边记录的序列长度 + 自己

但当 map[i] 的值更新后, 我们还应该将其最新值(长度)扩展至该连续序列的边界上去. 可是如何扩展呢?

如 map[i] 的左边最远的连续节点是谁? 右边的最远连续节点是谁? 显然左边可以递推至 map[i-1] 那去, 右边同理. map[i-1] 如果等于1, 表示到它这就断了, 如果等于2, 那么表示它的左边还有一个连续节点.

那么这个节点的位置也可以用一个式子表示:

map[i - map[i-1]] // 左边最远节点, 用当前位置 - 左边的长度, 正好获得最左连续节点的坐标.
map[i + map[i+1]] // 右边同理.

所以当 map[i] 的值更新后, 应推广至最左与最右. 即:

map[i - map[i-1]] = map[i + map[i+1]] = map[i] = map[i-1] + map[i+1] + 1;

最后, 由于我们只关心最长连续序列的长度, 所以我们设置一个变量来记录它, 如 max.

max = std::max(max, map[i]);

综合上述几个式子, 可以归并为一句话:

max = std::max(max, map[i] = map[i - map[i-1]] = map[i + map[i+1]] = map[i-1] + map[i+1] + 1);

这句话即为核心了.

由于哈希表 unordered_map 默认值为 0. 所以如果遇到一个新值, 则记 map[i] == 1. 如果 map[i] != 0, 显然这个值我们已经存在于 hash 表中, 可以舍弃, 进行下一次迭代.
21 changes: 21 additions & 0 deletions 61. Longest Consecutive Sequence/TEST.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#define CATCH_CONFIG_MAIN
#include "../Catch/single_include/catch.hpp"
#include "solution.h"

using std::vector;

TEST_CASE("Longest Consecutive Sequence", "[longestConsecutive]")
{
Solution s;
SECTION( "common" )
{
vector<int> vec{100, 4, 200, 1, 3, 2};
REQUIRE( s.longestConsecutive(vec) == 4 );
}

SECTION( "max" )
{
vector<int> vec{0,3,7,2,5,8,4,6,0,1};
REQUIRE( s.longestConsecutive(vec) == 9 );
}
}
18 changes: 18 additions & 0 deletions 61. Longest Consecutive Sequence/solution.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <vector>
#include <unordered_map>
using std::vector; using std::unordered_map;

class Solution {
public:
int longestConsecutive(vector<int> &num) {
unordered_map<int, int> map;
int max{0};
for (auto i : num)
if (!map[i]) {
map[i] = map[i+1] + map[i-1] + 1;
map[i-map[i-1]] = map[i+map[i+1]] = map[i];
max = std::max(max, map[i]);
}
return max;
}
};

0 comments on commit 3ab5d98

Please sign in to comment.