자료구조와 알고리즘을 배우기 시작했고, 아침마다 데일리코딩으로 문제를 풀고 있다.
나는 문제를 여러번 읽음에도 하나씩 놓치는 부분이 있어서 한번에 통과하는 경우는 적지만
풀리지않았을때 일상생활을 하면서도 계속 그 문제를 생각하는 터라 어려운 문제를 만났을 때 오히려 즐겁고, 요즘은 게임에서 이겼을때보다 문제를 풀었을때가 더 큰 성취와 기쁨이 느껴진다.
그동안 상상해왔던 개발자는 하루종일 타닥타닥 키보드를 쉴 새 없이 누르는 모습이였는데 코딩을 배우면 배울수록 새로운 코드를 짜는 시간보다 오류를 찾거나 문제해결을 위해 생각하는 시간이 훨씬 길다는 사실을 느끼며 이미지가 변하고 있다.
내가 원하는대로 동작하는 것은 기본이고, 예상치 못한 결과를 불어오는 상황도 미리 생각해서 예외 처리를 하고, 디버깅도 꼼꼼히 하는 개발자가 되어야겠다.
오늘의 데일리코딩 문제는 어떤수가 2의 제곱인지 아닌지를 알아내는 문제였는데, long타입이나 int타입인 경우 나누면 소수점을 버려서 정확한 값이 나오지 않는것을 확인하고 double형으로 변환하니 해결되었었다.
public boolean powerOfTwo(long num) {
boolean result = false;
if(num==1) return true;
double A = (double)num;
// 3나누기2는 1.5인데 1이 되어 통과될 수 있음
int i = 2;
while(i<=A){
A = A/2;
}
if(A==1) result = true;
return result;
}
// num = 36028797018963968L 결과값 : true
스택은 데이터를 일시적으로 쌓아놓은 자료구조이다.
스택은 데이터를 넣을 수 있는(top) 위치와 데이터를 빼낼 수 있는(top) 위치가 하나만 존재해서 입출력방향이 동일하고 한번에 하나의 데이터만 넣거나 꺼낼 수 있다. 가장 나중에 넣은 데이터가 가장 위에 위치하고, 가장 먼저 넣은 데이터가 가장 아래에 위치하므로 후입선출구조이다. 나는 프링글스통을 생각하니 단번에 이해되었다.
<스택에서 지원하는 메소드>
add/push(): 스택의 맨 위에 자료를 삽입합니다.
pop(): 스택의 맨 위에 있는 자료를 꺼내서 리턴합니다. (스택에서 삭제합니다.)
top/peek(): 스택의 맨 위에 있는 자료를 리턴합니다. (스택에서 삭제하지 않습니다.)
size(): 스택에 추가된 데이터의 크기를 리턴합니다.
스택을 사용하여 브라우저를 이용할 때 '뒤로 가기'나 '앞으로 가기'의 기능을 연습문제로 다루어보았는데 무척 재미있었다. 문제를 이해해보려고 그림까지 그려보았는데 덕분에 스택과 더 친해진 것 같다.
<조건>
- 새로운 페이지로 접속할 경우 prev 스택에 원래 있던 페이지를 넣고 next 스택을 비웁니다.
- 뒤로 가기 버튼을 누를 경우 원래 있던 페이지를 next 스택에 넣고 prev 스택의 top에 있는 페이지로 이동한 뒤 prev 스택의 값을 pop 합니다.
- 앞으로 가기 버튼을 누를 경우 원래 있던 페이지를 prev 스택에 넣고 next 스택의 top에 있는 페이지로 이동한 뒤 next 스택의 값을 pop 합니다.
- 브라우저에서 뒤로 가기, 앞으로 가기 버튼이 비활성화일 경우(클릭이 되지 않을 경우)에는 스택에 push 하지 않습니다.
- 새로운 페이지 접속은 알파벳 대문자로 표기합니다.
- 뒤로 가기 버튼을 누른 행동은 -1로 표기합니다.
- 앞으로 가기 버튼을 누른 행동은 1로 표기합니다.
- 반환되는 출력값 배열의 첫 번째 요소 prev 스택, 세 번째 요소 next 스택은 배열입니다. 스택을 사용자 정의한다면 출력에서는 배열로 변환해야 합니다.
public class Solution {
public ArrayList<Stack> browserStack(String[] actions, String start) {
Stack<String> prevStack = new Stack<>();
Stack<String> nextStack = new Stack<>();
Stack<String> current = new Stack<>();
ArrayList<Stack> result = new ArrayList<>();
current.add(start);
for (int i = 0; i <= (actions.length) - 1; i++) {
if (actions[i].equals("1") && !nextStack.empty()) { // 1이면
String next = nextStack.pop();
prevStack.push(current.pop());
current.push(next);
}
else if (actions[i].equals("-1") && !prevStack.empty()) { // -1이면
String prev = prevStack.pop();
nextStack.push(current.pop());
current.push(prev);
}else if(actions[i].equals("1") || actions[i].equals("-1")){
}else {
prevStack.push(current.pop());
current.push(actions[i]);
nextStack.clear();
}
}
result.add(prevStack);
result.add(current);
result.add(nextStack);
return result;
}
}
input :
String[] actions = new String[]{"B", "C", "-1", "D", "A", "-1", "1", "-1", "-1"};
String start = "A";
output : [A],[B],[A,D]
두번째 else if문의 {}안이 비어있지만 저 한줄을 통해서 if문에서 nextStack.empty 검사를 하지 않는 조건에 대해서 예외처리를 할 수 있다는 것을 페어분이 알려주었다. 실무에선 정답이 맞는지 아닌지 확인 할 수도 없으니 이런 부분을 더 많이 접하고 익숙해질 필요가 있는 것 같다.