https://leetcode.com/problems/3sum/
코드설명
정렬(Sort) + 투포인터(Two Pointer) + 아이디어(Idea) 를 활용합니다.
완전탐색으로도 가능합니다만, 재귀가 아닌 for문을 사용해야 성공합니다.
코드
시간초과 발생코드입니다. 3^2 x 10^6 이어서 1억을 초과하지 않기에 시간초과를 회필할 수 있을 것이라 생각했는데, 아쉽게 시간초과가 발생합니다.
(확인해보니, 저의 경우 재귀를 활용해서 작업을 했기에 시간초과가 납니다. 이중 for문으로 2개의 숫자를 정하고 3개의 숫자는 안하는 방식으로 하면 시간초과가 안납니다.)
import java.util.*; class Solution { //Brute Force Complexity : 3^3 x 10^9 //-> Better Brute Force : 3^2 x 10^6 List<List<Integer>> arr = new ArrayList<List<Integer>>(); HashMap<Integer, Integer> hashmap = new HashMap<>(); HashSet<String> hashset = new HashSet<String>(); public List<List<Integer>> threeSum(int[] nums) { for(int i=0; i<nums.length; i++){ hashmap.put(nums[i], hashmap.getOrDefault(nums[i], 0) + 1); } // Arrays.sort(nums, 0, nums.length); DFS(nums, 0, 0, 0, new ArrayList<Integer>()); return arr; } void DFS(int[] nums, int level, int idx, int sum, List<Integer> list){ if(level == 2){ //전체 중복체크.. 숫자는 3000까지 존재. //HashSet으로 한다면, 문자열을 가지고 있어야 한다. if(hashmap.containsKey(-sum) == true && hashmap.get(-sum) > 0){ list.add(-sum); // String numString1 = ""; String numString1 = String.valueOf(list.get(0)) + String.valueOf(list.get(1)) + String.valueOf(list.get(2)); String numString2 = String.valueOf(list.get(0)) + String.valueOf(list.get(2)) + String.valueOf(list.get(1)); String numString3 = String.valueOf(list.get(1)) + String.valueOf(list.get(0)) + String.valueOf(list.get(2)); String numString4 = String.valueOf(list.get(1)) + String.valueOf(list.get(2)) + String.valueOf(list.get(0)); String numString5 = String.valueOf(list.get(2)) + String.valueOf(list.get(0)) + String.valueOf(list.get(1)); String numString6 = String.valueOf(list.get(2)) + String.valueOf(list.get(1)) + String.valueOf(list.get(0)); if(hashset.contains(numString1) == false){ hashset.add(numString1); hashset.add(numString2); hashset.add(numString3); hashset.add(numString4); hashset.add(numString5); hashset.add(numString6); arr.add(new ArrayList<Integer>()); arr.get(arr.size() - 1).add(list.get(0)); arr.get(arr.size() - 1).add(list.get(1)); arr.get(arr.size() - 1).add(list.get(2)); } list.remove(list.size() - 1); } return ; } for(int i=idx; i<nums.length; i++){ list.add(nums[i]); hashmap.put(nums[i], hashmap.get(nums[i]) - 1); DFS(nums, level + 1, i + 1, sum + nums[i], list); hashmap.put(nums[i], hashmap.get(nums[i]) + 1); list.remove(list.size() - 1); } } }
투포인터를 활용한 신박한 방식입니다.
가장 핵심은, 중복된 숫자가 연속될 경우 중복 연산을 애초에 피할 수 있다는 것 입니다. 중복될 경우에는 i의 경우는 아예 다시는 판단하지 않고, j의 경우에는 계속해서 넘어가도록 처리합니다. ( 생각해보면, i값을 기준으로 숫자들이 연속된다면, 의미가 없음을 알 수 있습니다. j값을 기준으로 해도 마찬가지입니다. )
import java.util.*; class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> answer = new ArrayList<>(); Arrays.sort(nums); for(int i=0; i<nums.length; i++){ if(i > 0 && nums[i] == nums[i-1]){ // i++; continue 대신 이걸로 처리할시 중복이 연속될경우를 처리못합니다. continue; } int j = i + 1; int k = nums.length - 1; while( j < k){ int total = nums[i] + nums[j] + nums[k]; if(total > 0){ k -= 1; }else if(total < 0){ j += 1; } else { answer.add(Arrays.asList(nums[i], nums[j], nums[k])); j += 1; //while(l2 < r1){ 처음에 잘못작성한 코드입니다. 이렇게 되면 무한루프가 걸립니다. // while문 자체에 nums[l2-1] == nums[l2] 일때만 이동하도록 처리해야만 이동합니다. // if(nums[l2 - 1] == nums[l2]){ // l2 += 1; // } //} while(nums[j-1] == nums[j] && j < k){ j += 1; } } } } return answer; } }