이진 트리 소개 – 데이터 구조 및 알고리즘 튜토리얼
이진 트리 는 비선형 데이터 구조 각 노드에는 최대 2개의 자식이 있습니다. 이 기사에서는 이진 트리의 모든 기본 사항, 이진 트리 작업, 구현, 장점, 단점을 다루며 이진 트리를 기반으로 모든 문제를 해결하는 데 도움이 됩니다.
내용의 테이블
- 이진 트리란 무엇입니까?
- 이진 트리 표현
- 이진 트리의 유형
- 이진 트리에서의 작업
- 이진 트리의 보조 작업
- 이진 트리 구현
- 이진 트리 연산의 복잡성 분석
- 이진 트리의 장점
- 이진 트리의 단점
- 이진 트리의 응용
- 이진 트리에 대해 자주 묻는 질문
이진 트리란 무엇입니까?
이진 트리는 트리 데이터 구조(비선형) 각 노드가 가질 수 있는 최대 두 명의 자녀 이는 왼쪽 아이 그리고 오른쪽 아이 .
이진 트리의 최상위 노드를 뿌리 , 최하위 노드가 호출됩니다. 나뭇잎 . 이진 트리는 루트가 맨 위에 있고 잎이 맨 아래에 있는 계층 구조로 시각화할 수 있습니다.
이진 트리 표현
이진 트리의 각 노드는 세 부분으로 구성됩니다.
- 데이터
- 왼쪽 자식에 대한 포인터
- 올바른 자식에 대한 포인터
다음은 다양한 언어로 된 이진 트리 노드의 표현입니다.
C++ // Use any below method to implement Nodes of tree // Method 1: Using 'struct' to make // user-define data type struct node { int data; struct node* left; struct node* right; }; // Method 2: Using 'class' to make // user-define data type class Node { public: int data; Node* left; Node* right; }; 씨 // Structure of each node of the tree struct node { int data; struct node* left; struct node* right; }; 자바 // Class containing left and right child // of current node and key value class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } 파이썬 # A Python class that represents # an individual node in a Binary Tree class Node: def __init__(self, key): self.left = None self.right = None self.val = key
씨# // Class containing left and right child // of current node and key value class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } 자바스크립트
이진 트리의 유형
이진 트리는 여러 요인에 따라 여러 유형으로 분류될 수 있습니다.
- 자녀 수 기준
- 완전 이진 트리
- 퇴화된 이진 트리
- 기울어진 이진 트리
- 레벨 완료를 기준으로
- 완전한 이진 트리
- 완벽한 이진 트리
- 균형 잡힌 이진 트리
- 노드 값을 기준으로:
이진 트리에서의 작업
1. 이진 트리에 삽입
노드를 노드의 왼쪽 또는 오른쪽 자식으로 삽입하거나 노드를 트리의 루트로 만들어 이진 트리의 어느 곳에나 노드를 삽입할 수 있습니다.
이진 트리에 노드를 삽입하는 알고리즘:
- 이진 트리에 왼쪽 자식이 누락된 노드가 있는지 확인하세요. 그러한 노드가 존재하는 경우 새 노드를 왼쪽 자식으로 삽입합니다.
- 이진 트리에 오른쪽 자식이 누락된 노드가 있는지 확인하세요. 그러한 노드가 존재하는 경우 새 노드를 오른쪽 하위 노드로 삽입하십시오.
- 왼쪽 또는 오른쪽 자식이 누락된 노드를 찾지 못한 경우 두 자식이 모두 누락된 노드를 찾아 해당 노드를 왼쪽 또는 오른쪽 자식으로 삽입합니다.
2. 이진 트리 순회
이진 트리 순회에는 이진 트리의 모든 노드를 방문하는 작업이 포함됩니다. 트리 순회 알고리즘은 크게 두 가지 범주로 분류될 수 있습니다.
- 깊이 우선 검색(DFS) 알고리즘
- 너비 우선 검색(BFS) 알고리즘
깊이 우선 검색(DFS) 알고리즘:
- 선주문 순회(현재-왼쪽-오른쪽): 왼쪽 또는 오른쪽 하위 트리 내부의 노드를 방문하기 전에 현재 노드를 방문하십시오. 여기서 순회는 루트 - 왼쪽 자식 - 오른쪽 자식입니다. 이는 루트 노드를 먼저 통과한 다음 왼쪽 자식, 마지막으로 오른쪽 자식을 순회한다는 의미입니다.
- 중위 순회(왼쪽-현재-오른쪽): 왼쪽 하위 트리 내부의 모든 노드를 방문한 후 오른쪽 하위 트리 내의 모든 노드를 방문하기 전에 현재 노드를 방문합니다. 여기서 순회는 왼쪽 자식 – 루트 – 오른쪽 자식입니다. 이는 왼쪽 자식이 먼저 순회된 다음 루트 노드, 마지막으로 오른쪽 자식이 순회됨을 의미합니다.
- 후위 순회(왼쪽-오른쪽-현재): 왼쪽 및 오른쪽 하위 트리의 모든 노드를 방문한 후 현재 노드를 방문합니다. 여기서 순회는 왼쪽 자식 – 오른쪽 자식 – 루트입니다. 이는 왼쪽 자식이 먼저 통과한 다음 오른쪽 자식, 마지막으로 루트 노드를 통과했음을 의미합니다.
너비 우선 검색(BFS) 알고리즘:
- 레벨 순서 순회: 같은 레벨에서 노드를 레벨별로, 왼쪽에서 오른쪽으로 방문하세요. 여기서 순회는 수준별로 이루어집니다. 이는 가장 왼쪽에 있는 자식이 먼저 통과한 후 왼쪽에서 오른쪽으로 같은 레벨의 다른 자식들이 통과했다는 의미입니다.
3. 이진트리에서의 삭제
이진 트리의 모든 노드를 삭제하고 삭제 후 노드를 다시 배열하여 유효한 이진 트리를 다시 형성할 수 있습니다.
이진 트리에서 노드를 삭제하는 알고리즘:
- 루트부터 시작하여 이진 트리에서 가장 깊고 오른쪽에 있는 노드와 삭제하려는 노드를 찾습니다.
- 가장 오른쪽에 있는 노드의 데이터를 삭제하려는 노드로 교체합니다.
- 그런 다음 가장 오른쪽에 있는 노드를 삭제합니다.
4. 이진 트리에서 검색하기
순회 기술 중 하나를 사용하여 노드의 요소를 검색할 수 있습니다.
이진 트리에서 노드를 검색하는 알고리즘:
- 루트 노드에서 시작합니다.
- 현재 노드의 값이 목표값과 같은지 확인합니다.
- 현재 노드의 값이 목표 값과 같으면 이 노드가 필수 노드입니다.
- 그렇지 않고 노드의 값이 목표 값과 같지 않으면 왼쪽 및 오른쪽 자식에서 검색을 시작합니다.
- 값이 목표와 동일한 노드를 찾지 못하면 해당 값은 트리에 존재하지 않습니다.
이진 트리의 보조 작업
- 나무의 높이 구하기
- 이진 트리에서 노드 수준 찾기
- 전체 트리의 크기 구하기
이진 트리 구현
다음은 이진 트리의 삽입, 삭제, 순회를 위한 코드입니다.
씨 #include // Node structure to define the structure of the node typedef struct Node { int data; struct Node *left, *right; } Node; // Function to create a new node Node* newNode(int val) { Node* temp = (Node*)malloc(sizeof(Node)); temp->데이터 = 발; 온도->왼쪽 = 온도->오른쪽 = NULL; 복귀온도; } // 노드를 삽입하는 함수 Node* insert(Node* root, int data) { // 트리가 비어 있으면 새 노드가 루트가 됩니다. if (root == NULL) { root = newNode(data); 루트를 반환; } // 트리를 순회하고 노드를 삽입할 위치를 찾기 위한 큐 Node* queue[100]; int 앞 = 0, 뒤 = 0; 대기열[rear++] = 루트; while (앞 != 뒤) { Node* temp = queue[front++]; // 부모 노드의 왼쪽 자식으로 노드를 삽입합니다. if (temp->left == NULL) { temp->left = newNode(data); 부서지다; } // 왼쪽 자식이 null이 아니면 큐에 푸시합니다. else queue[rear++] = temp->left; // 부모 노드의 오른쪽 자식으로 노드를 삽입합니다. if (temp->right == NULL) { temp->right = newNode(data); 부서지다; } // 오른쪽 자식이 null이 아니면 큐에 푸시합니다. else queue[rear++] = temp->right; } 루트를 반환합니다; } /* 이진 트리에서 주어진 가장 깊은 노드(d_node)를 삭제하는 함수 */ void deletDeepest(Node* root, Node* d_node) { Node* queue[100]; int 앞 = 0, 뒤 = 0; 대기열[rear++] = 루트; // 마지막 노드까지 레벨 순서 순회를 수행합니다. Node* temp; while (앞 != 뒤) { temp = 대기열[앞++]; if (temp == d_node) { temp = NULL; 무료(d_node); 반품; } if (temp->right) { if (temp->right == d_node) { temp->right = NULL; 무료(d_node); 반품; } else queue[rear++] = 임시->오른쪽; } if (temp->left) { if (temp->left == d_node) { temp->left = NULL; 무료(d_node); 반품; } else queue[rear++] = 임시->왼쪽; } } } /* 이진 트리에서 요소를 삭제하는 함수 */ Node* 삭제(Node* root, int key) { if (!root) return NULL; if (루트->왼쪽 == NULL && 루트->오른쪽 == NULL) { if (루트->데이터 == 키) return NULL; 그렇지 않으면 루트를 반환합니다. } 노드* 대기열[100]; int 앞 = 0, 뒤 = 0; 대기열[rear++] = 루트; 노드* 온도; 노드* key_node = NULL; // 가장 깊은 노드(temp)와 삭제할 노드(key_node)를 찾기 위해 레벨 순서 순회를 수행합니다. while (front != Rear) { temp = queue[front++]; if (temp->data == key) key_node = 임시; if (temp->left) queue[rear++] = temp->left; if (temp->right) queue[rear++] = temp->right; } if (key_node != NULL) { int x = 임시->데이터; key_node->data = x; deleteDeepest(루트, 임시); } 루트를 반환합니다; } // 중위 트리 순회(왼쪽 - 루트 - 오른쪽) void inorderTraversal(Node* root) { if (!root) return; inorderTraversal(루트->왼쪽); printf('%d ', 루트->데이터); inorderTraversal(루트->오른쪽); } // 선주문 트리 순회(루트 - 왼쪽 - 오른쪽) void preorderTraversal(Node* root) { if (!root) return; printf('%d ', 루트->데이터); preorderTraversal(루트->왼쪽); preorderTraversal(루트->오른쪽); } // 후위 트리 탐색 (왼쪽 - 오른쪽 - 루트) void postorderTraversal(Node* root) { if (root == NULL) return; postorderTraversal(루트->왼쪽); postorderTraversal(루트->오른쪽); printf('%d ', 루트->데이터); } // 레벨 순서 트리 탐색을 위한 함수 void levelorderTraversal(Node* root) { if (root == NULL) return; // 레벨 순서 탐색을 위한 큐 Node* queue[100]; int 앞 = 0, 뒤 = 0; 대기열[rear++] = 루트; while (앞 != 뒤) { Node* temp = queue[front++]; printf('%d ', 임시->데이터); // (temp->left) queue[rear++] = temp->left인 경우 대기열에 왼쪽 자식을 푸시합니다. // (temp->right) queue[rear++] = temp->right인 경우 대기열의 오른쪽 자식을 푸시합니다. } } /* 위의 알고리즘을 확인하는 드라이버 함수입니다. */ int main() { 노드* 루트 = NULL; // 노드 삽입 root = insert(root, 10); 루트 = 삽입(루트, 20); 루트 = 삽입(루트, 30); 루트 = 삽입(루트, 40); printf('선주문 순회: '); preorderTraversal(root); printf('
중순 순회: '); inorderTraversal(루트); printf('
후순위 순회: '); postorderTraversal(root); printf('
레벨 순서 탐색: '); levelorderTraversal(root); // 데이터 = 20인 노드 삭제 root = delete(root, 20); printf('
삭제 후 중순순회: '); inorderTraversal(루트); 0을 반환합니다. } 자바 import java.util.LinkedList; import java.util.Queue; // Node class to define the structure of the node class Node { int data; Node left, right; // Parameterized Constructor Node(int val) { data = val; left = right = null; } } public class BinaryTree { // Function to insert nodes public static Node insert(Node root, int data) { // If tree is empty, new node becomes the root if (root == null) { root = new Node(data); return root; } // Queue to traverse the tree and find the position to // insert the node Queue q = 새로운 LinkedList(); q.offer(루트); while (!q.isEmpty()) { 노드 임시 = q.poll(); // 부모 노드의 왼쪽 자식으로 노드를 삽입합니다. if (temp.left == null) { temp.left = new Node(data); 부서지다; } // 왼쪽 자식이 null이 아니면 // 대기열에 푸시합니다. else q.offer(temp.left); // 부모 노드의 오른쪽 자식으로 노드를 삽입합니다. if (temp.right == null) { temp.right = new Node(data); 부서지다; } // 오른쪽 자식이 null이 아니면 // 대기열에 푸시합니다. else q.offer(temp.right); } 루트를 반환합니다; } /* 이진 트리에서 주어진 가장 깊은 노드(d_node)를 삭제하는 함수 */ public static void deletDeepest(Node root, Node d_node) { Queue q = 새로운 LinkedList(); q.offer(루트); // 마지막 노드까지 레벨 순서 순회를 수행합니다. Node temp; while (!q.isEmpty()) { temp = q.poll(); if (temp == d_node) { temp = null; d_node = null; 반품; } if (temp.right != null) { if (temp.right == d_node) { temp.right = null; d_node = null; 반품; } else q.offer(temp.right); } if (temp.left != null) { if (temp.left == d_node) { temp.left = null; d_node = null; 반품; } else q.offer(temp.left); } } } /* 이진 트리에서 요소를 삭제하는 함수 */ public static Node delete(Node root, int key) { if (root == null) return null; if (root.left == null && root.right == null) { if (root.data == key) return null; 그렇지 않으면 루트를 반환합니다. } 대기줄 q = 새로운 LinkedList(); q.offer(루트); 노드 온도 = 새 노드(0); 노드 key_node = null; // 가장 깊은 노드(temp)와 삭제할 노드(key_node)를 찾기 위해 레벨 순서 순회를 수행합니다. while (!q.isEmpty()) { temp = q.poll(); if (temp.data == key) key_node = 임시; if (temp.left != null) q.offer(temp.left); if (temp.right != null) q.offer(temp.right); } if (key_node != null) { int x = temp.data; key_node.data = x; deleteDeepest(루트, 임시); } 루트를 반환합니다; } // 중위 트리 순회(왼쪽 - 루트 - 오른쪽) public static void inorderTraversal(Node root) { if (root == null) return; inorderTraversal(root.left); System.out.print(root.data + ' '); inorderTraversal(root.right); } // 선주문 트리 순회(루트 - 왼쪽 - 오른쪽) public static void preorderTraversal(Node root) { if (root == null) return; System.out.print(root.data + ' '); preorderTraversal(root.left); preorderTraversal(root.right); } // 후위 트리 순회(왼쪽 - 오른쪽 - 루트) public static void postorderTraversal(Node root) { if (root == null) return; postorderTraversal(root.left); postorderTraversal(root.right); System.out.print(root.data + ' '); } // 레벨 순서 트리 탐색을 위한 함수 public static void levelorderTraversal(Node root) { if (root == null) return; // 레벨 순서 순회를 위한 큐 큐 q = 새로운 LinkedList(); q.offer(루트); while (!q.isEmpty()) { 노드 임시 = q.poll(); System.out.print(temp.data + ' '); // 대기열에 왼쪽 자식을 푸시합니다. if (temp.left != null) q.offer(temp.left); // 대기열의 오른쪽 자식을 푸시합니다. if (temp.right != null) q.offer(temp.right); } } /* 위의 알고리즘을 확인하는 드라이버 함수입니다. */ public static void main(String[] args) { 노드 루트 = null; // 노드 삽입 root = insert(root, 10); 루트 = 삽입(루트, 20); 루트 = 삽입(루트, 30); 루트 = 삽입(루트, 40); System.out.print('선주문 순회: '); preorderTraversal(root); System.out.print('
순서 순회: '); inorderTraversal(루트); System.out.print('
후순위 순회: '); postorderTraversal(root); System.out.print('
레벨 순서 탐색: '); levelorderTraversal(root); // 데이터 = 20인 노드 삭제 root = delete(root, 20); System.out.print('
삭제 후 순회: '); inorderTraversal(루트); } } 파이썬 from collections import deque # Node class to define the structure of the node class Node: def __init__(self, val): self.data = val self.left = self.right = None # Function to insert nodes def insert(root, data): # If tree is empty, new node becomes the root if root is None: root = Node(data) return root # Queue to traverse the tree and find the position to insert the node q = deque() q.append(root) while q: temp = q.popleft() # Insert node as the left child of the parent node if temp.left is None: temp.left = Node(data) break # If the left child is not null push it to the queue else: q.append(temp.left) # Insert node as the right child of parent node if temp.right is None: temp.right = Node(data) break # If the right child is not null push it to the queue else: q.append(temp.right) return root # Function to delete the given deepest node (d_node) in binary tree def deletDeepest(root, d_node): q = deque() q.append(root) # Do level order traversal until last node while q: temp = q.popleft() if temp == d_node: temp = None del d_node return if temp.right: if temp.right == d_node: temp.right = None del d_node return else: q.append(temp.right) if temp.left: if temp.left == d_node: temp.left = None del d_node return else: q.append(temp.left) # Function to delete element in binary tree def deletion(root, key): if not root: return None if root.left is None and root.right is None: if root.data == key: return None else: return root q = deque() q.append(root) temp = None key_node = None # Do level order traversal to find deepest node (temp) and node to be deleted (key_node) while q: temp = q.popleft() if temp.data == key: key_node = temp if temp.left: q.append(temp.left) if temp.right: q.append(temp.right) if key_node is not None: x = temp.data key_node.data = x deletDeepest(root, temp) return root # Inorder tree traversal (Left - Root - Right) def inorderTraversal(root): if not root: return inorderTraversal(root.left) print(root.data, end=' ') inorderTraversal(root.right) # Preorder tree traversal (Root - Left - Right) def preorderTraversal(root): if not root: return print(root.data, end=' ') preorderTraversal(root.left) preorderTraversal(root.right) # Postorder tree traversal (Left - Right - Root) def postorderTraversal(root): if root is None: return postorderTraversal(root.left) postorderTraversal(root.right) print(root.data, end=' ') # Function for Level order tree traversal def levelorderTraversal(root): if root is None: return # Queue for level order traversal q = deque() q.append(root) while q: temp = q.popleft() print(temp.data, end=' ') # Push left child in the queue if temp.left: q.append(temp.left) # Push right child in the queue if temp.right: q.append(temp.right) # Driver function to check the above algorithm if __name__ == '__main__': root = None # Insertion of nodes root = insert(root, 10) root = insert(root, 20) root = insert(root, 30) root = insert(root, 40) print('Preorder traversal:', end=' ') preorderTraversal(root) print('
Inorder traversal:', end=' ') inorderTraversal(root) print('
Postorder traversal:', end=' ') postorderTraversal(root) print('
Level order traversal:', end=' ') levelorderTraversal(root) # Delete the node with data = 20 root = deletion(root, 20) print('
Inorder traversal after deletion:', end=' ') inorderTraversal(root) 씨# using System; using System.Collections.Generic; // Node class to define the structure of the node public class Node { public int data; public Node left, right; // Parameterized Constructor public Node(int val) { data = val; left = right = null; } } public class Program { // Function to insert nodes public static Node Insert(Node root, int data) { // If tree is empty, new node becomes the root if (root == null) { root = new Node(data); return root; } // Queue to traverse the tree and find the position to insert the node Queue q = 새 대기열 (); q.인큐(루트); while (q.Count != 0) { 노드 임시 = q.Dequeue(); // 부모 노드의 왼쪽 자식으로 노드를 삽입합니다. if (temp.left == null) { temp.left = new Node(data); 부서지다; } // 왼쪽 자식이 null이 아니면 큐에 푸시합니다. else q.Enqueue(temp.left); // 부모 노드의 오른쪽 자식으로 노드를 삽입합니다. if (temp.right == null) { temp.right = new Node(data); 부서지다; } // 오른쪽 자식이 null이 아니면 큐에 푸시합니다. else q.Enqueue(temp.right); } 루트를 반환합니다; } /* 이진 트리에서 주어진 가장 깊은 노드(d_node)를 삭제하는 함수 */ public static void DeleteDeepest(Node root, Node d_node) { Queue q = 새 대기열 (); q.인큐(루트); // 마지막 노드까지 레벨 순서 순회를 수행합니다. Node temp; while (q.Count != 0) { temp = q.Dequeue(); if (temp == d_node) { temp = null; d_node = null; 반품; } if (temp.right != null) { if (temp.right == d_node) { temp.right = null; d_node = null; 반품; } else q.Enqueue(temp.right); } if (temp.left != null) { if (temp.left == d_node) { temp.left = null; d_node = null; 반품; } else q.Enqueue(temp.left); } } } /* 이진 트리에서 요소를 삭제하는 함수 */ public static Node Deletion(Node root, int key) { if (root == null) return null; if (root.left == null && root.right == null) { if (root.data == key) return null; 그렇지 않으면 루트를 반환합니다. } 대기줄 q = 새 대기열 (); q.인큐(루트); 노드 온도 = 새 노드(0); 노드 key_node = null; // 가장 깊은 노드(temp)와 삭제할 노드(key_node)를 찾기 위해 레벨 순서 순회를 수행합니다. while (q.Count != 0) { temp = q.Dequeue(); if (temp.data == key) key_node = 임시; if (temp.left != null) q.Enqueue(temp.left); if (temp.right != null) q.Enqueue(temp.right); } if (key_node != null) { int x = temp.data; key_node.data = x; DeleteDeepest(루트, 임시); } 루트를 반환합니다; } // 중위 트리 순회(왼쪽 - 루트 - 오른쪽) public static void InorderTraversal(Node root) { if (root == null) return; InorderTraversal(root.left); Console.Write(root.data + ' '); InorderTraversal(root.right); } // 선주문 트리 순회(루트 - 왼쪽 - 오른쪽) public static void PreorderTraversal(노드 루트) { if (root == null) return; Console.Write(root.data + ' '); PreorderTraversal(root.left); PreorderTraversal(root.right); } // 후위 트리 순회(왼쪽 - 오른쪽 - 루트) public static void PostorderTraversal(Node root) { if (root == null) return; PostorderTraversal(root.left); PostorderTraversal(root.right); Console.Write(root.data + ' '); } // 레벨 순서 트리 탐색을 위한 함수 public static void LevelorderTraversal(Node root) { if (root == null) return; // 레벨 순서 순회를 위한 큐 큐 q = 새 대기열 (); q.인큐(루트); while (q.Count != 0) { 노드 임시 = q.Dequeue(); Console.Write(temp.data + ' '); // 대기열에 왼쪽 자식을 푸시합니다. if (temp.left != null) q.Enqueue(temp.left); // 대기열의 오른쪽 자식을 푸시합니다. if (temp.right != null) q.Enqueue(temp.right); } } /* 위의 알고리즘을 확인하는 드라이버 함수입니다. */ public static void Main(string[] args) { 노드 루트 = null; // 노드 삽입 root = Insert(root, 10); 루트 = 삽입(루트, 20); 루트 = 삽입(루트, 30); 루트 = 삽입(루트, 40); Console.Write('선주문 순회: '); PreorderTraversal(루트); Console.Write('
중순 순회: '); InorderTraversal(루트); Console.Write('
후순 순회: '); PostorderTraversal(루트); Console.Write('
레벨 순서 탐색: '); LevelorderTraversal(루트); // 데이터 = 20인 노드 삭제 root = Deletion(root, 20); Console.Write('
삭제 후 순회: '); InorderTraversal(루트); } } 자바스크립트 // Node class to define the structure of the node class Node { constructor(val) { this.data = val; this.left = null; this.right = null; } } // Function to insert nodes function insert(root, data) { // If tree is empty, new node becomes the root if (root === null) { root = new Node(data); return root; } // queue to traverse the tree and find the position to // insert the node const q = []; q.push(root); while (q.length !== 0) { const temp = q.shift(); // Insert node as the left child of the parent node if (temp.left === null) { temp.left = new Node(data); break; } // If the left child is not null push it to the // queue else q.push(temp.left); // Insert node as the right child of parent node if (temp.right === null) { temp.right = new Node(data); break; } // If the right child is not null push it to the // queue else q.push(temp.right); } return root; } /* function to delete the given deepest node (d_node) in binary tree */ function deletDeepest(root, d_node) { const q = []; q.push(root); // Do level order traversal until last node let temp; while (q.length !== 0) { temp = q.shift(); if (temp === d_node) { temp = null; delete d_node; return; } if (temp.right) { if (temp.right === d_node) { temp.right = null; delete d_node; return; } else q.push(temp.right); } if (temp.left) { if (temp.left === d_node) { temp.left = null; delete d_node; return; } else q.push(temp.left); } } } /* function to delete element in binary tree */ function deletion(root, key) { if (!root) return null; if (root.left === null && root.right === null) { if (root.data === key) return null; else return root; } const q = []; q.push(root); let temp; let key_node = null; // Do level order traversal to find deepest // node(temp) and node to be deleted (key_node) while (q.length !== 0) { temp = q.shift(); if (temp.data === key) key_node = temp; if (temp.left) q.push(temp.left); if (temp.right) q.push(temp.right); } if (key_node !== null) { const x = temp.data; key_node.data = x; deletDeepest(root, temp); } return root; } // Inorder tree traversal (Left - Root - Right) function inorderTraversal(root) { if (!root) return; inorderTraversal(root.left); console.log(root.data + ' '); inorderTraversal(root.right); } // Preorder tree traversal (Root - Left - Right) function preorderTraversal(root) { if (!root) return; console.log(root.data + ' '); preorderTraversal(root.left); preorderTraversal(root.right); } // Postorder tree traversal (Left - Right - Root) function postorderTraversal(root) { if (root === null) return; postorderTraversal(root.left); postorderTraversal(root.right); console.log(root.data + ' '); } // Function for Level order tree traversal function levelorderTraversal(root) { if (root === null) return; // Queue for level order traversal const q = []; q.push(root); while (q.length !== 0) { const temp = q.shift(); console.log(temp.data + ' '); // Push left child in the queue if (temp.left) q.push(temp.left); // Push right child in the queue if (temp.right) q.push(temp.right); } } /* Driver function to check the above algorithm. */ let root = null; // Insertion of nodes root = insert(root, 10); root = insert(root, 20); root = insert(root, 30); root = insert(root, 40); console.log('Preorder traversal: '); preorderTraversal(root); console.log('
Inorder traversal: '); inorderTraversal(root); console.log('
Postorder traversal: '); postorderTraversal(root); console.log('
Level order traversal: '); levelorderTraversal(root); // Delete the node with data = 20 root = deletion(root, 20); console.log('
Inorder traversal after deletion: '); inorderTraversal(root); C++14 #include using namespace std; // Node class to define the structure of the node class Node { public: int data; Node *left, *right; // Parameterized Constructor Node(int val) { data = val; left = right = NULL; } }; // Function to insert nodes Node* insert(Node* root, int data) { // If tree is empty, new node becomes the root if (root == NULL) { root = new Node(data); return root; } // queue to traverse the tree and find the position to // insert the node queue 큐; q.push(루트); while (!q.empty()) { Node* temp = q.front(); q.pop(); // 부모 노드의 왼쪽 자식으로 노드를 삽입합니다. if (temp->left == NULL) { temp->left = new Node(data); 부서지다; } // 왼쪽 자식이 null이 아니면 // 대기열에 푸시합니다. else q.push(temp->left); // 부모 노드의 오른쪽 자식으로 노드를 삽입합니다. if (temp->right == NULL) { temp->right = new Node(data); 부서지다; } // 오른쪽 자식이 null이 아니면 // 대기열에 푸시합니다. else q.push(temp->right); } 루트를 반환합니다; } /* 이진 트리에서 주어진 가장 깊은 노드(d_node)를 삭제하는 함수 */ void deletDeepest(Node* root, Node* d_node) { queue 큐; q.push(루트); // 마지막 노드까지 레벨 순서 순회를 수행합니다. Node* temp; while (!q.empty()) { temp = q.front(); q.pop(); if (temp == d_node) { temp = NULL; 삭제(d_node); 반품; } if (temp->right) { if (temp->right == d_node) { temp->right = NULL; 삭제(d_node); 반품; } else q.push(temp->right); } if (temp->left) { if (temp->left == d_node) { temp->left = NULL; 삭제(d_node); 반품; } else q.push(임시->왼쪽); } } } /* 이진 트리에서 요소를 삭제하는 함수 */ Node* 삭제(Node* root, int key) { if (!root) return NULL; if (루트->왼쪽 == NULL && 루트->오른쪽 == NULL) { if (루트->데이터 == 키) return NULL; 그렇지 않으면 루트를 반환합니다. } 대기줄 큐; q.push(루트); 노드* 온도; 노드* key_node = NULL; // 가장 깊은 노드(temp)와 삭제할 노드(key_node)를 찾기 위해 레벨 순서 순회를 수행합니다. while (!q.empty()) { temp = q.front(); q.pop(); if (temp->data == key) key_node = 임시; if (온도->왼쪽) q.push(온도->왼쪽); if (온도->오른쪽) q.push(온도->오른쪽); } if (key_node != NULL) { int x = 임시->데이터; key_node->data = x; deleteDeepest(루트, 임시); } 루트를 반환합니다; } // 중위 트리 순회(왼쪽 - 루트 - 오른쪽) void inorderTraversal(Node* root) { if (!root) return; inorderTraversal(루트->왼쪽); 시합 < < root->데이터 < < ' '; inorderTraversal(root->오른쪽); } // 선주문 트리 순회(루트 - 왼쪽 - 오른쪽) void preorderTraversal(Node* root) { if (!root) return; 시합 < < root->데이터 < < ' '; preorderTraversal(root->왼쪽); preorderTraversal(루트->오른쪽); } // 후위 트리 탐색 (왼쪽 - 오른쪽 - 루트) void postorderTraversal(Node* root) { if (root == NULL) return; postorderTraversal(루트->왼쪽); postorderTraversal(루트->오른쪽); 시합 < < root->데이터 < < ' '; } // Function for Level order tree traversal void levelorderTraversal(Node* root) { if (root == NULL) return; // Queue for level order traversal queue 큐; q.push(루트); while (!q.empty()) { Node* temp = q.front(); q.pop(); 시합 < < temp->데이터 < < ' '; // Push left child in the queue if (temp->왼쪽) q.push(임시->왼쪽); // 큐의 오른쪽 자식을 푸시합니다. if (temp->right) q.push(temp->right); } } /* 위의 알고리즘을 확인하는 드라이버 함수입니다. */ int main() { 노드* 루트 = NULL; // 노드 삽입 root = insert(root, 10); 루트 = 삽입(루트, 20); 루트 = 삽입(루트, 30); 루트 = 삽입(루트, 40); 시합 < < 'Preorder traversal: '; preorderTraversal(root); cout < < '
Inorder traversal: '; inorderTraversal(root); cout < < '
Postorder traversal: '; postorderTraversal(root); cout < < '
Level order traversal: '; levelorderTraversal(root); // Delete the node with data = 20 root = deletion(root, 20); cout < < '
Inorder traversal after deletion: '; inorderTraversal(root); } 산출
Preorder traversal: 10 20 40 30 Inorder traversal: 40 20 10 30 Postorder traversal: 40 20 30 10 Level order traversal: 10 20 30 40 Inorder traversal after deletion: 40 10 30
이진 트리 연산의 복잡성 분석
| 운영 | 시간 복잡도 | 공간 복잡도 |
|---|---|---|
| 삽입 | 에) | 에) |
| 선주문 순회 | 에) | 에) |
| 중위순회 | 에) | 에) |
| 우편 주문 순회 | 에) | 에) |
| 레벨 순서 순회 | 에) | 에) |
| 삭제 | 에) | 에) |
| 수색 | 에) | 에) |
우리는 사용할 수 있습니다 모리스 횡단 O(1) 시간 안에 이진 트리의 모든 노드를 탐색합니다.
이진 트리의 장점
- 효율적인 검색: 이진 트리는 각 노드에 최대 2개의 하위 노드가 있으므로 특정 요소를 검색할 때 효율적입니다. 메모리 효율성: 이진 트리는 다른 트리 데이터 구조에 비해 더 적은 메모리를 필요로 하므로 메모리 효율적입니다.
- 이진 트리는 각 노드에 왼쪽 자식과 오른쪽 자식, 최대 두 개의 자식이 있으므로 구현하고 이해하기가 상대적으로 쉽습니다.
이진 트리의 단점
- 제한된 구조: 이진 트리는 노드당 두 개의 하위 노드로 제한되므로 특정 애플리케이션에서의 유용성이 제한될 수 있습니다. 예를 들어 트리에 노드당 2개 이상의 하위 노드가 필요한 경우 다른 트리 구조가 더 적합할 수 있습니다.
- 불균형한 나무: 한 하위 트리가 다른 하위 트리보다 상당히 큰 불균형 이진 트리는 검색 작업이 비효율적일 수 있습니다. 이는 트리의 균형이 적절하게 유지되지 않거나 데이터가 무작위가 아닌 순서로 삽입되는 경우 발생할 수 있습니다.
- 공간 비효율성: 이진 트리는 다른 데이터 구조와 비교할 때 공간이 비효율적일 수 있습니다. 이는 각 노드에 두 개의 하위 포인터가 필요하기 때문입니다. 이는 큰 트리의 경우 상당한 양의 메모리 오버헤드가 될 수 있습니다.
- 최악의 시나리오에서 성능 저하: 최악의 시나리오에서는 이진 트리가 퇴화되거나 왜곡될 수 있습니다. 즉, 각 노드에는 자식이 하나만 있습니다. 이 경우 검색 작업은 O(n) 시간 복잡도로 저하될 수 있습니다. 여기서 n은 트리의 노드 수입니다.
이진 트리의 응용
- 이진 트리는 다음과 같은 용도로 사용할 수 있습니다. 계층적 데이터를 표현 .
- 허프만 코딩 트리가 사용됩니다. 데이터 압축 알고리즘 .
- 우선순위 대기열 O(1) 시간 복잡도에서 최대 또는 최소를 검색하는 데 사용되는 이진 트리의 또 다른 응용 프로그램입니다.
- 데이터베이스에서 분할된 인덱싱에 유용하며, 시스템에 캐시를 저장하는데 유용합니다.
- 이진 트리는 분류 및 회귀 분석에 사용되는 기계 학습 알고리즘의 일종인 의사결정 트리를 구현하는 데 사용할 수 있습니다.
이진 트리에 대해 자주 묻는 질문
1. 이진 트리란 무엇입니까?
이진 트리는 노드로 구성된 비선형 데이터 구조이며, 각 노드에는 최대 2개의 자식(왼쪽 자식과 오른쪽 자식)이 있습니다.
2. 이진 트리의 유형은 무엇입니까?
이진 트리는 완전 이진 트리, 완전 이진 트리, 완전 이진 트리, 균형 이진 트리(AVL 트리, 레드-블랙 트리 등), 퇴화(또는 병리적) 이진 트리 등 다양한 유형으로 분류될 수 있습니다.
3. 이진 트리의 높이는 얼마입니까?
이진 트리의 높이는 루트 노드에서 리프 노드까지의 가장 긴 경로의 길이입니다. 루트 노드에서 리프 노드까지 가장 긴 경로의 간선 수를 나타냅니다.
4. 이진 트리에서 노드의 깊이는 얼마입니까?
이진 트리에서 노드의 깊이는 루트 노드에서 해당 특정 노드까지의 경로 길이입니다. 루트 노드의 깊이는 0입니다.
5. 이진 트리에서 트리 순회를 어떻게 수행합니까?
이진 트리의 트리 순회는 순차 순회, 선순 순회, 후순 순회, 수준 순회(폭 우선 순회라고도 함) 등 다양한 방식으로 수행될 수 있습니다.
6. 이진 트리의 중위 순회란 무엇입니까?
중위 순회에서는 왼쪽 자식, 루트, 오른쪽 자식 순서로 노드를 재귀적으로 방문합니다. 이 순회로 인해 이진 검색 트리에서 노드가 감소하지 않는 순서로 방문됩니다.
7. 바이너리 트리의 선주문 순회란 무엇입니까?
선주문 순회에서는 루트, 왼쪽 자식, 오른쪽 자식 순서로 노드를 재귀적으로 방문합니다. 이 순회로 인해 루트 노드가 방문할 첫 번째 노드가 됩니다.
8. 이진 트리의 후위 순회란 무엇입니까?
후위 순회에서는 노드가 왼쪽 자식, 오른쪽 자식, 루트 순서로 재귀적으로 방문됩니다. 이 순회로 인해 루트 노드가 방문할 마지막 노드가 됩니다.
9. 이진 트리와 이진 검색 트리의 차이점은 무엇입니까?
이진 트리는 각 노드가 최대 2개의 자식을 갖는 계층적 데이터 구조인 반면, 이진 검색 트리는 노드의 왼쪽 자식이 노드 값보다 작은 값을 포함하고 오른쪽 자식이 값을 포함하는 이진 트리 유형입니다. 노드의 값보다 큽니다.
10. 균형 이진 트리란 무엇입니까?
균형 이진 트리는 모든 노드의 왼쪽 및 오른쪽 하위 트리의 높이가 최대 1만큼 다른 이진 트리입니다. 균형 트리는 O(log n)에 가까운 시간 복잡도로 검색, 삽입, 삭제 등의 효율적인 작업을 유지하는 데 도움이 됩니다.
결론:
트리는 계층적 데이터 구조입니다. 트리의 주요 용도에는 계층적 데이터 유지, 적당한 액세스 제공, 삽입/삭제 작업이 포함됩니다. 이진 트리는 모든 노드가 최대 2개의 자식을 갖는 트리의 특별한 경우입니다.
관련 기사:
- 이진 트리의 속성
- 이진 트리의 유형