[알고리즘 기초] 다중 포인터 패턴(Multiple Pointers -java)
JavaScript (JS) Algorithms and Data Structures Masterclass | Udemy
해당 포스팅은 JavaScript 알고리즘 & 자료구조 마스터클래스강의를 참고하여 작성했습니다.
이 패턴의 개념은 인덱스
나 위치
에 해당하는 포인터나 값을 만든 다음 특정 조건에 따라 중간 지점에서부터 시작 지점
이나 끝 지점
이나 양쪽 지점
을 향해 이동시키는 것 입니다.
쉽게 이야기하면 이중 연결 리스트
나 단일 연결 리스트
를 만드는 것 입니다.(한쌍의 값이나 조건을 충족시키는 무언가를 찾는 개념)
다중 포인터 패턴(Multiple Pointers)
다음과 같이 오름차순으로 정렬된 배열에서 합계가 0인 첫 번째 쌍을 찾는 함수를 작성하세요.
예시
[-3, -2, -1, 0, 1, 2, 3] -> [-3, 3]
[-2, 0, 1, 3] -> X
[1, 2, 3] -> X
simpleSumZero
먼저 시간복잡도 O(N^2)
, 공간 복잡도 O(1)
가 사용된 간단한 알고리즘입니다.
// O(N^2)
void simpleSumZero(List<Integer> arr) {
for(int i = 0; i < arr.size(); i++) {
for(int j = i+1; j < arr.size(); j++) {
if(arr.get(i) + arr.get(j) == 0) {
log.info("[{}, {}]", arr.get(i), arr.get(j));
return;
}
}
}
}
가능한 중첩 loop
를 사용하는것은 좋지 않습니다.
순차적으로 탐색을 진행합니다.
j
를 증가시키면서 i의 값과 연산을 합니다.
j
가 끝에 도달하면 i
를 증가시키고 j
는 i
의 값에서 +1
을 합니다.
sumZero
시간 복잡도가 O(N)
으로 리팩토링된 알고리즘 입니다.sum
의 조건에 따라서 탐색방향을 변경 합니다.
void sumZero(List<Integer> arr) {
int left = 0;
int right = arr.size() - 1; // 배열의 마지막 위치
while (left < right) {
int sum = arr.get(left) + arr.get(right);
if(sum == 0) {
log.info("[{}, {}]", arr.get(left), arr.get(right));
return;
} else if(sum > 0) {
//sum > 0 이면 arr.get(right)가 더 크다는 의미
//현재 arr는 오름차순으로 정렬되었기 때문
right--;
} else {
left++;
}
}
}
하나의 loop
를 사용하고 sum
조건에 의해서 탐색 방향을 변경 합니다.
TEST
@Slf4j
public class MultiplePointers {
@Test
void test() {
simpleSumZero(List.of(-3, -2, -1, 0, 1, 2, 3));
sumZero(List.of(-3, -2, -1, 0, 1, 2, 4));
}
}
도전 과제
앞에서 배운 다중 포인터 패턴을 이용하여 고유값을 세는 함수를 작성해봅시다.
예시
[1, 1, 1, 2] -> 2
[1, 2, 2, 4, 4, 4, 5, 6] -> 5
[] -> 0
[-1, 0, 0, 1] -> 3
countUniqueValues
@Slf4j
public class CountUniqueValues {
@Test
void test() {
Assertions.assertThat(countUniqueValues(new int[]{1, 1, 1, 2})).isEqualTo(2);
Assertions.assertThat(countUniqueValues(new int[]{1, 2, 2, 4, 4, 4, 5, 6})).isEqualTo(5);
Assertions.assertThat(countUniqueValues(new int[]{})).isEqualTo(0);
Assertions.assertThat(countUniqueValues(new int[]{-1, 0, 0, 1})).isEqualTo(3);
}
int countUniqueValues(int[] arr) {
int i = 0;
for(int j = 1; j < arr.length; j++) {
if(arr[i] != arr[j]) {
i++;
arr[i] = arr[j];
}
}
if(arr.length == 0) return 0;
return i+1;
}
}
아래 그림과 같이 탐색을 합니다.
추가로 HashSet
을 사용하여 간편하게 코드를 작성할 수도 있습니다.
int countUniqueValuesBySet(int[] arr) {
Set<Integer> set = new HashSet<>();
// O(N)
for(int i = 0; i < arr.length; i++) {
set.add(arr[i]); // O(1)
}
return set.size();
}
정리
- 각 배열의
index
를 분리한다. - 조건에 맞게
index
를 조정하여 탐색 방향을 변경 한다.
#Study/java/algorithm
'0 + 알고리즘(Algorithm)' 카테고리의 다른 글
[알고리즘] 배열과 리스트(백준 11720, 1546) (0) | 2023.03.06 |
---|---|
[알고리즘] 재귀(Recursion -java) (0) | 2023.01.28 |
[알고리즘] 슬라이딩 윈도우 패턴(Sliding Window -java) (0) | 2023.01.27 |
[알고리즘] 빈도수 세기 패턴(Frequency Counters -java) (0) | 2023.01.22 |
[알고리즘] 빅오(Big-O) 표기법의 이해 -java (0) | 2023.01.21 |