<< 목차 >>
문제 설명
첫 번째 시도 코드
두 번째 시도 코드
나보다 빨리 푼 사람의 코드
원인 찾기
문제 해결 후 마지막 코드
문제 설명
문자열 s에는 공백으로 구분된 숫자들이 저장되어 있습니다. s에 나타나는 숫자 중 최소값과 최대값을 찾아 이를 "(최소값) (최대값)"형태의 문자열을 반환하는 함수, solution을 완성하세요.
예를들어 s가 "1 2 3 4"라면 "1 4"를 리턴하고, "-1 -2 -3 -4"라면 "-4 -1"을 리턴하면 됩니다.
첫 번째 시도 코드
s를 split 메서드를 사용하여 String배열로 만든 뒤, 각 원소의 최댓값과 최솟값을 for문, Math.min(), Math.max() 메서드를 이용하여 알아내였다. 마지막에는 String answer = ""에 concat을 해서 return하였다.
public String solution( String s ) {
String answer = "";
String[] sp = s.split( " " );
int min = Integer.parseInt( sp[0] );
int max = Integer.parseInt( sp[0] );
for ( int i = 0; i < sp.length; i++ ) {
min = Math.min( min, Integer.parseInt( sp[i] ) );
max = Math.max( max, Integer.parseInt( sp[i] ) );
}
answer = min + " " + max;
return answer;
}
생각보다 꽤 걸린다. 원래 프로그래머스 문제를 풀면 이렇게 걸리지 않는데..? 개선점을 찾아서 수정해보자. min() 메서드와 max() 메서드가 문제였을까? 조건문으로 비교하여 작은값과 큰 값을 대입하도록 코드를 수정해보자. 마지막의 String도 StringBuilder로 바꿔서 해보자.
두 번째 시도 코드
public String solution( String s ) {
StringBuilder sb = new StringBuilder();
String[] sp = s.split( " " );
int max = Integer.parseInt( sp[0] );
int min = Integer.parseInt( sp[0] );
for ( int i = 1; i < sp.length; i++ ) {
int ele = Integer.parseInt( sp[i] );
if ( min > ele ) { // 이 부분을 Math.max() 에서 직접 비교하여 넣어주게 바꿔주었다.
min = ele;
}
if ( max < ele ) {
max = ele;
}
}
sb.append( min + " " + max );
return sb.toString();
}
대체적으로 다 10ms 안으로 들어왔으나 비슷비슷한 것 같다. 다른 사람의 풀이를 보면서 공부해보려고 했는데, stream을 쓴 풀이가 있었다. 근데 그 풀이 댓글에 "이 코드가 가장 빠를 것 같네요"라는 댓글이 있었다. 나는 의아했다. stream을 쓰면 가독성이 좋고 코드를 짧게 끝낼 수는 있지만, 내가 for문으로 돌린 것보다 속도가 빠르다고? 그럴리가 없는데? 싶어서 그 사람의 코드로 문제를 풀어 보았다.
나보다 빨리 푼 사람의 코드
public String solution( String s ) {
String[] arrStr = s.split( " " );
int[] arrInt = new int[arrStr.length];
int i = 0;
for ( String part : arrStr ) {
arrInt[i] = Integer.parseInt( part );
i++;
}
StringBuffer sb = new StringBuffer();
sb.append( Arrays.stream( arrInt ).min().getAsInt() );
sb.append( " " );
sb.append( Arrays.stream( arrInt ).max().getAsInt() );
return sb.toString();
}
1ms?? 진짜 말도 안된다. 얼마 차이 안 나는 것도 아니고 거의 5배 이상 차이가 난다. 무엇이 문제일까?
원인 찾기
1. 시간복잡도
시간복잡도에서 차이가 나는가? 아니다. 내가 시간복잡도의 개념을 정확히 알지는 못하지만, String을 split을 이용하여 만든 배열의 length만큼 for문이 돌아가기 때문에 둘다 O(n).. 인것 같다. 오히려 stream을 이용하면서 저 사람이 복잡도가 높으면 더 높지 않을까..?
2. Integer.parseInt()
Integer.parseInt()가 문제인 것인가? 아니다. 나는 max와 min에 인덱스 0번의 값을 넣어주고 1번부터 돌렸고, 저 풀이에서도 향상된 for문이긴 하지만 결국 0번부터 끝번까지 index를 돌린 것이므로, 차이가 많이 나지는 않을 것이다.
3. 튜터님 찬스....
여기서 나는 결국 답을 구하지 못하고 튜터님께 여쭤보았다. 튜터님께서도 for문보다 stream이 성능이 낮은데 어떻게 저럴 수 있지 ? 하셨다. Input 값을 길게 하여 직접 코드코드마다 시간을 찍어 보시고 범인을 찾아 냈다..!!(<< 이렇게 범인을 찾으면 되는 거구나! 하고 튜터님의 코드를 보고 생각했다.) 범인은 for문도, Integer.parseInt()도 아닌 StringBuilder를 쓰는 방식이었다.
StringBuilder에 append를 할 때, min + " " + max 를 append했는데, 이 부분에서 String 객체를 concat 하는 부분이라 성능 저하가 일어난 것 같았다. 따로 따로 append를 해주자.
문제 해결 후 마지막 코드
public String solution( String s ) {
StringBuilder sb = new StringBuilder();
String[] sp = s.split( " " );
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for ( int i = 0; i < sp.length; i++ ) {
int ele = Integer.parseInt( sp[i] );
if ( min > ele ) {
min = ele;
}
if ( max < ele ) {
max = ele;
}
}
sb.append( min ); // 이 부분을 바꿔주었다.
sb.append( " " );
sb.append( max );
return sb.toString();
}
결과는... 두구두구두구두구 짠!!
그러췌~~ 요거줴~~~ 0ms 대를 기록했다!!!🎉🎉🎉
생각보다 String을 쓰게 되면 성능 저하가 많이 일어나게 된다는 것, StringBuilder append를 해 줄 때에는 플러스로 concat해서 넣어주면 느리다는 것!을 깨달았다..
for문은 stream문보다 빠른 게 맞았다..!!! 튜터님의 찬스를 썼으나, 누가 범인인지 알아내는 방식을 보고 나도 나중에 저렇게 하면 되겠구나를 알게 되어서 좋았다.
'STUDY > JAVA' 카테고리의 다른 글
[TIL] printStackTrace()는 왜 아래에 찍힐까? (0) | 2023.06.14 |
---|---|
[TIL] Getter와 Setter. 왜 쓸까? (1) | 2023.06.12 |
[TIL] I/O Study - ByteArrayInputStream 예제 01 (0) | 2023.06.08 |
[TIL] 잘못 알았던 재귀함수 (0) | 2023.06.05 |
[TIL] contain 메서드 공부하기 (0) | 2023.06.02 |