오늘은 비선형자료구조인 트리의 개념 및 배열과 연결리스를 통해서 Tree로 간단히 구현해보는 시간을 가지려고 한다.
ㆍTree의 개념
- 노드와 링크로 구성된 자료구조 (그래프의 일종, cycle x)
- 계층적 구조를 나타낼 때 사용
ㆍTree의 구조
- 노드 (Node)
- 트리구조3의 자료값을 담고있는 단위 - 에지 (Edge)
- 노드간의 연결선 - 루트노드 (Root Node)
- 부모가 없는 노드, 최상위 노드 - 잎새노드 (Leaf Node)
- 자식이 없는 노드, 단말 노드 - 내부노드 (Internal Node)
- 잎새노드를 제외한 모든 노드 - 부모노드 (Parent Node)
- 연결된 두 노드 중 상위의 노드 - 자식노드 (Child Node)
- 연결된 두 노드 중 하위의 노드 - 형제노드 (Sibling Node)
- 같은 부모를 가지는 노드 - 깊이 (Depth)
- 루트에서 어떤 노드까지의 간선의 수 - 레벨 (Level)
- 트리의 특정 깊이를 가지는 노드 집합 - 높이 (Height)
- 트리에서 가장 큰 레벨 값 - 크기 (Size)
- 자신을 포함한 자식 노드의 개수 - 차수 (Degree)
- 각 노드가 지닌 가지의 수 - 트리의 Degree
- 트리의 최대 차수
ㆍ 트리의 특징
- 하나의 노드에서 다른 노드로 이동하는 경로는 유일
- 노드가 N개인 트리의 Edge의 수 = N - 1개
- Acycle
- 모든 노드는 서로 연결되어 있다.
- 하나의 Edge를 끊으면 2개의 Sub-Tree로 분리
ㆍ 트리의 종류
- 이진 트리 (Binary tree)
- 각 노드는 최대 2개의 자식을 가질 수 있다.
- 자식노드는 좌우로 구분
- 왼쪽 자식 (부모노드의 왼쪽 아래) / 오른쪽 자식 (부모노드의 오른쪽 아래) - 포화이진트리 (Perfect binary tree)
- 모든 레벨에서 노드들이 꽉 채워져 있는 트리 - 완전이진트리 (Complete binary tree)
- 마지막 레벨을 제외하고 노드들이 모두 채워져 있는 트리 - 정 이진트리 (Full binary tree)
- 모든 노드가 0개 아니면 2개의 자식노드를 갖는 트리 - 편향트리 (Skewed binary tree)
- 한쪽으로 기울어진 트리 - 균형이진트리 (Balanced binary tree)
- 모든 노드의 좌우서브트리 높이가 1이상 차이 나지 않는 트리
ㆍ 이진트리의 특징
- 포화 이진트리의 높이가 h이라면, 노드의 수는 2의 h + 1 승 - 1개
- 포와(or 완전) 이진 트리의 노드가 N개일 때, 높이는 logN
- 이진 트리의 노드가 N개일 때, 최대 가능 높이는 N - 1
ㆍ 이진트리의 순회 (Traversal)
- 모든 노드를 빠뜨리지않고 중복하지 않고 방문하는 연산
- 전위순회 (Preorder) -> 방문순서 : 현재 - 왼쪽 - 오른쪽
- 중위순회 (Inorder) -> 방문순서 : 왼쪽 - 현재 - 오른쪽
- 후위순회 (Postorder) -> 방문순서 : 왼쪽 - 오른쪽 - 현재
- 레벨순회 (Levelorder) -> 방문순서 : 위쪽레벨부터 왼쪽 - 오른쪽
ㆍ 이진트리구조 구
import java.util.LinkedList;
public class TreeStructure {
public static void main(String[] args) {
char[] arr = new char[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = (char)('A' + i);
}
BinaryTreeStructure bts = new BinaryTreeStructure(arr);
// 루트노드
System.out.println(bts.head.data);
// 노드 찾기
Node2 findNode = bts.findNode('A');
System.out.print("찾은 Node : " + findNode.data);
if (findNode.left != null) {
System.out.print(" / left Node : " + findNode.left.data);
} else {
System.out.print(" / left Node : " + null);
}
if (findNode.right != null) {
System.out.print(" / right Node : " + findNode.right.data);
} else {
System.out.print(" / right Node : " + null);
}
if (findNode.parent != null) {
System.out.print(" / parent Node : " + findNode.parent.data);
} else {
System.out.print(" / parent Node : " + null);
}
System.out.println();
// 잎새노드 찾기 => 단말노드 찾기
System.out.println("단말노드 : ");
for (int i = 0; i < arr.length; i++) {
Node2 node = bts.findNode((char)('A' + i));
if (node.left == null && node.right == null) {
System.out.print(node.data + " ");
}
}
System.out.println();
// root -> 특정 노드까지 간선 수
System.out.println("간선의 수");
Node2 node2 = bts.findNode('E');
int count = 0;
while (node2.parent != null) {
node2 = node2.parent;
count++;
}
System.out.println("루트까지의 간선의 수 : " + count);
System.out.println();
// 특정 level의 노드들 구하기
System.out.println("특정 level의 노드 구하기");
for (int i = 0; i < arr.length; i++) {
Node2 nodeLevel = bts.findNode((char)('A' + i));
Node2 cur = nodeLevel;
count = 0;
while (cur.parent != null) {
cur = cur.parent;
count++;
}
if (count == 2) {
System.out.print(nodeLevel.data + " ");
}
}
System.out.println();
// 높이구하기
System.out.println("높이 구하기");
int h = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
Node2 node = bts.findNode((char)('A' + i));
Node2 cur = node;
count = 0;
while (cur.parent != null) {
cur = cur.parent;
count++;
}
if (count > h) {
h = count;
}
}
System.out.println("높이 : " + h);
// 자신을 포함한 자식노드들의 수 구하기
System.out.println(bts.countChildNodePlusMe('B'));
}
}
class Node2 {
char data;
Node2 left;
Node2 right;
Node2 parent;
public Node2(char data, Node2 left, Node2 right, Node2 parent) {
this.data = data;
this.left = left;
this.right = right;
this.parent = parent;
}
}
class BinaryTreeStructure {
Node2 head;
BinaryTreeStructure() {}
BinaryTreeStructure(char[] arr) {
Node2[] node = new Node2[arr.length];
for (int i = 0; i < arr.length; i++) {
node[i] = new Node2(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) {
node[i].left = node[left];
node[left].parent = node[i];
}
if (right < arr.length) {
node[i].right = node[right];
node[right].parent = node[i];
}
}
head = node[0];
}
public Node2 findNode(char data) {
LinkedList<Node2> list = new LinkedList<>();
list.add(head);
Node2 result = null;
while (!list.isEmpty()) {
Node2 node = list.removeFirst();
if (node.data == data) {
result = node;
break;
} else {
if (node.left != null) {
list.add(node.left);
}
if (node.right != null) {
list.add(node.right);
}
}
}
return result;
}
public int countChildNodePlusMe(char data) {
Node2 node = findNode(data);
int count = 0;
LinkedList<Node2> list = new LinkedList<>();
list.add(node);
while (!list.isEmpty()) {
Node2 nodeOutput = list.removeFirst();
count++;
if (nodeOutput.left != null) {
list.add(nodeOutput.left);
}
if (nodeOutput.right != null) {
list.add(nodeOutput.right);
}
}
return count;
}
}
ㆍ 이진트리 순회구현 (배열)
레벨 순회 순으로 배열로 Tree를 구현 후 전위, 중위, 후휘, 레벨 순휘를 구현
class BinaryTreeArray {
char[] arr;
public BinaryTreeArray(char[] data) {
this.arr = data.clone();
}
public void preOrder(int idx) {
System.out.print(arr[idx] + " ");
int left = idx * 2 + 1;
int right = idx * 2 + 2;
if (left < arr.length) {
preOrder(left);
}
if (right < arr.length) {
preOrder(right);
}
}
public void inOrder(int idx) {
int left = idx * 2 + 1;
int right = idx * 2 + 2;
if (left < arr.length) {
inOrder(left);
}
System.out.print(arr[idx] + " ");
if (right < arr.length) {
inOrder(right);
}
}
public void postOrder(int idx) {
int left = idx * 2 + 1;
int right = idx * 2 + 2;
if (left < arr.length) {
postOrder(left);
}
if (right < arr.length) {
postOrder(right);
}
System.out.print(arr[idx] + " ");
}
public void levelOrder(int idx) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
ㆍ이진트리 순회구현 (연결리스)
값과 간선을 관리하기 위한 노드로 연결리스트 구성해서 간단하게 Tree를 구현 후 전위, 중위, 후휘, 레벨 순휘를 구현
class NodeTree {
char data;
NodeTree left;
NodeTree right;
public NodeTree(char data, NodeTree left, NodeTree right) {
this.data = data;
this.left = left;
this.right = right;
}
}
class BinaryTreeLinkedList {
NodeTree head;
BinaryTreeLinkedList() {}
BinaryTreeLinkedList(char[] arr) {
NodeTree[] nodeTrees = new NodeTree[arr.length];
for (int i = 0; i < arr.length; i++) {
nodeTrees[i] = new NodeTree(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) {
nodeTrees[i].left = nodeTrees[left];
}
if (right < arr.length) {
nodeTrees[i].right = nodeTrees[right];
}
}
head = nodeTrees[0];
}
public void preOrder(NodeTree nodeTree) {
if (nodeTree == null) {
return;
}
System.out.print(nodeTree.data + " ");
preOrder(nodeTree.left);
preOrder(nodeTree.right);
}
public void inOrder(NodeTree nodeTree) {
if (nodeTree == null) {
return;
}
preOrder(nodeTree.left);
System.out.print(nodeTree.data + " ");
preOrder(nodeTree.right);
}
public void postOrder(NodeTree nodeTree) {
if (nodeTree == null) {
return;
}
preOrder(nodeTree.left);
preOrder(nodeTree.right);
System.out.print(nodeTree.data + " ");
}
public void levelOrder(NodeTree nodeTree) {
LinkedList<NodeTree> list = new LinkedList<>();
list.addFirst(nodeTree);
while(!list.isEmpty()){
NodeTree n = list.removeFirst();
System.out.print(n.data + " ");
if (n.left != null) {
list.addLast(n.left);
}
if (n.right != null) {
list.addLast(n.right);
}
}
}
}
'Algorithms > 개념' 카테고리의 다른 글
탐욕 알고리즘(Greedy), 분할정복 알고리즘(Divide and Conquer), 다이나믹 알고리즘 (Dynamic) (1) | 2024.03.15 |
---|---|
배열과 연결리스트로 Graph 구현 및 기본 개념 정리 (0) | 2024.03.06 |
ArrayList를 이용한 Deque구현 및 기본 개념 정리 (0) | 2024.03.06 |
ArrayList를 이용한 Queue구현 및 기본 개념 정리 (0) | 2024.03.06 |
ArrayList를 이용한 Stack 구현 및 기본 개념 정리 (0) | 2024.03.06 |