[LeetCode] #15. 3Sum (Python/파이썬)

2023. 8. 9. 23:12·Coding Practice/LeetCode
728x90

작성일시: 2023년 8월 9일 오후 8:55
등급 : Medium

📖Problem: #15. 3Sum

  • 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.
    정수 배열 nums에서 i != j, i != k, j != k 이면서 nums[i] + nums[j] + nums[k] == 0를 만족하는 모든 Triplets를 return하라.
  • Notice that the solution set must not contain duplicate triplets.
    솔루션 세트에는 중복된 Triplets이 포함되어서는 안된다.

🔍Institution

  • 문제 조건
    • i, j, k는 서로 달라야 함
    • 3개를 더해서 0을 만족하는 리스트를 반환
    • 중복이 되면 안 됨
  • 무슨 유형일까?
    • brute force → 3중 for문으로 해야 함 → 비효율적이므로 하지 않을 거임
    • two pointer : left, right와 새로운 변수를 하나 활용하여 값을 비교한다면 반복문을 하나 줄일 것으로 판단함 ⇒ two pointer로 풀 예정!

🔍Approach

더보기

삽질1: two_sum 변수 안에 연속되는 2개의 인덱스의 값을 넣은 후, left와 right로 비교→ 모든 케이스를 커버할 수 없음

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        answer = []
        left = 0
        right = len(nums)-1
        sorted(nums)
        
        while left < right:
            two_sum = nums[left] + nums[left+1]
            if two_sum + nums[right] == 0:
                if two_sum + nums[right] is not answer:
                    answer.append([nums[left], nums[left+1], nums[right]])
                    left += 1
            elif two_sum + nums[right] < 0:
                right -= 1
            elif two_sum + nums[right] > 0:
                right -= 1
        
        return answer

→ 모든 케이스를 커버할 수 없음

삽질2

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        answer = []
        left = 0
        right = len(nums)-1
        sorted(nums)

        target = 0

        while left < right:
            target = - (nums[left] + nums[right])
            for i in range(left, right): 
                if nums[i] == target:
                    answer.append([left, target, right])
                    left += 1
                    right -= 1
            if target < 0:
                left += 1
            
            

        return answer

 

위의 삽질의 결과는 “시간초과”와 “모든 경우의 수를 탐색하지 않은 것”이었다.
=> 즉, 시간초과가 되지 않으면서 “모든 경우의 수"탐색할 수 있어야 한다.

새로 풀이한 방식은 하나의 fix값을 고정한 후, 나머지에서 left와 right를 설정하여 투포인터 방식으로 풀이하는 것이다.

 

Flow
  • for문을 i=1부터 nums리스트 길이까지 반복한다.
  • fix변수는 for문 안에서 i의 값에 따라 고정하는 변수이다. 즉 fix = nums[i] 값을 저장한다. left의 값은 i의 다음이 되어야 한다. i와 같거나 i=0부터 시작한다면 이전에 봤던 것과 겹치기 때문! right의 값은 투포인터의 정석대로 가장 마지막 인덱스 값을 넣어주어야 한다.
    fix = nums[i] left = i + 1 right = len(nums) - 1
  • 이후 left가 right랑 같지 않을 동안 반복한다.
    • fix값과 nums[left], nums[right]를 모두 더한 값이 0인 경우,
      • answer에 [fix, nums[left], nums[right] 가 존재하지 않다면 ⇒ [fix, nums[left], nums[right]을 append한다.
      • 이때, 0인 경우가 나왔을 때 break하는 것이 아니라 다음으로 바로 넘어간다 (left += 1)
    • fix값과 nums[left], nums[right]를 모두 더한 값이 0보다 작은 경우,
    • ⇒ left값을 +1 한다.
    • fix값과 nums[left], nums[right]를 모두 더한 값이 0보다 큰 경우,
    • ⇒ right값을 -1한다.

 

최종 코드

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        answer = []
        left = fix = 0
        right = len(nums)-1
        nums = sorted(nums)

        for i in range(len(nums)):
            fix = nums[i]
            left = i + 1
            right = len(nums) - 1
            while left < right:
                if fix + nums[left] + nums[right] == 0:
                    if [fix, nums[left], nums[right]] not in answer:
                        answer.append([fix, nums[left], nums[right]])
                    left += 1

                elif fix + nums[left] + nums[right] < 0:
                    left += 1
                elif fix + nums[left] + nums[right] > 0:
                    right -= 1
        return answer

고려해야 할 상황

  • 모든 테스트케이스는 다 통과했으나, submit했을 때 새로운 테스트케이스에서 오류가 났음
  • [0,0,0,0]에서 time limit가 뜸
    • fix = 0, left = 0, right = 0 임 ⇒ 이것 때문에 0,0,0,0인 경우를 따로 만들었음
    • 즉, answer리스트에 있는지 없는지 판단하는 것이 필요함
  • [-2,0,1,1,2]가 들어오면 output 으로 [[-2, 0, 2], [-2,1,1]] 이 나와야 하는데 내 출력값은 [[-2,0,2]]만 나옴
    • 이 말은 즉슨, fix가 같은 값일 때 하나의 답만 나오는게 아니라 여러 값이 나올 수도 있다는 것을 의미함
    • 즉, append한 조건문에서 break나 return을 하면 안 된다는 것을 의미함!!
    • 그럼 어떻게???
      • fix한 값에서 다음 left값에서도 정답이 나올수도 있는거니까, 다음으로 넘어가야 함 left += 1하면 됨

정리

answer리스트에 이미 있는지 없는지 판단 ⇒

if [fix, nums[left], nums[right]] not in answer:

fix한 값 안에서도 여러 정답이 나올 수 있으므로, break가 아닌 다음 값으로 넘어가기

if fix + nums[left] + nums[right] == 0:
    if [fix, nums[left], nums[right]] not in answer:
        answer.append([fix, nums[left], nums[right]])
    left += 1

 

🚩My submission

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        answer = []
        left = fix = 0
        right = len(nums)-1
        nums = sorted(nums)

        for i in range(len(nums)):
            fix = nums[i]
            left = i + 1
            right = len(nums) - 1
            while left < right:
                if fix + nums[left] + nums[right] == 0:
                    if [fix, nums[left], nums[right]] not in answer:
                        answer.append([fix, nums[left], nums[right]])
                    left += 1

                elif fix + nums[left] + nums[right] < 0:
                    left += 1
                elif fix + nums[left] + nums[right] > 0:
                    right -= 1

        return answer

  • 시간 복잡도 : O(NlogN)
    • 보통 투 포인터로 풀게 된다면while 조건문이 left < right이므로 --> O(logN)
    • 근데 이 문제는 투포인터를 nums의 길이만큼 반복(for 문) 하기 때문에 --> O(NlogN)

 

🚩Others submission

class Solution:
   def threeSum(self, nums: List[int]) -> List[List[int]]:
       answer = []
       nums.sort()

       for i, a in enumerate(nums):
           if i > 0 and a == nums[i-1]:
               continue

           left, right = i+1, len(nums)-1

           while left < right:
               sum3 = a + nums[left] + nums[right]

               if sum3 > 0 :
                   right -= 1
               elif sum3 < 0:
                   left += 1
               else:
                   answer.append([a, nums[left], nums[right]])
                   left += 1
                   while nums[left] == nums[left-1] and left < right:
                       left += 1
       return answer

코드 설명:

 

if i > 0 and a == nums[i-1]:
               continue
  • 이 부분의 코드를 추가하면 시간을 더 효율적으로 쓸 수 있다고 함
  • 실제로 보았을 때 위 코드는 955ms가 뜨고, 내가 짠 코드는 6561ms가 되는 것으로 보아 훨씬 효율적인 것으로 볼 수 있다.

 

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        answer = []
        nums.sort()

        len_nums = len(nums)

        for i in range(0, len_nums-2):
            for j in range(i+1, len_nums-1):
                for k in range(j+1, len_nums):
                    if nums[i] + nums[j] + nums[k] == 0:
                        if [nums[i], nums[j], nums[k]] not in answer:
                            answer.append([nums[i], nums[j], nums[k]])
        return answer

💡TIL

  • 다음으로 넘어갈 때 바로 break시키는 게 아니라 테스트케이스를 확인해야 함!!
  • sorted함수 쓰면 대입을 해줘야 함.. 바보바보..
  • fix 부분 // 내가 짠 거에 헷갈렸음.. nums[fix]가 아님 fix = nums[i] 로 했으니까 그냥 fix만 넣어도 되는거임
  • 투포인터를 2개의 값만 비교했었는데 3개의 값을 비교할 때는 어떤 방식으로 접근하면 되는지 다양한 방식으로 사고할 수 있었다. (물론 지금 정리한 방식 말고 나머지 2개는 모든 경우의 수를 다루지 않은 경우긴 했다..)
    • 3개의 값을 투포인터로 비교할 때는 하나의 고정값이 필요하다.
728x90
저작자표시 비영리 변경금지 (새창열림)
'Coding Practice/LeetCode' 카테고리의 다른 글
  • [LeetCode] #101. Symmetric Tree (Python/파이썬)
  • [LeetCode] #70. Climbing Stairs (Python/파이썬)
  • [LeetCode] 94. Binary Tree Inorder Traversal (Python)
  • [LeetCode] #167. Two Sum 2 (Python)
밍구링구리
밍구링구리
밍구리의 실험실 이것저것 배우고 기록을 남깁니다
  • 밍구링구리
    Mingguri Labatory
    밍구링구리
    • 홈
    • 태그
    • 방명록
    • 깃허브
  • 전체
    오늘
    어제
    • 분류 전체보기 (96)
      • Language & Framework (15)
        • Java (3)
        • Vue.js (1)
        • Spring Boot (10)
      • Computer Science (4)
        • Operating System) (0)
        • Database (2)
        • Network (1)
        • Data Structure (0)
        • Algorithm (1)
      • Architecture & Design (7)
      • Cloud & Infra (5)
      • Trouble Shooting (0)
        • 에러 해결 (0)
        • 삽질 기록 (0)
      • 회고 (0)
      • Coding Practice (42)
        • Backjoon (24)
        • LeetCode (6)
        • Programmers (12)
      • Book (0)
      • ETC (21)
        • 블로그 관리 (3)
        • 일상 (1)
        • 후기 (2)
        • 밍구의 실험실 (1)
        • 🤔Study . Question🔍 (14)
      • 부트캠프 (1)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 태그

    스터디
    유스케이스
    알고리즘
    dfs
    퀴즈
    BFS
    프로그래머스
    LeetCode
    코딩테스트
    그리디
    구현
    Spring Boot
    OOP
    우선순위큐
    git
    코드잇
    투포인터
    테스트케이스
    스케일아웃
    SOLID
    골드5
    백준
    골드4
    문제
    vue.js
    백트래킹
    백엔드
    스케일업
    객체지향
    level 2
  • 250x250
  • hELLO· Designed By정상우.v4.10.3
밍구링구리
[LeetCode] #15. 3Sum (Python/파이썬)
상단으로

티스토리툴바