문제링크: https://leetcode.com/problems/longest-consecutive-sequence/
Union Find
를 이용해 연속적인 원소가 나타났을 때 ex)234'5' 부모를 동일하게 묶어 동일한 부모는 연속적인 수임을 나타낼 수 있다.
nums
의 부모를 저장할 myMap
을 만든다getHead
는 최종 부모를 얻는 함수, union
은 최종부모를 동일하게 묶는 함수다.nums
를 순회하면서 myMap
에 num + 1
이나 num - 1
이 존재할 경우 둘을 union
한다.var longestConsecutive = function(nums) {
// 연이은 숫자
// union
const myMap = new Map(); // num => headNum union느낌?
const getHead = (num) => {
let el = num;
while (myMap.get(el) !== el) {
el = myMap.get(el);
}
return el;
}
const union = (num1, num2) => {
myMap.set(getHead(num2), getHead(num1));
}
for (let num of nums) {
if (!myMap.has(num)) myMap.set(num ,num);
if (myMap.has(num - 1)) union(num - 1, num);
if (myMap.has(num + 1)) union(num + 1, num);// union;
}
const result = new Map();
for (let key of myMap.keys()) {
const keyHead = getHead(key);
result.has(keyHead) ? result.set(keyHead, result.get(keyHead) + 1) : result.set(keyHead, 1);
}
return Math.max(...result.values(), 0);
};
Union Find
는 여러 종류를 묶는 방법을 이해하기에는 굉장히 명료하지만 대부분의 과정에 parent
를 구하는 과정이 들어있기 때문에 항상 효율적이라고 할 수 없다. 이번 문제처럼 하나의 링크에 단일 원소를 잇는 과정은 많은 반복이 필요하게 된다.
연속된 sequence를 찾는 과정은 숫자 앞뒤의 숫자가 nums에 포함되어 있는지 찾는 과정과 같다. nums
에 특정 숫자가 존재하는지 찾는 과정은 Set
을 통해 O(1)
의 시간복잡도를 통해 찾아낼 수 있다.
nums
를 Set
으로 만든다.mySet
의 원소 하나를 골라 Set
에서 제거하고 앞과 뒤의 연속된 숫자가 존재한다면 또한 Set
에서 제거한다.mySet
의 모든 원소가 사라질 때까지 반복한다.var longestConsecutive = function(nums) {
// 연이은 숫자
// using Set
let result = 0;
const mySet = new Set(nums);
for (let el of mySet) {
let start = el;
mySet.delete(start);
let end = start;
while (mySet.has(start - 1)) {
mySet.delete(--start);
}
while (mySet.has(end + 1)) {
mySet.delete(++end);
}
result = Math.max(result, end - start + 1);
}
return result;
};
Set
의 has
알고리즘은 해시테이블로 구성되어 이론상으론 O(1)
이지만 실제 구현은 완벽하진 않다. 하지만 적어도 O(n)
보다는 좋은 성능을 가지며 이를 통해 nums
에서 dfs로 sequence를 찾아 효율적으로 제거할 수 있었다. 모든 원소는 Set에 추가될때 O(n)
모든 원소가 탐색되어 제거될때 O(n)
로 총 O(n)
의 시간복잡도를 가진다.