트리 (Tree)란 노드들이 나무 가지처럼 연결된 비선형 계층적 자료구조입니다.
트리는 항상 루트(root)에서부터 시작된다. 루트는 자식(child) 노드를 가지며, 간선(edge)으로 연결되어 있다. 자식 노드의 개수는 차수(degree)라고 하며, 크기(size)는 자신을 포함한 모든 자식 노드의 개수다. 높이(height)는 현재 위치에서부터 리프(leaf)까지의 거리, 깊이(depth)는 루트에서부터 현재 노드까지의 거리다. 트리는 그 자식도 트리인 서브트리(subtree) 구성을 띤다. 레벨(level)은 0부터 시작한다.
이진트리(Binary Tree)는 트리(Tree)의 일종으로, 각 노드가 최대 두 개의 자식 노드를 가지는 특별한 형태의 자료구조이다.
특징
특징
h
인 포화 이진 트리에서 노드 갯수는 2^(k+1) – 1
2^h
트리의 모든 노드들을 방문하는 과정을 트리 순회(TreeTraversal)라고 한다.
선형 자료 구조(연결 리스트, 스택, 큐 등)는 순차적으로 요소에 접근하지만 트리 자료구조는 다른 방식을 사용해야 합니다.
트리 순회 방식
전위 순회는 다음과 같은 방법으로 진행한다.
1. 왼쪽 서브 트리를 전위 순회한다.
2. Root 노드를 방문한다.
3. 오른쪽 서브 트리를 전위 순회한다.
1. 왼쪽 서브 트리를 전위 순회한다.
2. 오른쪽 서브 트리를 전위 순회한다.
3. Root 노드를 방문한다.
이진탐색트리란 다음과 같은 특징을 갖는 이진트리를 말한다.
1. 각 노드에 중복되지 않는 키(key)가 있다.
2. 루트노드의 왼쪽 서브 트리는 해당 노드의 키보다 작은 키를 갖는 노드들로 이루어져 있다.
3. 루트노드의 오른쪽 서브 트리는 해당 노드의 키보다 큰 키를 갖는 노드들로 이루어져 있다.
4. 좌우 서브 트리도 모두 이진 탐색 트리여야 한다.
BinarySearchTree
package com.example.datastructure.Tree;
import java.util.ArrayList;
import java.util.List;
public class BinarySearchTree<T extends Comparable<T>> implements ITree<T> {
private Node root;
private int size;
public BinarySearchTree() {
this.size = 0;
}
@Override
public void insert(T val) {
this.root = insertNode(root, val);
size++;
}
@Override
public void delete(T val) {
deleteNode(root, val);
}
private Node deleteNode(Node node, T val) {
if (node == null) {
return null;
}
if (val.compareTo(node.data) < 0) {
node.left = deleteNode(node.left, val);
} else if (val.compareTo(node.data) > 0) {
node.right = deleteNode(node.right, val);
} else {
this.size--;
if (node.left == null) {
return node.right;
} else if (node.right == null) {
return node.left;
}
node.data = minNode(node.right);
node.right = deleteNode(node.right, node.data);
}
return node;
}
public T min() {
return this.minNode(this.root);
}
public T max() {
return this.maxNode(this.root);
}
private T minNode(Node node) {
T minData = node.data;
while (node.left != null) {
minData = node.left.data;
node = node.left;
}
return minData;
}
private T maxNode(Node node) {
T maxData = node.data;
while (node.right != null) {
maxData = node.right.data;
node = node.right;
}
return maxData;
}
private Node insertNode(Node node, T val) {
if (node == null) {
return new Node(val);
}
// a.compareTo(b) 는 a 가 b 보다 작으면 -1 을 리턴
// 동일할 때 0 , b 가 더 크면 1을 리턴
if (val.compareTo(node.data) < 0) {
// 추가될 값이 노드의 데이터 보다 작은 경우 노드의 왼쪽에 위치 해야 함
node.left = insertNode(node.left, val);
} else if (val.compareTo(node.data) > 0) {
// 추가될 값이 노드의 데이터 보다 큰 경우 노드의 오른쪽 위치 해야 함
node.right = insertNode(node.right, val);
}
return node;
}
@Override
public boolean contains(T val) {
return containsNode(root, val);
}
@Override
public int size() {
return this.size;
}
private boolean containsNode(Node node, T val) {
if (node == null) {
return false;
}
// a.compareTo(b)
// a < b --> return -1
// a == b --> return 0
// a > b --> return 1
if (val.compareTo(node.data) == 0) {
return true;
}
if (val.compareTo(node.data) < 0) {
return containsNode(node.left, val);
}
return containsNode(node.right, val);
}
public List<T> preOrder() {
return preOrderTree(root, new ArrayList<>());
}
private List<T> preOrderTree(Node node, List<T> visited) {
if (node == null) return visited;
visited.add(node.data);
preOrderTree(node.left, visited);
preOrderTree(node.right, visited);
return visited;
}
public List<T> inOrder() {
return inOrderTree(root, new ArrayList<>());
}
private List<T> inOrderTree(Node node, List<T> visited) {
if (node == null) return visited;
inOrderTree(node.left, visited);
visited.add(node.data);
inOrderTree(node.right, visited);
return visited;
}
public List<T> postOrder() {
return postOrderTree(root, new ArrayList<>());
}
private List<T> postOrderTree(Node node, List<T> visited) {
if (node == null) return visited;
postOrderTree(node.left, visited);
postOrderTree(node.right, visited);
visited.add(node.data);
return visited;
}
private class Node {
T data;
Node left;
Node right;
Node(T data) {
this.data = data;
}
Node(T data, Node left, Node right) {
this.data = data;
this.left = left;
this.right = right;
}
}
}
삽입 과정
private Node insertNode(Node node, T val) {
if (node == null) {
return new Node(val);
}
// a.compareTo(b) 는 a 가 b 보다 작으면 -1 을 리턴
// 동일할 때 0 , b 가 더 크면 1을 리턴
if (val.compareTo(node.data) < 0) {
// 추가될 값이 노드의 데이터 보다 작은 경우 노드의 왼쪽에 위치 해야 함
node.left = insertNode(node.left, val);
} else if (val.compareTo(node.data) > 0) {
// 추가될 값이 노드의 데이터 보다 큰 경우 노드의 오른쪽 위치 해야 함
node.right = insertNode(node.right, val);
}
return node;
}
세가지 상황이 있다.
private Node deleteNode(Node node, T val) {
if (node == null) {
return null;
}
if (val.compareTo(node.data) < 0) {
node.left = deleteNode(node.left, val);
} else if (val.compareTo(node.data) > 0) {
node.right = deleteNode(node.right, val);
} else {
this.size--;
if (node.left == null) {
return node.right;
} else if (node.right == null) {
return node.left;
}
node.data = minNode(node.right);
node.right = deleteNode(node.right, node.data);
}
return node;
}
private boolean containsNode(Node node, T val) {
if (node == null) {
return false;
}
// a.compareTo(b)
// a < b --> return -1
// a == b --> return 0
// a > b --> return 1
if (val.compareTo(node.data) == 0) {
return true;
}
if (val.compareTo(node.data) < 0) {
return containsNode(node.left, val);
}
return containsNode(node.right, val);
}
package com.example.datastructure.Tree;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/*
9934. 완전 이진 트리
*/
public class Baekjoon_9934 {
static int K;
static int[] arr;
static StringBuffer[] ans;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
K = Integer.parseInt(br.readLine());
arr = new int[(int) Math.pow(2, K) - 1];
String[] input = br.readLine().split(" ");
for (int i = 0; i < arr.length; i++)
arr[i] = Integer.parseInt(input[i]);
ans = new StringBuffer[K];
for (int i = 0; i < K; i++)
ans[i] = new StringBuffer();
solve(0, arr.length - 1, 0);
for (int i = 0; i < K; i++)
bw.write(ans[i].toString() + "\n");
bw.flush();
}
public static void solve(int s, int e, int floor) {
if (floor == K)
return;
int m = (s + e) / 2;
ans[floor].append(arr[m] + " ");
solve(s, m - 1, floor + 1);
solve(m + 1, e, floor + 1);
}
}