가능한 모든 경우의 수를 확인하여 문제를 해결하는 방법입니다.
장점
단점
현재 상황에서 가장 좋아 보이는 선택을 하는 방법입니다.
장점
단점
효율적인 데이터 관리
메모리 효율성
기본자료 구조
[10, 20, 30, 40][10] → [20] → [30] → [40]탄알집대기줄응용 자료구조
파일 탐색기, 가계도지하철 노선도, 소셜 네트워크사전, 단어집순차 탐색
public class SequentialSearch {
public static int sequentialSearch(int[] arr, int target) {
// 1. 배열의 첫 번째 요소부터 시작
// 1-1. 배열을 순차적으로 탐색하기 위한 반복문 시작
for (int i = 0; i < arr.length; i++) {
// 1-2. 현재 인덱스의 요소에 접근
// 2. 찾고자 하는 값(target)과 현재 요소 비교
// 2-1. target과 현재 요소가 일치하면, 현재 인덱스 반환
if (arr[i] == target) {
return i;
}
// 2-2. 일치하지 않으면, 다음 인덱스로 이동하여 1-2부터 반복
}
// 3. 배열 탐색 완료 후 결과 처리
// 3-1. 배열 끝까지 탐색했는데 찾지 못한 경우 실패(-1) 반환
return -1;
}
public static void main(String[] args) {
// 테스트를 위한 예제 배열과 찾을 값 설정
int[] arr = {4, 2, 7, 1, 9, 3};
int target = 7;
// 순차 탐색 수행 및 결과 출력
int result = sequentialSearch(arr, target);
if (result != -1) {
System.out.println("찾은 위치: " + result);
} else {
System.out.println("값을 찾지 못했습니다.");
}
}
}
이진 탐색
public class Solution {
private static int binarySearch(int[] arr, int target) {
// 1. 탐색 범위의 시작점과 끝점 설정
// 1-1. left는 배열의 첫 번째 인덱스로 설정
int left = 0;
// 1-2. right는 배열의 마지막 인덱스로 설정
int right = arr.length - 1;
// 2. 탐색 범위가 유효한 동안 반복
while (left <= right) {
// 2-1. 중간 위치 계산
int mid = (left + right) / 2;
// 3. 중간 값과 target 비교 후 처리
// 3-1. 중간 값이 target과 같으면 해당 인덱스 반환
if (arr[mid] == target) {
return mid;
}
// 3-2. 중간 값이 target보다 크면 오른쪽 범위를 줄임
else if (arr[mid] > target) {
right = mid - 1;
}
// 3-3. 중간 값이 target보다 작으면 왼쪽 범위를 줄임
else {
left = mid + 1;
}
}
// 4. 탐색 완료 후 결과 처리
// 4-1. 찾지 못한 경우 실패(-1) 반환
return -1;
}
public static void main(String[] args) {
// 테스트를 위한 정렬된 예제 배열과 찾을 값 설정
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; //정렬된 배열이어야 함
int target = 7;
// 이진 탐색 수행 및 결과 출력
int result = binarySearch(arr, target);
if (result != -1) {
System.out.println("찾은 위치: " + result);
} else {
System.out.println("값을 찾지 못했습니다.");
}
}
}
투 포인터
value 이상이 되는 최소 길이의 부분 배열을 구하거나, 두 수를 선택해 목표 합을 만들 수 있는지 판단하는 문제에 적용할 수 있습니다.public class Solution {
public static boolean findTwoSum(int[] arr, int target) {
// 1. 포인터 초기화
// 1-1. left 포인터는 배열의 첫 번째 인덱스로 설정
int left = 0;
// 1-2. right 포인터는 배열의 마지막 인덱스로 설정
int right = arr.length - 1;
// 2. 두 포인터가 교차하기 전까지 반복
while (left < right) {
// 2-1. 현재 두 포인터가 가리키는 값의 합 계산
int sum = arr[left] + arr[right];
// 2-2. 합과 목표값 비교
// 3. 합과 목표값 비교 후 포인터 이동
// 3-1. 합이 목표값과 같으면 true 반환 (쌍을 찾음)
if (sum == target) {
System.out.println("찾은 두 수: " + arr[left] + ", " + arr[right]);
return true;
}
// 3-2. 합이 목표값보다 작으면 left 포인터를 오른쪽으로 이동 (더 큰 값 필요)
else if (sum < target) {
left++;
}
// 3-3. 합이 목표값보다 크면 right 포인터를 왼쪽으로 이동 (더 작은 값 필요)
else {
right--;
}
}
// 4. 탐색 완료 후 결과 처리
// 4-1. 목표값을 만드는 쌍을 찾지 못한 경우 false 반환
return false;
}
public static void main(String[] args) {
// 테스트를 위한 예제 배열과 목표 합 설정
int[] arr = {4, 1, 8, 7, 3, 2}; // 정렬되지 않은 배열
int target = 10; // 찾고자 하는 합
// 투 포인터 탐색 수행 및 결과 출력
System.out.println("원본 배열: " + Arrays.toString(arr));
System.out.println("목표 합: " + target);
boolean result = findTwoSum(arr, target);
System.out.println("합이 " + target + "이 되는 두 수가 " +
(result ? "존재합니다." : "존재하지 않습니다."));
}
}
import java.util.Arrays;
public class Solution {
public static int findExactSum(int[] arr, int target) {
// 1. 포인터와 현재 합계, 결과값 초기화
// 1-1. start 포인터는 배열의 첫 번째 인덱스로 설정
int start = 0;
// 1-2. end 포인터는 배열의 두 번째 인덱스로 설정
int end = 1;
// 1-3. 현재까지의 합을 첫 번째 원소로 초기화
// - 초기 구간은 첫 번째 원소만을 포함
// - start 포인터가 가리키는 원소부터 end-1까지의 합
int currentSum = arr[0];
// 1-4. 찾은 구간의 개수를 0으로 초기화
// - target과 정확히 일치하는 구간을 찾을 때마다 증가
int count = 0;
// 2. 구간 탐색
// 배열의 모든 구간을 확인할 때까지 반복
while (start < arr.length) {
// 2-1. 현재 구간의 합이 정확히 target과 같은 경우
if (currentSum == target) {
// target과 일치하는 구간을 찾았으므로 count 증가
count++;
// end가 배열 끝에 도달하지 않은 경우
if (end < arr.length) {
// 구간을 확장하기 위해 end 위치의 원소를 더하고 end 증가
currentSum += arr[end++];
}
// end가 배열 끝에 도달한 경우
else {
// 더 이상 구간을 확장할 수 없으므로 start를 이동
currentSum -= arr[start++];
}
}
// 2-2. end가 배열 끝에 도달하지 않았고, 현재 합이 target보다 작은 경우
else if (end < arr.length && currentSum < target) {
// 구간의 합을 늘리기 위해 end 위치의 원소를 더하고 end 증가
currentSum += arr[end++];
}
// 2-3. end가 배열 끝에 도달했거나, 현재 합이 target보다 큰 경우
else {
// 구간의 합을 줄이기 위해 start 위치의 원소를 빼고 start 증가
currentSum -= arr[start++];
}
}
// 3. 탐색 완료 후 결과 처리
// 3-1. 찾은 모든 구간의 개수 반환
return count;
}
public static void main(String[] args) {
// 테스트를 위한 예제 배열과 목표 합 설정
int[] arr = {1, 2, 3, 1, 2}; // 예시 배열
int target = 3; // 목표값
// 입력 데이터 출력
System.out.println("원본 배열: " + Arrays.toString(arr));
System.out.println("목표 합: " + target);
// 결과 계산 및 출력
int result = findExactSum(arr, target);
System.out.println("합이 정확히 " + target + "인 연속된 구간의 개수: " + result);
// 모든 가능한 구간 출력 (설명을 위한 추가 코드)
System.out.println("\n만족하는 구간들:");
for (int i = 0; i < arr.length; i++) {
int sum = 0;
for (int j = i; j < arr.length; j++) {
sum += arr[j];
if (sum == target) {
System.out.println(Arrays.toString(Arrays.copyOfRange(arr, i, j + 1))
+ " (합: " + sum + ")");
}
}
}
}
}
버블 정렬
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int n = arr.length;
// 1. 외부 반복문: 정렬 과정(패스)을 몇 번 반복할지를 결정
// - 배열의 크기만큼 반복하며, 한 번 반복할 때마다 하나의 숫자가 최종 위치를 찾음
for (int i = 0; i < n - 1; i++) {
// 2. 내부 반복문: 실제 비교와 교환이 일어나는 과정
// - 이웃한 두 숫자를 비교
// - 범위가 점점 줄어듦 (n-i-1): 이미 정렬된 요소는 제외
for (int j = 0; j < n - i - 1; j++) {
// 3. 인접한 두 요소 비교 및 교환
// - 왼쪽 숫자가 오른쪽 숫자보다 크면 두 숫자의 위치를 바꿈
if (arr[j] > arr[j + 1]) {
// 이웃한 두 수 자리 교환
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 배열 출력을 위한 유틸리티 메서드
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
public static void main(String[] args) {
// 예시 배열 [5, 3, 8, 2]로 테스트
int[] arr = {5, 3, 8, 2};
System.out.println("정렬 전 배열:");
printArray(arr);
// 버블 정렬 수행
bubbleSort(arr);
System.out.println("정렬 후 배열:");
printArray(arr);
}
}
계수 정렬
public class CountingSort {
public static void countingSort(int[] arr) {
// 1. 최댓값 찾기 - O(N)
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
// 2. 카운팅 배열 생성 및 초기화
int[] counts = new int[max + 1];
// 3. 숫자 세기 - O(N)
for (int num : arr) {
counts[num]++;
}
// 4. 결과 배열 생성
int index = 0;
for (int i = 0; i <= max; i++) {
while (counts[i] > 0) {
arr[index] = i;
index++;
counts[i]--;
}
}
}
// 배열 출력을 위한 유틸리티 메서드
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
public static void main(String[] args) {
// 예시 배열 [4, 1, 3, 1, 2]로 테스트
int[] arr = {4, 1, 3, 1, 2};
System.out.println("정렬 전 배열:");
printArray(arr);
// 계수 정렬 수행
countingSort(arr);
System.out.println("정렬 후 배열:");
printArray(arr);
}
}
안정 계수 정렬
public class StableCountingSort {
public static int[] stableCountingSort(int[] arr) {
// 1. 카운팅 배열 생성
// 원본 배열의 최댓값 찾기
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
// 원본 배열의 최댓값 크기만큼의 카운팅 배열을 생성
int[] counts = new int[max + 1];
// 원본 배열을 순회하면서 각 숫자가 몇 번 등장했는지 카운팅 배열에 기록
for (int num : arr) {
counts[num]++;
}
// 2. 누적 합 배열 생성
// 카운팅 배열과 같은 크기의 누적 합 배열을 생성
int[] cumulative = new int[max + 1];
// 카운팅 배열을 순회하면서 각 위치까지의 누적 합을 계산
cumulative[0] = counts[0];
for (int i = 1; i <= max; i++) {
cumulative[i] = cumulative[i - 1] + counts[i];
}
// 3. 결과 배열 생성
// 결과 배열을 원본 배열과 같은 크기로 생성
int[] result = new int[arr.length];
// 원본 배열을 뒤에서부터 순회
for (int i = arr.length - 1; i >= 0; i--) {
int num = arr[i];
// 현재 숫자를 num이라 할 때, (cumulative[num] - 1)을 인덱스로 하여 결과 배열에 num을 저장
result[cumulative[num] - 1] = num;
// cumulative[num]의 값을 1 감소
cumulative[num]--;
}
return result;
}
// 배열 출력을 위한 유틸리티 메서드
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
public static void main(String[] args) {
// 예시 배열 [4, 1, 3, 1, 2]로 테스트
int[] arr = {4, 1, 3, 1, 2};
System.out.println("정렬 전 배열:");
printArray(arr);
// 안정 계수 정렬 수행하고 결과 받기
int[] sortedArr = stableCountingSort(arr);
System.out.println("정렬 후 배열:");
printArray(sortedArr);
}
}
행과 열 두 차원을 가진 배열입니다.

요소 접근
크기
연속된 메모리 공간
// 2차원 배열 선언과 초기화
int[][] matrix = new int[3][4]; // 3행 4열의 2차원 배열 생성
// 초기 값을 바로 넣어 3행 3열 배열 생성
int[][] scores = {
{90, 85, 95},
{75, 80, 85},
{88, 92, 96}
};
// 2차원 배열 요소 접근하기
matrix[0][1] = 10; // 1행 2열 위치에 10을 저장
int score = scores[1][2]; // scores[1][2]는 2행 3열 값, 즉 85
// 중첩 for문을 이용한 2차원 배열 순회
for (int i = 0; i < scores.length; i++) {
for (int j = 0; j < scores[i].length; j++) {
System.out.print(scores[i][j] + " ");
}
System.out.println(); // 각 행 출력 후 줄바꿈
}
// for-each문을 이용한 2차원 배열 순회
for (int[] row : scores) { // scores 배열에서 한 행씩 꺼내기
for (int value : row) { // 해당 행에서 각 값 꺼내기
System.out.print(value + " ");
}
System.out.println();
}
행렬 순회 방법

public class MatrixTraversal {
public static void rowMajorOrder(int[][] arr) {
int rows = arr.length;
int cols = arr[0].length;
// 1. 행 반복문(바깥쪽)과 열 반복문(안쪽) 설정
// 1-1. 행 인덱스 i는 0부터 행의 크기-1까지 반복
for (int i = 0; i < rows; i++) {
// 1-2. 열 인덱스 j는 0부터 열의 크기-1까지 반복
for (int j = 0; j < cols; j++) {
// 2. 현재 위치(i,j)의 원소에 접근
// 2-1. arr[i][j] 형태로 각 원소에 접근
System.out.print(arr[i][j] + " ");
}
// 3. 다음 행으로 이동하여 반복
// 3-1. 현재 행의 순회가 끝나면 다음 행으로 이동
System.out.println();
}
}
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("행 우선 순회 결과:");
rowMajorOrder(matrix);
}
}
public class MatrixTraversal {
public static void columnMajorOrder(int[][] arr) {
int rows = arr.length;
int cols = arr[0].length;
// 1. 열 반복문(바깥쪽)과 행 반복문(안쪽) 설정
// 1-1. 열 인덱스 j는 0부터 열의 크기-1까지 반복
for (int j = 0; j < cols; j++) {
// 1-2. 행 인덱스 i는 0부터 행의 크기-1까지 반복
for (int i = 0; i < rows; i++) {
// 2. 현재 위치(i,j)의 원소에 접근
// 2-1. arr[i][j] 형태로 각 원소에 접근
// 2-2. 각 열을 위에서 아래로 순회
System.out.print(arr[i][j] + " ");
}
// 3. 다음 열로 이동하여 반복
// 3-1. 현재 열의 순회가 끝나면 다음 열로 이동
System.out.println();
}
}
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("열 우선 순회 결과:");
columnMajorOrder(matrix);
}
}
public class MatrixTraversal {
public static void zigzagOrder(int[][] arr) {
int rows = arr.length;
int cols = arr[0].length;
// 1. 행 인덱스에 따른 순회 방향 결정
for (int i = 0; i < rows; i++) {
// 1-1. 행 인덱스(i)가 짝수인 경우: 왼쪽에서 오른쪽으로 순회
if (i % 2 == 0) {
// 2-1. 짝수 행: j를 0부터 열의 크기-1까지 증가
for (int j = 0; j < cols; j++) {
System.out.print(arr[i][j] + " ");
}
}
// 1-2. 행 인덱스(i)가 홀수인 경우: 오른쪽에서 왼쪽으로 순회
else {
// 2-2. 홀수 행: j를 열의 크기-1부터 0까지 감소
for (int j = cols - 1; j >= 0; j--) {
System.out.print(arr[i][j] + " ");
}
}
// 3. 다음 행으로 이동하여 반복
// 3-1. 현재 행의 순회가 끝나면 다음 행으로 이동
System.out.println();
}
}
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("지그재그 순회 결과:");
zigzagOrder(matrix);
}
}
아래와 같은 5x5 격자가 있다고 가정하겠습니다:
0 1 2 3 4
0[1] [2] [3] [4] [5]
1[6] [7] [8] [9] [10]
2[11][12][13][14][15]
3[16][17][18][19][20]
4[21][22][23][24][25]
현재 위치: (2,3) → 값 14가 있는 칸
상하좌우를 통해 확인할 칸:
- 상: (1,3) 값 9
- 하: (3,3) 값 19
- 좌: (2,2) 값 13
- 우: (2,4) 값 15
1. 델타 배열 정의하기
- 2차원 격자에서 상하좌우 인접 칸을 확인하고 싶다면, 이동 방향은 아래와 같습니다.
- 상(Up): 행 -1, 열 0
- 하(Down): 행 +1, 열 0
- 좌(Left): 행 0, 열 -1
- 우(Right): 행 0, 열 +1
- 이를 배열 형태로 표현해보면 다음과 같습니다.
dx = [0, 0, -1, 1] // 열 변화량: 상(0), 하(0), 좌(-1), 우(1)
dy = [-1, 1, 0, 0] // 행 변화량: 상(-1), 하(1), 좌(0), 우(0)
dx, dy의 인덱스 i에 따라 방향이 결정됩니다.
i=0일 때 상
i=1일 때 하
i=2일 때 좌
i=3일 때 우
2. 현재 위치에서 시작하기
- 현재 탐색하고 싶은 위치를 (row, col)이라 하겠습니다.
3. 델타를 이용한 인접 위치 탐색
- 상하좌우 각각에 대해 새로운 좌표를 계산합니다.
- i=0 (상) 방향일 때:
```
newRow = row + dy[0] = 2 + (-1) = 1
newCol = col + dx[0] = 3 + (0) = 3
```
이때 (1, 3)이 행렬 범위 내인지 확인합니다.
범위 내의 값이라면 필요한 작업을 수행할 수 있습니다.
- i=1 (하) 방향일 때:
```
newRow = 2 + dy[1] = 2 + 1 = 3
newCol = 3 + dx[1] = 3 + 0 = 3
```
새 좌표는 (3,3). 범위 내인지 확인 후 처리합니다.
- i=2 (좌) 방향일 때:
```
newRow = 2 + dy[2] = 2 + 0 = 2
newCol = 3 + dx[2] = 3 + (-1) = 2
```
새 좌표는 (2,2). 범위 내인지 확인 후 처리합니다.
- i=3 (우) 방향일 때:
```
newRow = 2 + dy[3] = 2 + 0 = 2
newCol = 3 + dx[3] = 3 + 1 = 4
```
새 좌표는 (2,4). 범위 내인지 확인 후 처리합니다.
행렬의 회전
// 90도 회전 (직사각형 행렬)
public static int[][] rotate90(int[][] matrix) {
int N = matrix.length; // 행의 개수
int M = matrix[0].length; // 열의 개수
int[][] result = new int[M][N]; // 결과 배열의 크기가 바뀜
for(int i = 0; i < N; i++) {
for(int j = 0; j < M; j++) {
result[j][N-1-i] = matrix[i][j];
}
}
return result;
}
// 180도 회전 (직사각형 행렬)
public static int[][] rotate180(int[][] matrix) {
int N = matrix.length;
int M = matrix[0].length;
int[][] result = new int[N][M]; // 원래 크기 유지
for(int i = 0; i < N; i++) {
for(int j = 0; j < M; j++) {
result[N-1-i][M-1-j] = matrix[i][j];
}
}
return result;
}
// 270도 회전 (직사각형 행렬)
public static int[][] rotate270(int[][] matrix) {
int N = matrix.length;
int M = matrix[0].length;
int[][] result = new int[M][N]; // 결과 배열의 크기가 바뀜
for(int i = 0; i < N; i++) {
for(int j = 0; j < M; j++) {
result[M-1-j][i] = matrix[i][j];
}
}
return result;
}