from abc import ABC, abstractmethod from typing import Dict, List, Any, Optional, Union, Callable from docx import Document from docx.shared import Pt, Cm, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING from docx.enum.style import WD_STYLE_TYPE from docx.oxml.ns import qn from docx.oxml import OxmlElement import re class GostDocumentConfig: """Конфигурация параметров документа по ГОСТ""" def __init__(self, **kwargs): # Поля документа (в сантиметрах) self.margins = kwargs.get('margins', { 'top': 2.0, 'bottom': 2.0, 'left': 3.0, 'right': 1.5 }) # Шрифты self.font = kwargs.get('font', { 'name': 'Times New Roman', 'size': 14, 'main_language': 'ru-RU' }) # Межстрочный интервал self.line_spacing = kwargs.get('line_spacing', 1.5) # Отступы для заголовков self.heading_spacing = kwargs.get('heading_spacing', { 1: {'before': 24, 'after': 12}, 2: {'before': 24, 'after': 6}, 3: {'before': 18, 'after': 6} }) # Стили заголовков self.heading_styles = kwargs.get('heading_styles', { 1: {'size': 16, 'bold': True, 'all_caps': True, 'align': 'center'}, 2: {'size': 14, 'bold': True, 'all_caps': False, 'align': 'left'}, 3: {'size': 14, 'bold': True, 'italic': True, 'all_caps': False, 'align': 'left'} }) # Стиль абзаца self.paragraph = kwargs.get('paragraph', { 'first_line_indent': 1.25, # в см 'alignment': 'justify' }) # Стиль таблиц self.table = kwargs.get('table', { 'font_size': 12, 'header_bold': True }) # Стиль подписей self.caption = kwargs.get('caption', { 'font_size': 12, 'italic': True, 'align': 'center' }) class GostDocumentCore(ABC): """Базовый класс для создания документов по ГОСТ с возможностью расширения""" def __init__(self, config: Optional[GostDocumentConfig] = None, title: str = "", author: str = ""): """ Инициализация документа с настройками ГОСТ Args: config: Конфигурация документа (если None - используются стандартные настройки ГОСТ) title: Заголовок документа author: Автор документа """ self.config = config or GostDocumentConfig() self.title = title self.author = author self.document = Document() self._element_processors = {} self._setup_document() self._create_gost_styles() # Регистрируем базовые процессоры элементов self.register_processor('heading', self._process_heading) self.register_processor('paragraph', self._process_paragraph) self.register_processor('table', self._process_table) self.register_processor('list', self._process_list) self.register_processor('image', self._process_image) def _setup_document(self): """Настройка базовых параметров документа согласно ГОСТ""" # Настройка полей страницы sections = self.document.sections for section in sections: section.top_margin = Cm(self.config.margins['top']) section.bottom_margin = Cm(self.config.margins['bottom']) section.left_margin = Cm(self.config.margins['left']) section.right_margin = Cm(self.config.margins['right']) # Установка стандартного стиля для всего документа style = self.document.styles['Normal'] font = style.font font.name = self.config.font['name'] font.size = Pt(self.config.font['size']) # Установка кириллического шрифта font.element.rPr.rFonts.set(qn('w:eastAsia'), self.config.font['name']) # Настройка формата абзаца paragraph_format = style.paragraph_format paragraph_format.line_spacing_rule = ( WD_LINE_SPACING.ONE_POINT_FIVE if self.config.line_spacing == 1.5 else WD_LINE_SPACING.DOUBLE if self.config.line_spacing == 2.0 else WD_LINE_SPACING.EXACTLY ) paragraph_format.alignment = ( WD_ALIGN_PARAGRAPH.JUSTIFY if self.config.paragraph['alignment'] == 'justify' else WD_ALIGN_PARAGRAPH.LEFT if self.config.paragraph['alignment'] == 'left' else WD_ALIGN_PARAGRAPH.CENTER ) paragraph_format.first_line_indent = Cm(self.config.paragraph['first_line_indent']) def _create_gost_styles(self): """Создание стилей заголовков и других элементов в соответствии с ГОСТ""" # Заголовок 1 уровня self._create_heading_style(1) # Заголовок 2 уровня self._create_heading_style(2) # Заголовок 3 уровня self._create_heading_style(3) # Стиль для списка if 'List GOST' not in self.document.styles: list_style = self.document.styles.add_style('List GOST', WD_STYLE_TYPE.PARAGRAPH) list_style.base_style = self.document.styles['List'] list_style.font.name = self.config.font['name'] list_style.font.size = Pt(self.config.font['size']) list_style.paragraph_format.left_indent = Cm(1.25) list_style.paragraph_format.space_before = Pt(0) list_style.paragraph_format.space_after = Pt(6) list_style.paragraph_format.line_spacing_rule = ( WD_LINE_SPACING.ONE_POINT_FIVE if self.config.line_spacing == 1.5 else WD_LINE_SPACING.DOUBLE ) # Стиль для подписей таблиц и рисунков if 'Caption GOST' not in self.document.styles: caption_style = self.document.styles.add_style('Caption GOST', WD_STYLE_TYPE.PARAGRAPH) caption_style.base_style = self.document.styles['Caption'] caption_style.font.name = self.config.font['name'] caption_style.font.size = Pt(self.config.caption['font_size']) caption_style.font.italic = self.config.caption['italic'] caption_style.paragraph_format.alignment = ( WD_ALIGN_PARAGRAPH.CENTER if self.config.caption['align'] == 'center' else WD_ALIGN_PARAGRAPH.LEFT ) caption_style.paragraph_format.space_before = Pt(6) caption_style.paragraph_format.space_after = Pt(12) def _create_heading_style(self, level: int): """Создает стиль заголовка заданного уровня""" style_name = f'Heading {level} GOST' if style_name in self.document.styles: return style = self.document.styles.add_style(style_name, WD_STYLE_TYPE.PARAGRAPH) # Настройка шрифта heading_config = self.config.heading_styles[level] style.font.name = self.config.font['name'] style.font.size = Pt(heading_config['size']) style.font.bold = heading_config['bold'] style.font.italic = heading_config.get('italic', False) style.font.all_caps = heading_config['all_caps'] # Настройка абзаца spacing = self.config.heading_spacing[level] style.paragraph_format.space_before = Pt(spacing['before']) style.paragraph_format.space_after = Pt(spacing['after']) style.paragraph_format.line_spacing_rule = ( WD_LINE_SPACING.ONE_POINT_FIVE if self.config.line_spacing == 1.5 else WD_LINE_SPACING.DOUBLE ) # Выравнивание if heading_config['align'] == 'center': style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER elif heading_config['align'] == 'left': style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT style.paragraph_format.first_line_indent = Cm(0) # Без красной строки для заголовков def register_processor(self, element_type: str, processor: Callable): """ Регистрирует обработчик для определенного типа элемента Args: element_type: Тип элемента (например, 'heading', 'table') processor: Функция-обработчик """ self._element_processors[element_type] = processor def get_processor(self, element_type: str) -> Optional[Callable]: """ Возвращает обработчик для указанного типа элемента Args: element_type: Тип элемента Returns: Обработчик или None, если не найден """ return self._element_processors.get(element_type) def _process_heading(self, element: Dict[str, Any]) -> None: """Обрабатывает элемент заголовка""" level = element.get('level', 1) text = element.get('text', '') heading = self.document.add_heading(level=0) style_name = f'Heading {level} GOST' if style_name in self.document.styles: heading.style = style_name else: # Базовая настройка, если стиль не создан heading.alignment = WD_ALIGN_PARAGRAPH.CENTER if level == 1 else WD_ALIGN_PARAGRAPH.LEFT run = heading.add_run(text) run.font.size = Pt(16 if level == 1 else 14) run.font.bold = True # Добавляем текст heading.add_run(text) def _process_paragraph(self, element: Dict[str, Any]) -> None: """Обрабатывает элемент абзаца""" text = element.get('text', '') style = element.get('style') p = self.document.add_paragraph() if style and style in self.document.styles: p.style = style # Основной текст по ГОСТ p.paragraph_format.line_spacing_rule = ( WD_LINE_SPACING.ONE_POINT_FIVE if self.config.line_spacing == 1.5 else WD_LINE_SPACING.DOUBLE ) p.paragraph_format.alignment = ( WD_ALIGN_PARAGRAPH.JUSTIFY if self.config.paragraph['alignment'] == 'justify' else WD_ALIGN_PARAGRAPH.LEFT ) p.paragraph_format.first_line_indent = Cm(self.config.paragraph['first_line_indent']) print(text) # Добавляем текст p.add_run(text) def _process_table(self, element: Dict[str, Any]) -> None: """Обрабатывает элемент таблицы""" data = element.get('data', []) headers = element.get('headers') # Добавляем подпись таблицы caption = self.document.add_paragraph() caption.style = 'Caption GOST' caption.add_run("Таблица ").bold = True # Создаем таблицу rows = len(data) + (1 if headers else 0) cols = len(data[0]) if data else 0 if cols == 0: return table = self.document.add_table(rows=rows, cols=cols) table.style = 'Table Grid' # Заполняем заголовки, если они есть if headers: hdr_cells = table.rows[0].cells for i, header in enumerate(headers): hdr_cells[i].text = header for paragraph in hdr_cells[i].paragraphs: for run in paragraph.runs: run.font.bold = self.config.table['header_bold'] # Заполняем данные start_row = 1 if headers else 0 for i, row_data in enumerate(data): row_cells = table.rows[i + start_row].cells for j, cell_data in enumerate(row_data): row_cells[j].text = str(cell_data) # Настройка шрифта для всех ячеек for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: for run in paragraph.runs: run.font.name = self.config.font['name'] run.font.size = Pt(self.config.table['font_size']) def _process_list(self, element: Dict[str, Any]) -> None: """Обрабатывает элемент списка""" items = element.get('items', []) list_type = element.get('type', 'unordered') # 'unordered' или 'ordered' for item in items: p = self.document.add_paragraph(style='List GOST') p.paragraph_format.left_indent = Cm(1.25) p.paragraph_format.first_line_indent = Cm(-0.63) if list_type == 'ordered' else Cm(0) if list_type == 'ordered': # Добавляем номер вручную для упорядоченного списка p.add_run(f"{items.index(item) + 1}. ").bold = True p.add_run(item) def _process_image(self, element: Dict[str, Any]) -> None: """Обрабатывает элемент изображения (заглушка для будущей реализации)""" # В реальной реализации здесь будет добавление изображения path = element.get('path', '') width = element.get('width', None) # Добавляем подпись caption = self.document.add_paragraph() caption.style = 'Caption GOST' caption.add_run("Рисунок ").bold = True # В реальной реализации здесь будет добавление изображения # if path and os.path.exists(path): # if width: # self.document.add_picture(path, width=Cm(width)) # else: # self.document.add_picture(path) @abstractmethod def add_element(self, element_type: str, **kwargs) -> None: """ Абстрактный метод для добавления элемента в документ. Должен быть реализован в дочерних классах. Args: element_type: Тип элемента (heading, paragraph, table и т.д.) **kwargs: Параметры элемента """ pass def save(self, filename: str) -> None: """ Сохраняет документ в файл Args: filename: Имя файла для сохранения """ # Добавляем настройки совместимости try: self.document.settings.element.xpath('./w:compat')[0].clear() except (IndexError, AttributeError): pass self.document.save(filename) def create_title_page(self) -> None: """Создает титульную страницу документа""" # Добавляем отступ сверху for _ in range(5): self.document.add_paragraph() # Заголовок title_paragraph = self.document.add_heading(level=0) title_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER title_run = title_paragraph.add_run(self.title) title_run.font.size = Pt(16) title_run.font.bold = True title_run.font.name = self.config.font['name'] # Добавляем отступ self.document.add_paragraph() # Автор author_paragraph = self.document.add_paragraph() author_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER author_run = author_paragraph.add_run(self.author) author_run.font.size = Pt(self.config.font['size']) author_run.font.name = self.config.font['name'] # Добавляем отступ снизу for _ in range(10): self.document.add_paragraph() # Дата date_paragraph = self.document.add_paragraph() date_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER date_run = date_paragraph.add_run("2023") date_run.font.size = Pt(self.config.font['size']) date_run.font.name = self.config.font['name'] # Устанавливаем титульную страницу как отдельный раздел self.document.sections[0].start_type = 2 # Новая страница class MarkdownGostDocument(GostDocumentCore): """ Расширенная реализация для обработки Markdown и преобразования в документ по ГОСТ """ def __init__(self, config: Optional[GostDocumentConfig] = None, title: str = "", author: str = ""): super().__init__(config, title, author) self._markdown_processors = {} self._setup_markdown_processors() def _setup_markdown_processors(self): """Настраивает обработчики для различных элементов Markdown""" # Заголовки self.register_markdown_processor(r'#{1,6}\s+(.+)', self._process_md_heading) # Обычный текст self.register_markdown_processor(r'^(?!#{1,6}\s+)(?!-{3,})(?!#{1,6}$)(?!```)(?!!\[.*\]\(.*\)).+', self._process_md_paragraph) # Горизонтальная линия self.register_markdown_processor(r'^-{3,}$', self._process_md_horizontal_rule) # Списки self.register_markdown_processor(r'^(\*|\-|\d+\.)\s+(.+)$', self._process_md_list) # Таблицы (упрощенная обработка) self.register_markdown_processor(r'^\|.*\|$', self._process_md_table) # Изображения self.register_markdown_processor(r'^!\[(.*)\]\((.*)\)$', self._process_md_image) # Код self.register_markdown_processor(r'^```(.*)$', self._process_md_code) def register_markdown_processor(self, pattern: str, processor: Callable): """ Регистрирует обработчик для определенного паттерна Markdown Args: pattern: Регулярное выражение для паттерна processor: Функция-обработчик """ self._markdown_processors[pattern] = processor def _process_md_heading(self, match: re.Match, line: str) -> Dict[str, Any]: """Обрабатывает заголовок Markdown""" heading_text = match.group(1).strip() level = line.count('#') # Ограничиваем уровень заголовка от 1 до 3 (согласно ГОСТ) level = min(max(level, 1), 3) return {'type': 'heading', 'level': level, 'text': heading_text} def _process_md_paragraph(self, match: re.Match, line: str) -> Dict[str, Any]: """Обрабатывает обычный текст Markdown""" return {'type': 'paragraph', 'text': line.strip()} def _process_md_horizontal_rule(self, match: re.Match, line: str) -> Dict[str, Any]: """Обрабатывает горизонтальную линию Markdown""" # В ГОСТ горизонтальные линии не используются, поэтому просто пропускаем return None def _process_md_list(self, match: re.Match, line: str) -> Dict[str, Any]: """Обрабатывает список Markdown""" items = [line.strip()] list_type = 'ordered' if re.match(r'^\d+\.', line) else 'unordered' return {'type': 'list', 'items': items, 'type': list_type} def _process_md_table(self, match: re.Match, line: str) -> Dict[str, Any]: """Обрабатывает таблицу Markdown (заглушка)""" # Полная обработка таблиц требует анализа нескольких строк return None # Будет обработано в полной реализации def _process_md_image(self, match: re.Match, line: str) -> Dict[str, Any]: """Обрабатывает изображение Markdown""" alt_text = match.group(1) image_path = match.group(2) return {'type': 'image', 'path': image_path, 'alt': alt_text} def _process_md_code(self, match: re.Match, line: str) -> Dict[str, Any]: """Обрабатывает блок кода Markdown""" # Код в ГОСТ обычно оформляется как отдельный раздел или сноска return {'type': 'paragraph', 'text': f"Код: {line}", 'style': 'Code'} def add_element(self, element_type: str, **kwargs) -> None: """ Добавляет элемент в документ согласно ГОСТ Args: element_type: Тип элемента (heading, paragraph, table и т.д.) **kwargs: Параметры элемента """ processor = self.get_processor(element_type) if processor: element = {'type': element_type, **kwargs} processor(element) def process_markdown(self, markdown_text: str) -> None: """ Обрабатывает Markdown текст и добавляет элементы в документ Args: markdown_text: Текст в формате Markdown """ lines = markdown_text.split('\n') i = 0 while i < len(lines): line = lines[i].strip() if not line: i += 1 continue processed = False for pattern, processor in self._markdown_processors.items(): match = re.match(pattern, line) if match: element = processor(match, line) if element: self.add_element(element['type'], **{k: v for k, v in element.items() if k != 'type'}) processed = True break if not processed: # Если строка не соответствует ни одному паттерну, обрабатываем как обычный текст self.add_element('paragraph', text=line) i += 1 def process_markdown_file(self, file_path: str) -> None: """ Обрабатывает файл Markdown и добавляет элементы в документ Args: file_path: Путь к файлу Markdown """ with open(file_path, 'r', encoding='utf-8') as f: markdown_text = f.read() self.process_markdown(markdown_text) # Пример использования if __name__ == "__main__": # Создаем кастомную конфигурацию (можно оставить стандартную) custom_config = GostDocumentConfig( margins={ 'top': 2.0, 'bottom': 2.0, 'left': 3.0, 'right': 1.5 }, font={ 'name': 'Times New Roman', 'size': 14 }, line_spacing=1.5, heading_spacing={ 1: {'before': 24, 'after': 12}, 2: {'before': 24, 'after': 6}, 3: {'before': 18, 'after': 6} }, heading_styles={ 1: {'size': 16, 'bold': True, 'all_caps': True, 'align': 'center'}, 2: {'size': 14, 'bold': True, 'all_caps': False, 'align': 'left'}, 3: {'size': 14, 'bold': True, 'italic': True, 'all_caps': False, 'align': 'left'} } ) # Создаем документ с поддержкой Markdown doc = MarkdownGostDocument( config=custom_config, title="Отчет о научно-исследовательской работе", author="Иванов И.И., группа 123" ) # Добавляем титульную страницу doc.create_title_page() # Пример обработки Markdown markdown_example = """ ## Практическая работа 6 (часть 2) **Тема. Нелинейные структуры данных. Бинарное дерево.** --- ### 1. Условие задания и варианта **Вариант 27** **Значение информационной части:** Содержит символьное значение и счетчик (подсчитывает, сколько раз в дерево вставлялся символ). **Операции варианта:** 1. Модифицировать алгоритм вставки ключа в дерево в соответствии со структурой узла. 2. Определить символ, который встретился большее количество раз. 3. Определить количество цифр в дереве, используя алгоритм обхода в ширину. --- ### 2. Свойства дерева варианта - **Тип дерева:** Бинарное дерево поиска (БДП) без операции удаления. - **Структура узла:** ```cpp struct Node { char symbol; // Символ int count; // Счетчик вхождений Node* left; // Указатель на левое поддерево Node* right; // Указатель на правое поддерево }; ``` - **Особенности:** - При вставке символа: - Если символ уже существует в дереве, увеличивается его `count`. - Иначе создается новый узел с `count = 1`. - Упорядочение узлов: По ASCII-коду символов (меньшие символы — слева, большие — справа). - Операция удаления не реализована (требования варианта 24–30). --- ### 3. Тестовые примеры #### Пример 1: Вставка символов `a`, `a`, `b`, `3`, `3`, `3` **Структура дерева:** ``` b(1) a(2) 3(3) ``` **Пояснение:** - `a` вставлен 2 раза → `count = 2`. - `3` вставлен 3 раза → `count = 3` (самый частый символ). - Количество цифр: 3 (только символ `3`). #### Пример 2: Вставка символов `5`, `5`, `a`, `5` **Структура дерева:** ``` a(1) 5(3) ``` **Пояснение:** - `5` вставлен 3 раза → `count = 3` (самый частый символ). - Количество цифр: 3. --- ### 4. Описание функций #### 4.1. Модифицированная вставка символа **Задача:** Добавить символ в БДП, увеличивая счетчик при совпадении. **Предусловие:** Дерево инициализировано. **Постусловие:** Символ добавлен или обновлен в дереве. **Прототип:** ```cpp void BinaryTree::insert(char symbol); ``` **Алгоритм:** 1. Начать с корня. 2. Пока текущий узел не `nullptr`: - Если символ совпадает с текущим узлом → увеличить `count` и завершить. - Если символ меньше текущего → перейти в левое поддерево. - Иначе → перейти в правое поддерево. 3. Если узел не найден → создать новый узел с `count = 1`. --- #### 4.2. Поиск самого частого символа **Задача:** Найти символ с максимальным значением `count`. **Предусловие:** Дерево не пустое. **Постусловие:** Возвращен символ и его счетчик. **Прототип:** ```cpp std::pair BinaryTree::findMostFrequent(); ``` **Алгоритм:** 1. Инициализировать `maxCount = 0`, `maxSymbol = '\\0'`. 2. Обойти все узлы (инордер-обход): - Для каждого узла сравнить `count` с `maxCount`. - Если `count > maxCount` → обновить `maxSymbol` и `maxCount`. 3. Вернуть пару `(maxSymbol, maxCount)`. --- #### 4.3. Подсчет цифр в дереве (обход в ширину) **Задача:** Посчитать общее количество вхождений цифровых символов (`0`–`9`). **Предусловие:** Дерево инициализировано. **Постусловие:** Возвращено число — сумма `count` для всех цифровых символов. **Прототип:** ```cpp int BinaryTree::countDigits(); ``` **Алгоритм:** 1. Инициализировать очередь и счетчик `total = 0`. 2. Поместить корень в очередь. 3. Пока очередь не пуста: - Извлечь узел из очереди. - Если символ узла — цифра (`'0'`–`'9'`) → добавить `count` к `total`. - Поместить левый и правый дочерние узлы в очередь. 4. Вернуть `total`. --- #### 4.4. Отображение дерева **Задача:** Вывести структуру дерева на экран (повернутое на 90°). **Предусловие:** Дерево инициализировано. **Постусловие:** Дерево отображено в консоли. **Прототип:** ```cpp void BinaryTree::displayTree(); void BinaryTree::printTree(Node* node, int space); ``` **Алгоритм:** 1. Рекурсивно обойти дерево в порядке: **правое поддерево → корень → левое поддерево**. 2. Для каждого уровня увеличивать отступ (на 10 пробелов). 3. Вывести символ и `count` с текущим отступом. --- ### 5. Код программы ```cpp #include #include #include using namespace std; struct Node { char symbol; int count; Node* left; Node* right; Node(char s) : symbol(s), count(1), left(nullptr), right(nullptr) {} }; class BinaryTree { private: Node* root; void printTree(Node* node, int space) { if (node == nullptr) return; space += 10; printTree(node->right, space); cout << endl; for (int i = 10; i < space; i++) cout << " "; cout << node->symbol << "(" << node->count << ")"; printTree(node->left, space); } public: BinaryTree() : root(nullptr) {} void insert(char symbol) { Node** current = &root; while (*current != nullptr) { if (symbol == (*current)->symbol) { (*current)->count++; return; } else if (symbol < (*current)->symbol) { current = &((*current)->left); } else { current = &((*current)->right); } } *current = new Node(symbol); } pair findMostFrequent() { if (root == nullptr) return make_pair('\\0', 0); char maxSymbol = '\\0'; int maxCount = 0; stack s; Node* current = root; while (current != nullptr || !s.empty()) { while (current != nullptr) { s.push(current); current = current->left; } current = s.top(); s.pop(); if (current->count > maxCount) { maxCount = current->count; maxSymbol = current->symbol; } current = current->right; } return make_pair(maxSymbol, maxCount); } int countDigits() { if (root == nullptr) return 0; int total = 0; queue q; q.push(root); while (!q.empty()) { Node* current = q.front(); q.pop(); if (current->symbol >= '0' && current->symbol <= '9') { total += current->count; } if (current->left) q.push(current->left); if (current->right) q.push(current->right); } return total; } void displayTree() { printTree(root, 0); cout << "\n\n"; } }; int main() { BinaryTree tree; int choice; char symbol; while (true) { cout << "\n1. Insert symbol\n"; cout << "2. Display tree\n"; cout << "3. Find most frequent symbol\n"; cout << "4. Count digits\n"; cout << "5. Exit\n"; cout << "Enter choice: "; cin >> choice; switch (choice) { case 1: cout << "Enter symbol: "; cin >> symbol; tree.insert(symbol); break; case 2: cout << "Tree structure:\n"; tree.displayTree(); break; case 3: { auto result = tree.findMostFrequent(); if (result.second == 0) { cout << "Tree is empty.\n"; } else { cout << "Most frequent symbol: " << result.first << " (count: " << result.second << ")\n"; } break; } case 4: cout << "Number of digits: " << tree.countDigits() << endl; break; case 5: return 0; default: cout << "Invalid choice.\n"; } } } ``` --- ### 6. Скриншоты тестирования #### Тест 1: Вставка `a`, `a`, `b`, `3`, `3`, `3` ![Тест 1](https://i.imgur.com/1aXJz9l.png) - **Вывод дерева:** Корректно отображена структура с `3(3)` как самым частым символом. - **Количество цифр:** 3 (символ `3` вставлен 3 раза). #### Тест 2: Вставка `5`, `5`, `a`, `5` ![Тест 2](https://i.imgur.com/5GkR7yP.png) - **Самый частый символ:** `5` (count=3). - **Количество цифр:** 3. --- ### 7. Ответы на контрольные вопросы 1. **Степень дерева** определяется максимальным количеством дочерних узлов у любого узла. 2. **Сильноветвящееся дерево** — дерево, где степень каждого узла равна максимальной степени дерева. 3. **Путь в дереве** — последовательность узлов от корня к целевому узлу. 4. **Длина пути** — количество ребер в пути (на 1 меньше количества узлов). 5. **Степень бинарного дерева** равна 2 (максимум 2 дочерних узла на узел). 6. **Пустое дерево** допустимо (корень = `nullptr`). 7. **Бинарное дерево** — дерево, где каждый узел имеет не более 2 дочерних узлов. 8. **Алгоритм обхода** используется для последовательного доступа ко всем узлам. 9. **Высота дерева:** ``` height(nullptr) = -1 height(node) = 1 + max(height(node->left), height(node->right)) ``` 10. **Дерево по таблице:** ![Дерево](https://i.imgur.com/mVcRf0D.png) 11. **Порядки обхода:** - Прямой (КЛП): 18 → 12 → 7 → 4 → 10 → 2 → 5 → 21 → 15 → 14 - Симметричный (ЛКП): 7 → 12 → 2 → 10 → 5 → 18 → 21 → 4 → 14 → 15 - Обратный (ЛПК): 7 → 2 → 5 → 10 → 12 → 21 → 4 → 15 → 14 → 18 12. **Обход в ширину** использует **очередь**. 13. **Пример обхода в ширину:** 18 → 12 → 14 → 7 → 10 → 15 → 4 → 2 → 5 → 21. 14. **Не рекурсивный обход в глубину** использует **стек**. 15. **Обход дерева выражения `(5+3)*((1+2)/3)`:** - Прямой: `* + 5 3 / + 1 2 3` - Симметричный: `5 + 3 * 1 + 2 / 3` - Обратный: `5 3 + 1 2 + 3 / *` 16. **Деревья выражений:** - `a+b-c*d+e` → `+ - + a b * c d e` - `/a-b*c d` → Ошибка в выражении (некорректный формат). - `a b c d / - *` → `* a - b / c d` - `* - / + a b c d e` → `* - / + a b c d e` 17. **Обход в ширину со стеком** даст порядок, аналогичный **обратному обходу в глубину**. 18. **БДП для симметричного обхода `40 45 46 50 65 70 75`:** ![БДП](https://i.imgur.com/3kLmF9A.png) 19. **Исходное дерево для прямого обхода `50 45 35 15 40 46 65 75 70`:** ![Дерево](https://i.imgur.com/8W0jKQv.png) 20. **БДП после вставки `1, 48, 75, 100`:** ![Дерево](https://i.imgur.com/5GkR7yP.png) --- **Вывод:** Реализованы все операции для бинарного дерева поиска с модифицированной структурой узла. Программа корректно обрабатывает вставку, поиск самого частого символа и подсчет цифр через обход в ширину. Тесты подтверждают работоспособность алгоритмов.. """ # Обрабатываем Markdown doc.process_markdown(markdown_example) # Сохраняем документ doc.save("./asdotchet_po_GOST.docx") print("Документ успешно создан и сохранен как 'otchet_po_GOST.docx'")