읽기 전에 알아두면 좋아요
아래 개념이 익숙하면 본문을 바로 읽으세요. 낯선 개념만 링크에서 복습하면 됩니다.
- JavaScript 이진 탐색 트리 구현: 중복 키와 세 가지 삭제 처리이진 탐색 트리는 작은 키와 큰 키를 양쪽으로 나누어 저장합니다. BST 방향 규칙과 중복 개수·중위 순회가 구간 결과에 미치는 영향을 확인합니다.
- 트리 자료구조 차이: 이진 트리 BST MST 구분하기트리는 노드가 부모와 자식으로 연결된 구조입니다. 연결을 따라 읽는 방법을 확인합니다. 트리의 관계와 깊이, 이진 트리의 종류를 정리합니다.
개념과 예제는 실행하지 않아도 읽을 수 있습니다. 내 컴퓨터·터미널에서 직접 실행하는 과정은 선택 실습입니다.
과정 위치와 선택 복습 안내
과정 위치: 트리·그래프와 최단거리 · 문제
과정 위치는 이 글이 속한 묶음입니다. 이 단계의 모든 개념을 먼저 읽을 필요는 없습니다. 위에 연결한 개념 중 낯선 내용만 복습하세요. 문제의 조건·상태·갱신 순서는 익숙한 언어로 설명하면 됩니다.
JavaScript로 문법·실행 복습하기 (선택)
개념 글의 코드는 JavaScript 예시입니다. 문제 화면에서는 JavaScript·Python·Java·C++·C# 중 익숙한 언어의 함수를 작성하세요. 본문의 Node.js 입출력 예시는 로컬 실행을 위한 별도 안내입니다.
BST 구간 보고서 문제에서는 중복 키를 포함한 트리의 어떤 키를 대상으로 하나요?
중복 키를 포함한 트리에서 닫힌 구간에 해당하는 키의 보고서를 만드는 문제입니다. 이진 탐색 트리·구간·중복의 기본을 준비하고, 아래 입력·반환 계약을 확인해 직접 풀어보세요.
적용 조건·범위: 입력은 엄격한 BST이며 공유 노드나 사이클이 없습니다. 정답 함수는 트리 전체의 유효성 검사를 다시 하지 않습니다. 표준 출력은 조건을 만족하는 숫자 배열이고 양쪽 경계와 같은 키도 포함합니다. 출력 개수가 커질 수 있으므로 count를 생략하거나 1로 추측해서 처리하지 않습니다.
독립 창작 문제
이 문제의 상황·입출력 계약·예시는 이 글을 위해 직접 구성했습니다. 공식 문제를 번역하거나 복제한 지문이 아닙니다. 구현 개념은 JavaScript 이진 탐색 트리 구현: 중복 키와 세 가지 삭제 처리에서 먼저 확인할 수 있습니다.
이미 만들어진 이진 탐색 트리와 low, high가 주어집니다. low ≤ key ≤ high인 키를 중복 횟수 count만큼 오름차순으로 출력하세요. low가 high보다 크면 빈 배열을 반환합니다. 트리를 새로 구축하거나 변경할 필요는 없습니다.
각 노드의 왼쪽 부분 트리에는 더 작은 키, 오른쪽에는 더 큰 키만 존재합니다. 중복 키는 별도 노드로 나뉘지 않고 해당 노드의 count에 합쳐져 있습니다. 단순 이진 트리를 BST라고 가정하면 가지치기가 유효하지 않으므로 이 조건은 입력 계약의 핵심입니다.
입력·출력과 제약
표준 입력은 {root,low,high} JSON 객체입니다. root는 null 또는 {key,count,left,right} 노드입니다. key, low, high는 안전 정수이고 count는 필수 필드인 1~100의 정수입니다. 없는 자식은 null 또는 필드 생략입니다. 서로 다른 노드 수 U는 0~10000, count 합 N은 100000 이하, 최대 깊이는 200입니다.
입력은 엄격한 BST이며 공유 노드나 사이클이 없습니다. 정답 함수는 트리 전체의 유효성 검사를 다시 하지 않습니다. 표준 출력은 조건을 만족하는 숫자 배열이고 양쪽 경계와 같은 키도 포함합니다. 출력 개수가 커질 수 있으므로 count를 생략하거나 1로 추측해서 처리하지 않습니다.
입력 예시
{"root":{"key":12,"count":1,"left":{"key":7,"count":2,"left":null,"right":null},"right":{"key":18,"count":1,"left":{"key":14,"count":3,"left":null,"right":null},"right":null}},"low":7,"high":14}
출력 예시
[7,7,12,14,14,14]
실행 방법
Node.js CommonJS 환경에서 정답의 전체 코드를 bst-problem.cjs로 저장합니다. 입력 예시를 UTF-8 input.json 파일로 저장한 뒤 Windows PowerShell에서는 아래 명령을 실행하세요. 내장 node:fs 외에 다른 파일이나 패키지가 필요하지 않습니다.
Get-Content -Raw -Encoding UTF8 ./input.json | node ./bst-problem.cjs
macOS·Linux의 Bash에서는 아래 입력 리다이렉션을 사용할 수 있습니다. 이 < 문법은 PowerShell 명령이 아닙니다.
node ./bst-problem.cjs < input.json
힌트
현재 키가 low보다 작으면 왼쪽 전체도 작습니다. 반대로 중위 순회에서 꺼낸 키가 high를 넘으면 그 뒤에 나올 값은 전부 범위 밖입니다. 중위 순서를 유지하는 스택과 이 두 가지 생략 조건을 결합하세요.
직접 풀어보기
먼저 선택한 언어의 입력·반환 계약에 맞춰 작성해 보세요. 힌트 받기와 정답과 비교하기는 코드를 실행하지 않는 AI 분석입니다. 해답·해설은 아래에서 필요할 때 펼칠 수 있습니다.
AI 힌트
코드를 실행한 채점 결과가 아닌 AI의 코드 분석입니다.
해답·해설 보기
'use strict';
function checkKey(key) {
if (! Number.isSafeInteger(key)) throw new TypeError('key must be a safe integer');
}
function rangeValues(root, low, high) {
checkKey(low);
checkKey(high);
const result = [];
if (low > high) return result;
const stack = [];
let current = root;
while (current != null || stack.length > 0) {
while (current != null) {
if (current.key < low) {
current = current.right;
} else {
stack.push(current);
current = current.left;
}
}
if (stack.length === 0) break;
current = stack.pop();
if (current.key > high) break;
for (let copy = 0; copy < current.count; copy += 1) {
result.push(current.key);
}
current = current.right;
}
return result;
}
function solve( {
root, low, high
}) {
return rangeValues(root, low, high);
}
원문 JSON 객체를 파싱한 객체 input을 solve(input)에 전달하고 배열을 반환합니다. 표준 입출력은 포함하지 않습니다.
def solve(input):
low, high = input['low'], input['high']
out=[]
def visit(node):
if node is None: return
key=node['key']
if key>low: visit(node.get('left'))
if low<=key<=high: out.extend([key]*node['count'])
if key<high: visit(node.get('right'))
if low<=high: visit(input['root'])
return out
원문 JSON 객체를 파싱한 dict input을 solve(input)에 전달하고 list를 반환합니다. 표준 입출력은 포함하지 않습니다.
import java.util.*;
class Solution {
static class Node {
long key;
int count;
Node left, right;
Node(long key, int count) {
this.key = key;
this.count = count;
}
}
void visit(Node n, long low, long high, List < Long > out) {
if (n == null) return;
if (n.key > low) visit(n.left, low, high, out);
if (low <= n.key && n.key <= high) for (int i = 0; i < n.count; i++) out.add(n.key);
if (n.key < high) visit(n.right, low, high, out);
}
public long[] solve(Node root, long low, long high) {
List < Long > out = new ArrayList < >();
if (low <= high) visit(root, low, high, out);
return out.stream().mapToLong(Long::longValue).toArray();
}
}
Solution.Node로 원문 BST를 구성합니다. long key와 int count, left/right를 그대로 지정하고 solve(root,low,high) → long[]. 안전 정수 범위를 long으로 보존합니다.
#include <vector>
struct Node {
long long key;
int count;
Node * left = nullptr;
Node * right = nullptr;
Node(long long k, int c) : key(k), count(c) {
}
};
void visit(Node * n, long long low, long long high, std::vector < long long > & out) {
if (! n) return;
if (n->key > low) visit(n->left, low, high, out);
if (low <= n->key && n->key <= high) for (int i = 0; i < n->count; i++) out.push_back(n->key);
if (n->key < high) visit(n->right, low, high, out);
}
std::vector < long long > solve(Node * root, long long low, long long high) {
std::vector < long long > out;
if (low <= high) visit(root, low, high, out);
return out;
}
Node 객체의 key/count/left/right로 원문 BST를 구성하고 solve(root,low,high)를 호출합니다. 입력 노드 소유권은 호출자에게 있으며 함수는 변경하지 않습니다. C++11 이상, 64비트 정수 사용.
using System.Collections.Generic;
public class Solution {
public class Node {
public long key;
public int count;
public Node left, right;
public Node(long k, int c) {
key = k;
count = c;
}
}
void Visit(Node n, long low, long high, List < long > result) {
if (n == null) return;
if (n.key > low) Visit(n.left, low, high, result);
if (low <= n.key && n.key <= high) for (int i = 0; i < n.count; i++) result.Add(n.key);
if (n.key < high) Visit(n.right, low, high, result);
}
public long[] Solve(Node root, long low, long high) {
var result = new List < long >();
if (low <= high) Visit(root, low, high, result);
return result.ToArray();
}
}
Solution.Node로 원문 BST를 구성하고 Solve(root,low,high) → long[]. long을 사용해 안전 정수 범위를 보존합니다.
한 파일로 실행하는 전체 정답
'use strict';
function checkKey(key) {
if (!Number.isSafeInteger(key)) throw new TypeError('key must be a safe integer');
}
function rangeValues(root, low, high) {
checkKey(low);
checkKey(high);
const result = [];
if (low > high) return result;
const stack = [];
let current = root;
while (current != null || stack.length > 0) {
while (current != null) {
if (current.key < low) {
current = current.right;
} else {
stack.push(current);
current = current.left;
}
}
if (stack.length === 0) break;
current = stack.pop();
if (current.key > high) break;
for (let copy = 0; copy < current.count; copy += 1) {
result.push(current.key);
}
current = current.right;
}
return result;
}
function solve({ root, low, high }) {
return rangeValues(root, low, high);
}
module.exports = { solve };
if (require.main === module) {
const fs = require('node:fs');
const input = JSON.parse(fs.readFileSync(0, 'utf8').replace(/^\uFEFF/, ''));
process.stdout.write(JSON.stringify(solve(input)) + '\n');
}
정답 해설: 순서가 생략을 정당화합니다
내부 반복에서 key < low인 노드는 스택에 저장하지 않고 오른쪽으로 넘어갑니다. 그 노드의 왼쪽까지 방문할 필요가 없기 때문입니다. 나머지는 스택에 쌓고 왼쪽으로 내려가 아직 출력하지 않은 작은 키를 먼저 처리합니다.
스택에서 꺼낸 키는 low 이상입니다. 그 키가 high를 넘으면 이후 중위 방문도 더 큰 키뿐이라 break로 전체 반복을 끝낼 수 있습니다. 범위 안이면 count만큼 결과에 추가하고 오른쪽 부분 트리로 이동합니다. 입력의 연결이나 count를 고치지 않는 읽기 전용 연산입니다.
| 예시 단계 | 판정 | 출력 |
|---|---|---|
| 루트 12 보류, 왼쪽 7 | 7은 low와 같음 | 7,7 |
| 스택의 12 처리 | 범위 안 | 7,7,12 |
| 오른쪽 18 보류, 왼쪽 14 | 14는 high와 같음 | 7,7,12,14,14,14 |
| 18 처리 시도 | high 초과, 종료 | 변화 없음 |
예시에서는 왼쪽 키 7의 count가 2이고 키 14의 count가 3입니다. 같은 숫자를 여러 번 출력하는 것은 중복 방문 오류가 아니라 문제에서 요구한 확장입니다. low=high=14라면 14 세 개만 출력하고 low=15,high=14라면 탐색 없이 빈 결과입니다.
복잡도
h는 높이, k는 실제 출력 개수입니다. 유효한 BST에서 범위 안 노드와 두 경계로 가는 경로만 방문하므로 시간은 O(h+k+1), 최악 O(U+k)입니다. 보조 스택은 O(h+1), 결과는 O(k)입니다. 표준 입력 JSON 트리까지 포함한 전체 프로그램 공간은 O(U+k)이며, 트리 균형은 보장하지 않으므로 h를 무조건 log U로 바꾸면 안 됩니다.
이 문제는 완성된 트리를 받으므로 삽입으로 구축하는 비용은 없습니다. values 배열을 BST에 하나씩 넣는 별도 입력 설계를 택하면 정렬된 입력에서 구축만 O(U²)이 될 수 있습니다. 입력 형식이 달라지면 같은 범위 함수라도 프로그램 전체 복잡도가 달라집니다.
직접 확인할 경계
풀이 직접 실행
본문 풀이에 예제 입력을 넣고, 입력을 바꿔 계산 결과를 비교합니다.
예제 크게 보기 (새 탭)빈 root, low > high, 모든 키보다 낮거나 높은 범위, 한 키만 포함하는 범위, 중복 횟수가 큰 노드, 편향 트리를 확인하세요. 테스트에서는 BST에 넣은 원래 다중집합을 filter 후 sort한 결과와 비교하면 가지치기 구현과 독립적으로 정답을 확인할 수 있습니다.
함께 연습하고 확인할 자료
Delete Node in a BST: BST 노드 삭제를 연습하는 외부 문제입니다. 이 글의 count 중복 정책과 달리 공식 입력의 키 조건을 따르세요.
관련 개념 참고: Princeton Algorithms — Binary Search Trees 공식 자료와 문제 페이지는 2026년 9월 10일 확인했습니다. 외부 문제의 지문·예시를 복제하지 않고 이 글의 예제와 창작 문제를 별도로 구성했습니다.
이 문제를 푼 뒤 확인하기
범위 양 끝과 같은 키가 여러 번 들어올 때 중복 횟수와 출력 순서를 확인해 보세요.
본문의 예제와 이 반례를 직접 실행해 예상 출력과 비교하고, 결과가 다르면 입력 조건과 반복 범위를 다시 확인하세요.
글 소개·학습 준비·전체 목차

핵심 요약
중복 횟수를 가진 BST에서 닫힌 구간의 키만 오름차순으로 출력합니다. 모든 노드를 수집해 정렬하는 대신 BST의 순서로 불가능한 가지를 제외하는 독립 창작 문제입니다.
이 글이 도움이 되었나요?
이어서 공부하기
체크포인트: 해설을 가리고 다시 풀고, 경계 입력을 하나 바꾸어 예상 결과와 실제 결과를 비교하세요. 층별 방문 순서와 최솟값 추출을 추적하고, 다익스트라의 비음수 조건과 오래된 후보를 설명하세요.
다음 추천 · 자료구조 개념: JavaScript 최소 힙 구현: 우선순위 큐의 push·pop과 비교 함수
권장 순서는 학습 가이드와 같습니다. 다른 순서는 접힌 참고 목록에서 확인할 수 있습니다. 읽음 표시는 숙달이나 채점 결과가 아닙니다.
원래 과정 목록 순서 (선택)
읽음 기록
읽음 — / 전체 58개 · 필수 58개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.