In [1]:
1
2
3
4
from typing import List
import sys, random
sys.path.append("/home/swyoo/algorithm/")
from utils.verbose import logging_time, visualize_ds

128. Longest Consecutive Sequence

Disjoint Set(or Union Find)

With Sorting

\(O(nlogn)\)

In [2]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Disjoint Set data structure
par, rnk = {}, {}
cnt = {}
def find(x):
    if x not in par:
        par[x] = x
        rnk[x] = 0
        cnt[x] = 1
        return par[x]
    if x != par[x]:
        par[x] = find(par[x])
    return par[x]

def union(x, y):
    x, y = find(x), find(y)
    if x == y: return
    if rnk[x] > rnk[y]:
        x, y = y, x
    par[x] = y
    cnt[y] += cnt[x]
    if rnk[x] == rnk[y]:
        rnk[y] += 1
In [3]:
1
2
3
4
5
6
7
8
9
10
11
12
class Solution1:
    @logging_time
    def longestConsecutive(self, nums: List[int], show=False) -> int:
        if not nums: return 0
        nums = sorted(nums)
        for i in range(1, len(nums)):
            if nums[i] - nums[i - 1] == 1:
                union(nums[i - 1], nums[i])
        if show: visualize_ds(par)
        return max(cnt.values()) if cnt else 1

sol1 = Solution1()
In [4]:
1
2
A = [100,4,200,1,3,2]
print(sol1.longestConsecutive(A, show=True, verbose=True))

png

1
2
3
WorkingTime[longestConsecutive]: 173.99216 ms
4

Without Sorting

If the algorithm use some techniques as follows, it can be improved.

  • Conversion of nums from list to set: checking if exist takes $O(1)$.
  • Both ‘path compression’ ans ‘union by rank’

The time complexity of the whole process becomes as follows.
\(O(n\alpha)\)

In [5]:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution2:
    @logging_time
    def longestConsecutive(self, nums: List[int], show=False) -> int:
        if not nums: return 0
        nums = set(nums)
        used = set()
        for e in nums:
            if e not in used and e - 1 in nums:
                union(e - 1, e)
                used.add(e)
        if show: visualize_ds(par)
        return max(cnt.values()) if cnt else 1
sol2 = Solution2()
In [6]:
1
2
par, rnk, cnt = {}, {}, {}
print(sol2.longestConsecutive(A, show=True, verbose=True))

png

1
2
3
WorkingTime[longestConsecutive]: 162.27818 ms
4

In [7]:
1
2
size = 10000000
A = [random.randint(-size*10, size*10) for i in range(size)]
In [8]:
1
2
3
4
par, rnk, cnt = {}, {}, {}
ans1 = sol1.longestConsecutive(A, verbose=True) 
par, rnk, cnt = {}, {}, {}
ans2 = sol2.longestConsecutive(A, verbose=True)
1
2
3
WorkingTime[longestConsecutive]: 6538.45930 ms
WorkingTime[longestConsecutive]: 3892.41171 ms

Without Disjoint Set

I imported a solution from a document of the discuss

In [9]:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution3:
    @logging_time
    def longestConsecutive(self, nums):
        nums = set(nums)
        best = 0
        for x in nums:
            if x - 1 not in nums:
                y = x + 1
                while y in nums:
                    y += 1
                best = max(best, y - x)
        return best
sol3 = Solution3()
In [10]:
1
ans3 = sol3.longestConsecutive(A, verbose=True)
1
2
WorkingTime[longestConsecutive]: 4654.11186 ms

In [11]:
1
ans1, ans2, ans3
1
(6, 6, 6)

Leave a comment