노드와 링크로 구성된 자료구조(그래프 일종, Cycle 없음 : 있으면 그래프)
계층적 구조를 나타낼때 사용
노드(Node)
: 트리 구조의 자료값을 담고 있는 단위엣지(Edge)
: 노드 간의 연결선(== link, Branch)루트 노드(Root)
: 부모가 없는 노드, 가장 위의 노드리프 노드(Leaf)
: 자식이 없는 노드(=단말)내부 노드(Internal)
: 리프 노드를 제외한 모든 노드부모(Parent)
: 연결된 두 노드 중 상위 노드자식(Child)
: 연결된 두 노드 중 하위의 노드형제(Sibling)
: 같은 부모를 가지는 노드깊이(Depth)
: 루트에서 어떤 노드까지의 간선의 수레벨(Level)
: 트리의 특정 깊이를 가지는 노드 집합높이(Height)
: 트리에서 가장 큰 레벨 값크기(Size)
: 자신을 포함한 자식 노드의 개수차수(Degree)
: 각 노드가 지닌 가지의 수트리의 차수
: 트리의 최대 차수각 노드는 최대 2개의 자식을 가질 수 있음
자식 노드는 좌우를 구분함
-> 왼쪽 자식 : 부모 노드의 왼쪽 아래
-> 오른쪽 자식 : 부모 노드의 오른쪽 아래
포화이진트리(Perfect Binary Tree)
: 모든 레벨에서 노드들이 꽉 채워져 있는 트리
완전이진트리(Complete Binary Tree
) : 마지막 레벨을 제외하고 노드들이 모두 채워져 있는 트리
정 이진 트리 (Full binary Tree
) : 모든 노드가 0개 또는 2개의 자식 노드를 갖는 트리
편향 트리 (Skewed Binary Tree
) : 한쪽으로 기울어진 트리
균형 이진 트리(Balanced Binary Tree
) : 모든 노드의 좌우 서브 트리 높이(트리에서 가장 큰 레벨 값)가 1이상 차이가 나지 않는
트리
-> 균형 이진 트리가 아닌 것 보다 균형 이진 트리는 탐색 속도가 빠름
포화 이진 트리의 높이가 h일때 -> 노드의 수는 Math.pow(2, h+1) - 1
포화 (or 완전) 이진 트리의 노드가 N개 일대, 높이는 logN
(소숫점은 버림)
이진 트리의 노드가 N개 일때, 최대 가능 높이는 N-1
(편향 트리인 경우)
모든 노드를 빠뜨리거나 중복하지 않고 방문하는 연산
순회 종류 4가지
전위 순회, 중위 순회, 후위 순회 -> DFS의 일종
레벨 순회 -> BFS의 일종
현재 노드 -> 왼쪽 노드 -> 오른쪽 노드
왼쪽 노드 -> 현재 노드 -> 오른쪽 노드
왼쪽 노드 -> 오른쪽 노드 -> 현재 노드
위쪽 레벨부터 왼쪽 노드 -> 오른쪽 노드
배열 : 레벨 순회 순으로 배열에 구성
위 그림은 배열의 인덱스가 1부터 시작한다 가정
연결리스트 사용 시 -> 값과 간선을 관하기 위한 노드로 연결리스트 구성(next가 2개 있다고 생각하자)
// 배열을 이용한 이진 트리 구성, 순회
class BinaryTree{
char[] arr;
BinaryTree(char[] data){
this.arr = data.clone();
}
public void preOrder(int idx){
System.out.print(this.arr[idx] + " ");
int left = 2 * idx + 1;
int right = 2 * idx + 2;
if(left < this.arr.length){
this.preOrder(left);
}
if(right < this.arr.length){
this.preOrder(right);
}
}
public void inOrder(int idx){
int left = 2 * idx + 1;
int right = 2 * idx + 2;
if(left < this.arr.length){
this.inOrder(left);
}
System.out.print(this.arr[idx] + " ");
if(right < this.arr.length){
this.inOrder(right);
}
}
public void postOrder(int idx){
int left = 2 * idx + 1;
int right = 2 * idx + 2;
if(left < this.arr.length){
this.postOrder(left);
}
if(right < this.arr.length){
this.postOrder(right);
}
System.out.print(this.arr[idx] + " ");
}
public void levelOrder(int idx){
for(int i = 0; i < this.arr.length; i++){
System.out.print(this.arr[i] + " ");
}
System.out.println();
}
}
class Node{
char data;
Node left;
Node right;
public Node(char data, Node left, Node right) {
this.data = data;
this.left = left;
this.right = right;
}
}
class BinaryTree{
Node head;
BinaryTree(){};
BinaryTree(char[] arr){
Node[] nodes = new Node[arr.length];
for (int i = 0; i < arr.length; i++) {
nodes[i] = new Node(arr[i], null, null);
}
for (int i = 0; i < arr.length; i++) {
int left = 2 * i + 1;
int right = 2 * i + 2;
if(left < arr.length){
nodes[i].left = nodes[left];
}
if(right < arr.length){
nodes[i].right = nodes[right];
}
}
this.head = nodes[0];
}
public void preOrder(Node node){
if(node == null){
return;
}
System.out.print(node.data + " ");
this.preOrder(node.left);
this.preOrder(node.right);
}
public void inOrder(Node node){
if(node == null){
return;
}
this.inOrder(node.left);
System.out.print(node.data + " ");
this.inOrder(node.right);
}
public void postOrder(Node node){
if(node == null){
return;
}
this.preOrder(node.left);
this.preOrder(node.right);
System.out.print(node.data + " ");
}
public void levelOrder(Node node){
Queue<Node> queue = new LinkedList<>();
queue.add(node);
while(!queue.isEmpty()){
Node cur = queue.poll();
System.out.println(cur.data + " ");
if(cur.left!= null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
}
package org.example;
// 연결리스트를 이용한 이진트리 구성
import java.util.LinkedList;
import java.util.Queue;
class Node{
char data;
Node left;
Node right;
Node parent;
public Node(char data, Node left, Node right, Node parent) {
this.data = data;
this.left = left;
this.right = right;
this.parent = parent;
}
}
class BinaryTree{
Node head;
BinaryTree(char[] arr){
Node[] nodes = new Node[arr.length];
for (int i = 0; i < arr.length; i++) {
nodes[i] = new Node(arr[i], null, null, null);
}
for(int i = 0; i < arr.length; i++){
int left = 2 * i + 1;
int right = 2 * i + 2;
if(left < arr.length){
nodes[i].left = nodes[left];
nodes[left].parent = nodes[i];
}
if(right < arr.length){
nodes[i].right = nodes[right];
nodes[right].parent = nodes[i];
}
}
this.head = nodes[0];
}
public Node searchNode(char data){
Queue<Node> queue = new LinkedList<>();
queue.add(this.head);
while(!queue.isEmpty()){
Node cur = queue.poll();
if(cur.data == data){
return cur;
}
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
return null;
}
public Integer checkSize(char data){
Node node = this.searchNode(data);
Queue<Node> queue = new LinkedList<>();
queue.add(node);
int size = 0;
while(!queue.isEmpty()){
Node cur = queue.poll();
if(cur.left != null){
queue.offer(cur.left);
size++;
}
if(cur.right != null){
queue.offer(cur.right);
size++;
}
}
return size + 1;
}
}
public class Main {
public static void main(String[] args) {
char[] arr = new char[10];
for(int i = 0; i < arr.length; i++){
arr[i] = (char)('A' + i);
}
BinaryTree bt = new BinaryTree(arr);
// Root node
System.out.println("ROOT : " + bt.head.data);
//B's Child node
Node B = bt.searchNode('B');
if(B.left != null){
System.out.println("B -> left child: " + B.left.data);
}
if(B.right != null){
System.out.println("B -> right child: " + B.right.data);
}
// F's parent node
Node F = bt.searchNode('F');
System.out.println("F -> parent : " + F.parent.data);
// Leaf node
System.out.print("Leaf node: ");
for (int i = 0; i < arr.length; i++) {
Node cur = bt.searchNode((char)('A' + i));
if(cur.left == null && cur.right == null){
System.out.print(cur.data + " ");
}
}
System.out.println();
// E's Depth (루트 부터 E까지 엣지를 몇번 거치는지)
Node E = bt.searchNode('E');
Node cur = E;
int cnt = 0;
while(cur.parent != null){
cur = cur.parent;
cnt++;
}
System.out.println("E depth : " + cnt);
// Level2 Node
System.out.print("Level2 node : ");
for (int i = 0; i < arr.length; i++) {
Node target = bt.searchNode((char)('A' + i));
cur = target;
cnt = 0;
while(cur.parent != null){
cur = cur.parent;
cnt++;
}
if(cnt == 2){
System.out.print(target.data + " ");
}
}
System.out.println();
//Height
int maxCnt = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
Node target = bt.searchNode((char)('A' + i));
cur = target;
cnt = 0;
while(cur.parent != null){
cur = cur.parent;
cnt++;
}
if(maxCnt < cnt){
maxCnt = cnt;
}
}
System.out.println("Height : " + maxCnt);
// B's Size
int size = bt.checkSize('B');
System.out.println("B size = " + size);
}
}
// Practice1
// 종이 접기
// 종이를 반으로 접었을 때, 안으로 파인 부분은 0, 볼록 튀어나온 부분은 1이라고 하자.
// 종이를 접을 때는 오른쪽에서 왼쪽으로 접는다.
// 종이를 N번 접었을 때의 접힌 상태를 출력하는 문제를 작성하세요.
// 입출력 예시
// 입력: 1
// 출력: 0
// 입력: 2
// 출력: 0, 0, 1
// 입력: 3
// 출력: 0, 0, 1, 0, 0, 1, 1
public class Practice1 {
public static void solution(int n) {
int[] binaryTree = new int[(int)Math.pow(2,n)];
binaryTree[0] = 0;
for (int i = 0; i < (int)Math.pow(2, n-1) - 1; i++) {
binaryTree[i * 2 + 1] = 0;
binaryTree[i * 2 + 2] = 1;
}
inOrder(binaryTree, 0);
System.out.println();
}
public static void inOrder(int[] arr, int idx){
int left = 2 * idx + 1;
int right = 2 * idx + 2;
if(left < arr.length-1){
inOrder(arr,left);
}
System.out.print(arr[idx] + " ");
if(right < arr.length - 1){
inOrder(arr,right);
}
}
public static void main(String[] args) {
// Test code
solution(1);
solution(2);
solution(3);
}
}
// Practice2
// 각각의 에지에 가중치가 있는 포화 이진 트리가 있다.
// 루트에서 각 리프까지의 경로 길이를 모두 같게 설정하고,
// 이 때, 모든 가중치들의 총합이 최소가 되도록 하는 프로그램을 작성하세요.
class BinaryTree{
int h;
int[] arr;
int result;
public BinaryTree(int h, int[] w){
this.h = h;
arr = new int[(int)Math.pow(2, h+1)];
result = 0;
for (int i = 2; i < (int)Math.pow(2, h+ 1); i++) {
arr[i] = w[i - 2];
}
}
public int dfs(int idx, int h){
if(this.h == h){
result += arr[idx];
return arr[idx];
}
int left = dfs(idx * 2, h + 1);
int right = dfs(idx * 2 + 1, h + 1);
result += arr[idx] + Math.abs(left-right);
return arr[idx] + Math.max(left,right);
}
}
public class Practice2 {
public static void solution(int h, int[] w) {
BinaryTree bt = new BinaryTree(h,w);
bt.dfs(1,0);
System.out.println(bt.result);
}
public static void main(String[] args) {
// Test code
int h = 2; // 트리 높이
int[] w = {2, 2, 2, 1, 1, 3}; // 트리에서 각각 에지의 가중치
solution(h, w);
System.out.println();
h = 3;
w = new int[]{1, 2, 1, 3, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1};
solution(h, w);
}
}
트리는 노드와 엣지로 연결된 그래프의 특수한 형태
-> 즉 그래프의 표현으로도 tree를 표현할 수 있다
트리에서 임의의 두 노드를 이어주는 경로는 유일
코딩테스트에서 트리에 관한 문제가 나올떈 크게 2가지
그래프로 푸는 트리
트리만을 위한 문제