본문 바로가기
개발 & 기술

[Java] 별찍기 알고리즘 문제 풀기(다이아몬드 별찍기)

by chococake 2021. 8. 25.

별 찍기 알고리즘 문제를 풀기 시작했다. 생각보다 난이도가 엄청 높다. 그냥 간단하게 생각해서는 절대 풀리지 않을 문제들이다. 그렇기 때문에 복습 차원에서 과정을 정리하는 것이 아주 중요하다고 생각한다.

 

 

다음과 같이 별찍기로 다이아몬드 형태를 표현하려고 한다. 처음 접근했을 때에는 중간에 별을 찍고 양쪽에 빈칸을 넣어야 한다고 생각했다. 하지만 생각해보니 오른쪽에는 굳이 빈칸을 넣을 필요가 없다. 그냥 칸 이동만 할 경우 다음 줄로 자연스럽게 넘어가기 때문이다. 

 

 

 

<핵심>

 

전체적인 핵심은 이 부분이 한줄 한 줄 따로 있다고 생각하는 것이 아니라 모든 줄이 하나의 줄로 쭉 연결되어있다고 생각해야 한다. 그래서 중간중간 끊어줘서 행을 구분한다고 생각해야 한다. (줄줄이 소시지가 1번째 줄에는 1개, 2번째 줄에는 2개 이런 식으로 있지만 알고 보면 하나로 연결된 것처럼 말이다)

 

일단 먼저 이렇게 만드는게 목표다. 삼각형을 만든다는 것은 빈칸의 수는 점점 적어지고 별의 수는 점점 많아진다는 것이다. 여기서는 for문을 사용해서 구현했다.

 

먼저 전체 행이 다섯줄이므로 5번을 반복할 수 있도록 큰 틀에서 for문을 만들었다.

 

for(let i = 1; i<=5; i++) {
.
.
.
}

틀을 이렇게 짠 다음에 안에 빈칸의 수와 별의 수만큼 입력해야 한다. 빈칸의 수는 갈수록 적어지기 때문에 for문에서 숫자가 점점 적어지는 형태로 구현했다.

 

for(let i = 1; i<=5; i++) {
            for(let j = 5; j>=i; j--) {
                sum += " "
            }
            .
            .
            .
}

이 코드의 해석은 다음과 같다. i가 1일 경우 j는 5,4,3,2,1 총 다섯 번을 돈다. 그래서 빈칸이 다섯 번 추가된다.

 

 

그다음에 별을 개수만큼 넣어야 한다. 별은 1개, 3개, 5개처럼 홀수로 증가한다. 그렇기 때문에 홀수를 일반화할 수 있는 식인 2n-1을 이용한다.

 

for(let i = 1; i<=5; i++) {
            for(let j = 5; j>=i; j--) {
                sum += " "
            }
            for(let k = 1; k<=(2*i)-1; k++) {
                sum += "*"
            }
            sum += "\n"
}

먼저 별은 1개가 들어가고 그다음은 행이 바뀔 때마다 홀수개로 늘어난다. 홀수는 (2*i)-1로 구현했다. 그리고 for문을 빠져나가면 한 행이 완성되었기 때문에 경계선으로 구분을 해야 한다. 즉, 행이 바뀌어야 한다는 것이다. 그렇기 때문에 \n으로 줄 바꿈을 했다.

 

 

두 번째는 다음과 같이 구현하면 된다.

 

역삼각형을 만들면 되는데 다이아몬드 중간 부분은 겹치지 않도록 그 아래부터 4행만 구현하면 된다. 사실 이 부분은 위에 삼각형과 반대로 하면 된다. 하지만 언뜻보기에는 쉬워 보이는 이 구현이 생각보다 어렵다.

 

여기서는 빈칸은 늘어나고 별의 개수는 점점 줄어드는 형태로 구현하면 된다. 아까처럼 for문 안에 for문 두 개를 입력해서 구현한다.

 

for(let l = 5; l>1; l--) {
.
.
.
.
}

먼저 이렇게 4행을 추가할 수 있도록 전체적인 틀을 짠다. 아까와 다르게 점차 수가 줄어들게 한 이유는 별의 빈칸의 개수를 점차 늘려갈 때 수로 맞춰주기 위해서이다. (이 부분을 생각하는 것이 아주 어려웠다.)

 

 

그다음에 빈칸을 먼저 추가할 수 있는 for문을 안에 넣는다. 

 

for(let l = 5; l>1; l--) {
            for(let m = 7; m>l; m--) { // 76, 765, 7654,76543
                sum += " "
            }
            
            .
            .
            .
}

여기서 숫자를 정하는 것이 좀 헷갈렸다. 숫자의 범위를 얼마나 지정해야 내가 원하는 만큼 빈칸을 줄 수 있느냐가 관건이었다. 만약 l이 5일 경우 m은 7, 6으로 for문을 두 번 돌기 때문에 빈칸을 두 번 준다. 그다음에 l이 4가 되면 m은 7, 6, 5로 세 번 돌아서 빈칸을 세 번 준다. 

이렇게 빈칸의 수를 점점 늘려나갈 수 있었다. 

 

그 다음에 별을 개수만큼 더한다. 별의 개수는 점차 줄어든다.

 

for(let l = 5; l>1; l--) {
            for(let m = 7; m>l; m--) { // 76, 765, 7654,76543
                sum += " "
            }
            for(let n = 2; n<(2*l)-1; n++) {
                sum += "*"
            }
            sum += "\n"
        }

여기서도 별의 개수는 7, 5, 3, 1개로 홀수 단위로 줄어든다. 그렇기 때문에 아까와 동일하게 (2*l)-1로 구현했다. 또한 l이 점점 줄어들기 때문에 그에 맞게 별의 개수를 줄일 수 있도록 했다. l이 5일 때 n은 2,3,4,5,6,7,8로 총 7번을 돈다. l이 4일 경우 n은 2,3,4,5,6으로 총 5번 돈다. 결국 별의 개수가 7, 5, 3, 1로 홀수로 줄어들게 된다.

 

 

전체 코드는 다음과 같다.

 

<script>
        
        let sum = ""

        for(let i = 1; i<=5; i++) {
            for(let j = 5; j>=i; j--) {
                sum += " "
            }
            for(let k = 1; k<=(2*i)-1; k++) {
                sum += "*"
            }
            sum += "\n"
        }

        for(let l = 5; l>1; l--) {
            for(let m = 7; m>l; m--) { // 76, 765, 7654,76543
                sum += " "
            }
            for(let n = 2; n<(2*l)-1; n++) {
                sum += "*"
            }
            sum += "\n"
        }
        
        console.log(sum)
    </script>

 

댓글