15. 3SUM

Evan Lee ㅣ 2023. 6. 13. 02:58

문제

https://leetcode.com/problems/3sum/description/

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] 
such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.

주어진 정수 배열들로 인덱스가 겹치지않고, 모든 합이 0에 만족하는 삼중 항인 [nums[i], nums[j], nums[k]] 반환하세요.
중복 삼중항은 없어야합니다.

 

무식하게 풀어보기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var threeSum = function(nums) {
    let answer = [];
  for ( let i = 0; i < nums.length - 2; i++ ) {
    for ( let j = i + 1; j < nums.length - 1; j++ ) {
      for ( let k = j + 1; k < nums.length; k++ ) {
        if ( nums[i] + nums[j] + nums[k] === 0 ) {
          const sortedTripet = [nums[i], nums[j], nums[k]].sort((a,b) => a - b);
          answer.push(sortedTripet)
        }
      }
    }
  }
  const set = new Set(answer.map(JSON.stringify));
  return answer = Array.from(set).map(JSON.parse);
};
cs
우리들의 친구 브루트포스로 무려 3중 for문을 돌리면서 맞막에 겹치는건 sort로 풀어주고 set 객체를 통해 없앴는데... 물론 다들 예상하였다 싶이... 런타임에 걸렸다. 

 

투포인터를 이용한 최적화된 정답 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
const solution = (nums) => {
  let answer = [];
  
  // 예외처리를 해줍니다. 
  if(nums.length<3return answer;
 
  // 내림차순으로 정렬합니다.
  nums.sort((a,b) => a-b);
  
  // 정수 배열을 순회합니다.
  for(let i = 0 ; i < nums.length; i+=1) {
 
    // 만약 현재 숫자가 0보다 크다면 더이상 순회할 필요가 없습니다. 내림차순을 했기 때문입니다.
    if(nums[i] > 0break;
 
    // 만약 현재 숫자가 이전 숫자와 같다면 중복된 결과가 나오므로 continue를 해줍니다.
    if(i > 0 && nums[i] === nums[i-1]) continue;
 
    // 투포인터를 사용합니다.
    let left = i+1;
    let right = nums.length-1;
 
    // left가 right보다 작을 때까지 순회합니다.
    while(left < right) {
      // 3개의 숫자를 더합니다.
      const sum = nums[i] + nums[left] + nums[right];
 
      // 만약 3개의 숫자의 합이 0보다 작다면 left를 증가시킵니다.
      if(sum < 0) {
        left+=1;
      } 
      
      // 만약 3개의 숫자의 합이 0보다 크다면 right를 감소시킵니다.
      else if(sum > 0) {
        right-=1;
      } 
      
      // 3개의 숫자의 합이 0이라면 정답 배열에 넣어줍니다.
      else {
        answer.push([nums[i], nums[left], nums[right]]);
 
        // 만약 left와 다음에 나올 left+1의 값이 같다면 left를 증가시킵니다.
        while(left < right && nums[left] === nums[left+1]) left+=1;
        // 만약 right와 다음에 나올 right-1의 값이 같다면 right를 감소시킵니다.
        while(left < right && nums[right] === nums[right-1]) right-=1;
 
        // left와 right를 각각 증가, 감소시킵니다.
        left+=1;
        right-=1;
      }
    }
  }
  return answer;
};
cs
일단 나는 항상 예외처리를 깜박한다. 그리고 문제를 잘 읽어보면 정답으로 반환하는 정수의 순서는 상관이 없다고 써있었다. 그렇다는 뜻은 입력받은 정수 배열을 내가 손 봐도 된다는 뜻이다. 그래서 합이 0이라는 조건이 있으니까 정렬을 해서 애초에 그 값이 안나오는 경우는 다  스킵할 수 있었고, 또한 겹치는 숫자와 앞서 진행했던 숫자가 다음 인덱스에서도 나온다면 굳이 연산을 안하고 지나가면 되기때문에 최적화가 가능했다. 기본적이지만 생각이 좀 많이 필요했던 문제였다.

'Javascript > LeetCode' 카테고리의 다른 글

11 - Container With Most Water  (0) 2023.06.01
2. Add Two Numbers  (0) 2023.05.27