문제 : 이진트리 순회(깊이우선탐색)
문제 설명
아래 그림과 같은 이진트리를 전위순회와 후위순회를 연습해보세요.
전위순회 출력 : 1 2 4 5 3 6 7
중위순회 출력 : 4 2 5 1 6 3 7
후위순회 출력 : 4 5 2 6 7 3 1
내코드
function solution(n, end) {
let preorderAnswer = "";
let inorderAnswer = "";
let postorderAnswer = "";
// 트리 생성부분
let tree = [];
let current = 1; // 현재 입력될 숫자를 의미
// n 부터 end 만큼 반복하면서 이진트리 생성 num 현재 노드를 의미
// tree 배열의 주소 index 와 노드의 값이 같아 index 값이 즉 index 값을 가진 노드를 의미한다.
for (let num = n; num <= end; num++) {
tree.push({
root: num,
l: current < end ? ++current : null,
r: current < end ? ++current : null,
});
}
console.log(tree);
// 전위순회
function preorder(obj) {
// 현재 노드의 값을 정답 문자열에 추가
preorderAnswer += obj.root + " ";
console.log("# root", obj);
// 왼쪽 노드가 있다면 왼쪽노드를 활용해 재귀적으로 함수 호출
obj.l && preorder(tree[obj.l - 1]);
// 오른쪽 노드가 있다면 오른쪽노드를 활용해 재귀적으로 함수 호출
obj.r && preorder(tree[obj.r - 1]);
}
// 중위순회
function inorder(obj) {
console.log("# root", obj);
// 왼쪽 노드가 있다면 왼쪽노드를 활용해 재귀적으로 함수 호출
obj.l && inorder(tree[obj.l - 1]);
// 현재 노드의 값을 정답 문자열에 추가
inorderAnswer += obj.root + " ";
// 오른쪽 노드가 있다면 오른쪽노드를 활용해 재귀적으로 함수 호출
obj.r && inorder(tree[obj.r - 1]);
}
// 후위순회
function postorder(obj) {
console.log("# root", obj);
// 왼쪽 노드가 있다면 왼쪽노드를 활용해 재귀적으로 함수 호출
obj.l && postorder(tree[obj.l - 1]);
// 오른쪽 노드가 있다면 오른쪽노드를 활용해 재귀적으로 함수 호출
obj.r && postorder(tree[obj.r - 1]);
// 현재 노드의 값을 정답 문자열에 추가
postorderAnswer += obj.root + " ";
}
preorder(tree[0]);
console.log("전위순회 결과 출력 : " + preorderAnswer);
inorder(tree[0]);
console.log("중위순회 결과 출력 : " + inorderAnswer);
postorder(tree[0]);
console.log("후위순회 결과 출력 : " + postorderAnswer);
}
solution(1, 7);
풀이
- 그림과 같은 트리를 구현하기 위해
tree
배열을 생성하고 현재 입력될 숫자를 의미할current
변수를 1 로 초기화 n
부터end
만큼 반복하면서 객체 형식의 노드를 생성해 노드의 값은num
이며 좌측 자식 노드는++current
, 우측 자식 노드를 한번 더++current
값을 지정 (tree
배열의 주소index + 1
와 노드의 값이 같아index + 1
값을 가진 노드를 의미한다.)- 순회 방식을 재귀 함수로 구현
전위 순회 :
1. 현재 노드의 값을 정답 문자열에 추가
2. 왼쪽 노드가 있다면 왼쪽 노드를 재귀적으로 함수 호출
3. 오른쪽 노드가 있다면 오른쪽 노드를 재귀적으로 함수 호출
중위 순회 :
1. 왼쪽 노드가 있다면 왼쪽 노드를 재귀적으로 함수 호출
2. 현재 노드의 값을 정답 문자열에 추가
3. 오른쪽 노드가 있다면 오른쪽 노드를 재귀적으로 함수 호출
후위 순회 :
1. 왼쪽 노드가 있다면 왼쪽 노드를 재귀적으로 함수 호출
2. 오른쪽 노드가 있다면 오른쪽 노드를 재귀적으로 함수 호출
3. 현재 노드의 값을 정답 문자열에 추가
오답 노트
// 초창기 코드
function solution(n, end) {
let answer = "";
// 0번째는 헤더를 가지는 트리 생성(연결리스트 head 느낌)
let tree = [{ head: 0, l: 1 }];
let current = 1; // 현재 입력될 숫자를 의미
// n 부터 end 만큼 반복하면서 이진트리 생성 num 현재 노드를 의미
// tree 배열의 주소 index 와 노드의 값이 같아 index 값이 즉 index 값을 가진 노드를 의미한다.
for (let num = n; current < end; num++) {
tree.push({
root: num,
l: current < end ? ++current : null,
r: current < end ? ++current : null,
});
}
// 전위 순회
function front(obj) {
// 노드가 채워지 있지 않은 경우 최상위 노드 1 정답 문자열에 추가
if (obj.head) answer += obj.l;
console.log("# root", obj);
// 왼쪽 노드를 정답 문자열에 추가
answer += obj.l ? obj.l + " " : "";
// 왼쪽 노드 존재한다면 왼쪽노드를 활용해 재귀적으로 함수 호출
if (tree[obj.l]) {
console.log("# l", tree[obj.l]);
front(tree[obj.l]);
}
// 오른쪽 노드를 정답 문자열에 추가
answer += obj.r ? obj.r + " " : "";
// 오른쪽 노드 존재한다면 왼쪽노드를 활용해 재귀적으로 함수 호출
if (tree[obj.r]) {
console.log("# r", tree[obj.r]);
front(tree[obj.r]);
}
}
// 후위 순회
function back(obj) {
// 노드가 채워지 있지 않은 경우 최상위 노드 1 정답 문자열에 추가
if (obj.head) answer += obj.l;
console.log("# root", obj);
// 왼쪽 노드를 정답 문자열에 추가
if (tree[obj.l]) {
console.log("# l", tree[obj.l]);
back(tree[obj.l]);
}
// 왼쪽 노드 존재한다면 왼쪽노드를 활용해 재귀적으로 함수 호출
answer += obj.l ? obj.l + " " : "";
// 오른쪽 노드 존재한다면 왼쪽노드를 활용해 재귀적으로 함수 호출
if (tree[obj.r]) {
console.log("# r", tree[obj.r]);
back(tree[obj.r]);
}
// 오른쪽 노드를 정답 문자열에 추가
answer += obj.r ? obj.r + " " : "";
}
console.log(tree);
front(tree[0]);
// back(tree[0]);
return answer;
}
console.log(solution(1, 7));
트리를 구성하는 반복문이 current < end
까지 진행되므로 실질적으로 맨 마지막 노드 4,5,6,7 은 노드로 구성되지 않는다 즉 2와 3의 자식 노드로만 존재하게 된 상황이다.
이후 순회시에 마지막 노드들 4,5,6,7 노드를 방문하지 않고 값을 정답 문자열 집어넣기 위해 그 부모 노드에서 자식노드를 집어넣는 방식을 택하다보니 중위순회를 구현하기에도 어렵고(위와 같은 로직으로 구현하는데 실패하고 전위, 후위 순회만 구현함) head 노드를 따로 만들어 최상위 노드 1 을 집어 넣는 if 문으로 제작하는 등 만드는 동안에도 이상하다고 느낌
다른 코드를 참고해보고 생각해보니 결국 자식 노드의 값을 정답 문자열에 집어넣고 재귀함수를 사용하는 방식이 아니라
이 아닌 현재 노드의 값을 정답 문자열에 넣고 자식 노드값을 재귀함수에 넣어 처리하는 방식이 적절해 보였다.
위 방식을 적용하기 위해 최하위 노드 4,5,6,7 도 만들어 tree 배열에 노드 객체로 존재해야 가능하기 때문에 트리를 구성하는 반복문이 current < end
기준을 num < end
로 변경해 모든 노드를 객체로 만든 후 위 방식을 적용해 순회하는 함수를 구현하여 조금 더 깔끔하게 변경
다른 해결 방법
function solution(n, end) {
let answer = "";
function DFS(v) {
if (v > end) return;
else {
// 전위순회
answer += v + " ";
DFS(v * 2);
DFS(v * 2 + 1);
// 중위순회
// DFS(v * 2);
// answer += v + " ";
// DFS(v * 2 + 1);
// 후위순회
// DFS(v * 2);
// DFS(v * 2 + 1);
// answer += v + " ";
}
}
DFS(n);
return answer;
}
console.log(solution(1, 7));
풀이
- 그림과 같이 왼쪽 자식 노드가 부모 노드의 x 2 이고 오른쪽 자식 노드가 부모 노드의 x 2 + 1 인 것을 활용
- end 7 보다 커질 때 종료되는 재귀함수를 구현
전위 순회 :
1. 현재 노드의 값을 정답 문자열에 추가
2. 재귀 함수에 현재 좌측 노드값을 의미하는 (노드값 x 2) 으로 다시 함수 호출
3. 재귀 함수에 현재 우측 노드값을 의미하는 (노드값 x 2 + 1) 으로 다시 함수 호출
중위 순회 :
1. 재귀 함수에 현재 좌측 노드값을 의미하는 (노드값 x 2) 으로 다시 함수 호출
2. 현재 노드의 값을 정답 문자열에 추가
3. 재귀 함수에 현재 우측 노드값을 의미하는 (노드값 x 2 + 1) 으로 다시 함수 호출
후위 순회 :
1. 재귀 함수에 현재 좌측 노드값을 의미하는 (노드값 x 2) 으로 다시 함수 호출
2. 재귀 함수에 현재 우측 노드값을 의미하는 (노드값 x 2 + 1) 으로 다시 함수 호출
3. 현재 노드의 값을 정답 문자열에 추가
참고
'코딩 테스트 문제 및 풀이' 카테고리의 다른 글
[JS] 코딩 테스트 문제 : 합이 같은 부분집합 [DFS] (0) | 2024.01.24 |
---|---|
[JS] 코딩 테스트 문제 : 부분 집합 구하기 [DFS] (0) | 2024.01.23 |
[JS] 코딩 테스트 문제 : 마구간 정하기 [2진 탐색, 결정알고리즘] (0) | 2024.01.19 |
[JS] 코딩 테스트 문제 : 뮤직비디오 [2진 탐색, 결정알고리즘] (0) | 2024.01.17 |
[JS] 코딩 테스트 문제 : 이분검색 [이진 탐색] (0) | 2024.01.16 |