[#14] Java 재귀 & 배열 & Exception

2025. 2. 13. 23:36·LG 유플러스 유레카 SW/Java

재귀

package recur;

public class Test {
	public static void main(String[] args) {
		int n = 10;
		
		printNums(n);
	}
	
	public static void printNums(int n) {
		if(n > 0) { // 기저 조건
			printNums(n-1);
			System.out.print(n +" ");
		}
	}
}

 

재귀를 사용하는 사례

  • 분할 정복 (Divide and Conquer): 문제를 더 작은 하위 문제로 나누어 해결하는 방식이 필요할 때. 
    • 퀵 정렬 (Quick Sort) 및 병합 정렬 (Merge Sort): 배열을 여러 부분으로 나누고 각각을 정렬한 후 다시 합치는 방식입니다.
  • 트리 및 그래프 탐색: 트리나 그래프 구조에서 깊이 우선 탐색(DFS)과 같은 탐색 알고리즘이 필요할 때. 예: 이진 트리의 순회 (전위, 중위, 후위 순회)
  • 순열, 조합, 부분집합 생성: 주어진 집합에서 가능한 모든 조합이나 순열을 생성해야 할 때. 예: 특정 숫자의 조합을 구하는 문제
  • 백트래킹 (Backtracking): 모든 가능한 경우를 탐색해야 하며, 특정 조건을 만족할 때 해결책을 찾는 경우.예: N-Queens 문제, 미로 찾기, 스도쿠 해결 등.
  • 피보나치 수열: 앞의 두 수의 합이 바로 뒤의 수가 되는 수의 배열. 
    예: F(n) = F(n-1) + F(n-2)로 정의되는 피보나치 수열.
  • 동적 프로그래밍: 재귀적 구조가 필요한 문제에서 메모이제이션 기법을 사용할 경우.예: 특정 문제를 재귀적으로 해결하면서 이전에 계산한 결과를 저장하여 성능을 개선하는 경우.

 

Factorial 예제

package recur;

public class FactorialTest {
	
	public static void main(String[] args) {
		int n = 5;
		int result = factorial(n);
		
		System.out.println(result);
	}
	
	public static int factorial(int n) {
		if(n == 0) return 1;
		return n * factorial(n-1);
	}
	

/* for 문 */
	
//	public static void main(String[] args) {
//		int n = 5;
//		int result = 1;
//		
//		for(int i = 1; i <= n; i++) {
//			result *= i;
//		}
//		
//		System.out.println(result);
//
//	}

}

 

재귀 장/단점

장점

  • 코드를 간결하게 작성할 수 있음
  • 복잡한 문제를 쉽게 해결할 수 있음

단점

  • 함수 호출 시마다 시스템 스택 메모리를 사용하므로 스택오버플로우가 발생될 수 있음
  • 성능이 비효율적, 반복문으로 대체할 수 있는지 고려

 

배열

1차원 배열 - 투포인터

투 포인터(Two Pointers) 기법은 주로 배열이나 리스트와 같은 선형 자료 구조에서 사용되는 알고리즘 기법. 이 방법은 두 개의 포인터를 사용하여 문제를 해결.

 

적용 예)

  • 합을 찾는 문제: 두 포인터를 사용하여 특정 합을 찾는 경우, 한 포인터는 시작 지점에서, 다른 포인터는 끝 지점에서 시작하여 두 포인터를 이동시키며 조건을 만족하는 경우를 찾음.
  • 중복 제거: 정렬된 배열에서 중복된 요소를 제거할 때, 하나의 포인터는 현재 위치를 가리키고 다른 포인터는 중복 여부를 확인하는 데 사용.
  • 슬라이딩 윈도우: 연속된 부분 배열의 합이나 최댓값을 찾는 문제에서 두 포인터를 사용하여 윈도우의 크기를 조절.

➡️ 이 기법은 시간 복잡도를 줄이고, 공간 복잡도도 절약할 수 있어 매우 효율적

/*
Q. 정렬된 배열 nums와 정수 target이 주어질 때, 
두 수의 합이 target이 되는 두 수의 인덱스를 반환하세요
*/

package array1;

public class TwoSum {

	public static void main(String[] args) {
		int[] nums = { 1, 2, 3, 4, 6 };
		int target = 6;
		
		int left = 0;
		int right = nums.length - 1;
		
		while(left < right) {
			int sum = nums[left] + nums[right];
			
			if( sum == target) {
				System.out.println(left + ":" + right);
				break;
			} else if(sum < target) {
				left++;
			} else {
				right--;
			}
			
		}

	}

}

 

슬라이딩 윈도우

슬라이딩 윈도우(Sliding Window) 기법은 주로 배열이나 리스트와 같은 선형 자료 구조에서 연속적인 부분 배열이나 부분 문자열을 처리하는 데 유용한 알고리즘 기법. 이 기법은 특정 범위를 효율적으로 탐색하고, 계산할 수 있음.
  • 슬라이딩 윈도우는 두 개의 포인터 또는 인덱스를 사용하여 데이터의 일부를 선택하고, 이 선택된 범위를 "윈도우"라고 부름. 이 윈도우는 데이터의 특정 부분을 나타내며, 필요에 따라 크기나 위치를 조정할 수 있음.

📍 주요 특징

  • 연속성: 윈도우 내의 요소들은 항상 연속적.
  • 효율성: 윈도우의 크기를 변경하면서 반복적으로 계산할 수 있어, 중복 계산을 피하고 시간 복잡도를 줄임.
  • 상태 유지: 각 윈도우의 상태를 유지하면서 필요한 계산을 수행할 수 있음.
  • 시간 복잡도 감소: O(n)
  • 간결한 코드: 복잡한 중첩 루프를 줄이고, 코드의 가독성을 높임.

 

📍 사용 예시

  • 최대/최소 합의 부분 배열 찾기: 연속된 K 개의 요소의 최대 합을 찾는 문제.
  • 문자열 문제: 주어진 문자열에서 특정 조건을 만족하는 부분 문자열의 길이나 개수를 찾는 문제.
  • 중복 제거: 특정 조건을 만족하는 부분 배열에서 중복 요소를 제거하는 문제.

 

📍 슬라이딩 윈도우의 유형

  • 고정 크기 윈도우: 윈도우의 크기가 고정되어 있는 경우 (예: K 개의 요소).
  • 가변 크기 윈도우: 윈도우의 크기가 동적으로 조정되는 경우 (예: 조건에 따라 요소를 추가하거나 제거).
/*
Q. 최대합이 되는 연속된 k개의 요소 찾기
*/

package array1;

public class SlidingWindow {

	public static void main(String[] args) {
		int[] nums =  { 1, 2, 3, 4, 5};
		int k = 3; // 연속된 요소의 개수
		
		int result = maxSum(nums, k);
		System.out.println(result);
		

	}
	
	public static int maxSum(int[] nums, int k) {
		int n = nums.length;
		
		if(n < k) throw new IllegalArgumentException("배열의 길이가 k보다 커야합니다.");
		
		int maxSum = 0;
		for (int i = 0; i < k; i++) {
			maxSum += nums[i];
		}
		
		int windowSum = maxSum;
		for (int i = k; i < n; i++) {
			windowSum += nums[i] - nums[i-k];
			maxSum = Math.max(maxSum, windowSum);
		}
		return maxSum;
	}

}

 

2차원 배열

행우선순회

/*
    1 2 3 
    4 5 6 
    7 8 9 
*/

package array2;

public class 행우선순회 {

	public static void main(String[] args) {
		int [][]array= {
				{1,2,3},
				{4,5,6},
				{7,8,9}
		};
		
		trave(array);

	}
	
	public static void trave(int [][] arr) {
		int rows=arr.length;
		
		for (int i = 0; i < rows; i++) {
			int cols=arr[i].length;
			
			for (int j = 0; j < cols; j++) {
				System.out.print(arr[i][j]+" ");
			}
			System.out.println();
		}
	}

}

열우선순회

/*
    1 4 7 
    2 5 8 
    3 6 9 
*/

package array2;

public class 열우선순회 {

	public static void main(String[] args) {
		int [][]array= {
				{1,2,3},
				{4,5,6},
				{7,8,9}
		};
		
		trave(array);

	}
	
	public static void trave(int [][] arr) {
		int rows=arr.length;
		int cols=arr[0].length;
		
		for (int j = 0; j < cols; j++) {
			for (int i = 0; i < rows; i++) {			
				System.out.print(arr[i][j]+" ");
			}
			System.out.println();
		}
	}

}

지그재그순회

/*
    1 2 3 
    6 5 4 
    7 8 9 	
*/

package array2;

public class 지그재그순회 {

	public static void main(String[] args) {
		int [][]array= {
				{1,2,3},
				{4,5,6},
				{7,8,9}
		};
		
		trave(array);

	}
	
	public static void trave(int [][] arr) {
		int rows=arr.length;
		int cols=arr[0].length;
		
		for (int i = 0; i < rows; i++) {
			if(i % 2 == 0) {
				for (int j = 0; j < cols; j++) {
					System.out.print(arr[i][j]+" ");
				}
			}else {
				for (int j = cols-1; j >=0 ; j--) {
					System.out.print(arr[i][j]+" ");
				}
			}
			
			
			System.out.println();
		}
	}

}

역지그재그순회

/*
    1 4 7 
    8 5 2 
    3 6 9 
*/

public class ReverseZigzagTraversal {
    public static void main(String[] args) {
        // 2차원 배열 정의 및 초기화
        int[][] array = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };

        // 역방향 지그재그 순회를 위한 메서드 호출
        traverseReverseZigzag(array);
    }

    // 2차원 배열을 역방향 지그재그 방식으로 순회하는 메서드
    public static void traverseReverseZigzag(int[][] array) {
        // 배열의 행 길이
        int rows = array.length;

        // 배열의 열 길이
        int cols = array[0].length;

        // 각 열을 순회
        for (int j = 0; j < cols; j++) {
            // 홀수 열일 때 (1, 3, ...)는 아래에서 위로 순회
            if (j % 2 == 0) {
                for (int i = 0; i < rows; i++) {
                    System.out.print(array[i][j] + " ");
                }
            } 
            // 짝수 열일 때 (0, 2, ...)는 위에서 아래로 순회
            else {
                for (int i = rows - 1; i >= 0; i--) {
                    System.out.print(array[i][j] + " ");
                }
            }
        }
    }
}

 

Exception

Exception 처리 기법

1️⃣ try~catch

package exception;

public class Test {

	public static void main(String[] args) {
		int x = 100;
		int y = 0;
		
		try {
			y = Integer.parseInt(args[0]);
			System.out.println(x / y);
		} catch(ArrayIndexOutOfBoundsException e) {
			System.out.println("args[0]을 제공해야 합니다.");
			y = 1;
		} catch(ArithmeticException e) {
			
		} catch(NumberFormatException e) {
			System.out.println("args[0]은 Integer 타입이어야 합니다.");
		} catch(Exception e) { // 최상위 Exception, 정교한 복구 불가능(보안에 나쁨)
			
		}
		
		System.out.println("중요한 일");
		
	}

}

2️⃣ throws

package exception;

public class Test {

	public static void main(String[] args) {
		int x = 100;
		int y = 0;
		
        y = Integer.parseInt(args[0]);

        int result;
        try {
            result = Divider.divide(x, y);
            System.out.println(result);
        } catch (ArithmeticException e) {
 
        }
		
		System.out.println("중요한 일");
		
	}

}

class Divider {
	public static int divide(int x, int y) throws Exception {
    	// ... Exception 처리
		return x / y;
		
	}
}

 

User define Exception

  • RuntimeException ➡️ Checked Exception
  • Exception Message 간단, 명확 전달
package exception;

public class Test {

	public static void main(String[] args) {
		int x = 100;
		int y = 0;

        y = Integer.parseInt(args[0]);

        int result;
        try {
            result = Divider.divide(x, y);
            System.out.println(result);
        } catch (MyException e) {
            System.out.println(e.getMessage());
        }
		
		System.out.println("중요한 일");
		
	}

}

class Divider {
	public static int divide(int x, int y) throws MyException {
		if(y == 0) {
			throw new MyException("0을 입력하지 마세요."); // 에러 메시지는 너무 자세하면 안 됨(보안 위험), 정제화
		}
		
		return x / y;
		
	}
}

class MyException extends Exception {

	public MyException(String message) {
		super(message);
		
	}
	
}
728x90
반응형

'LG 유플러스 유레카 SW > Java' 카테고리의 다른 글

[#13] Java Generic & Collection  (0) 2025.02.12
[#12] Java 코드 성능 & 가독성 높이는 법  (0) 2025.02.11
[#11] Java 다형성 + Class 다이어그램 그려보기  (0) 2025.02.10
[#10] Java 들어가기 + 알고리즘 스터디  (0) 2025.02.07
[#9] Java 들어가기 전에..  (0) 2025.02.06
'LG 유플러스 유레카 SW/Java' 카테고리의 다른 글
  • [#13] Java Generic & Collection
  • [#12] Java 코드 성능 & 가독성 높이는 법
  • [#11] Java 다형성 + Class 다이어그램 그려보기
  • [#10] Java 들어가기 + 알고리즘 스터디
nueos
nueos
  • nueos
    nueos 공부 기록
    nueos
  • 전체
    오늘
    어제
    • 분류 전체보기 (191)
      • 해커톤 (1)
      • 네이버 BoostCamp (6)
      • LG 유플러스 유레카 SW (3)
        • React (21)
        • TypeScript (2)
        • JavaScript (2)
        • HTML+CSS (5)
        • Spring (7)
        • Java (6)
        • SQL (2)
        • Algorithm (8)
        • CX (6)
        • Git (2)
        • 프로젝트 (2)
        • 스터디 (9)
        • 과제 (8)
        • 특강 (1)
      • React (3)
      • Next (0)
      • Javascript (2)
      • HTML (2)
      • CSS (9)
      • Algorithm (6)
      • Database (0)
      • OS (13)
      • C++ (24)
      • Python (1)
      • jQuery (1)
      • Django (1)
      • Git (1)
      • 개발 지식 (3)
      • 정보 보안 (22)
      • 포렌식 (1)
      • 암호 (2)
      • 기타 (4)
      • 패스트캠퍼스 FE 프로젝트십 (5)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    디지랩챌린지
    큐
    완전 탐색
    Queue
    스택
    exhaustive search
    제주지역혁신플랫폼지능형서비스사업단
    디지털혁신
    제주해커톤
    힙
    Stack
    기술로바꾸는세상
    heap
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
nueos
[#14] Java 재귀 & 배열 & Exception
상단으로

티스토리툴바