Solingen

SAKOD_lab3.py

Mar 12th, 2025 (edited)
41
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.43 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # \author: Артюхин Матфей
  3. # \date_start: 2025-03-10
  4. # \date_end: 2025-03-12
  5.  
  6. # Класс Node - элемент двунаправленного списка
  7. class Node:
  8.     """
  9.    \brief Класс Node представляет элемент двунаправленного списка.
  10.  
  11.    \param data [in]       - данные элемента (числовые, строковые, даты и т.п.)
  12.    \param p_next [in/out] - ссылка на следующий элемент списка
  13.    \param p_prev [in/out] - ссылка на предыдущий элемент списка
  14.    """
  15.     def __init__(self, data):
  16.         # Инициализация данных элемента
  17.         self.data = data  # данные элемента
  18.         self.p_next = None  # указатель на следующий элемент
  19.         self.p_prev = None  # указатель на предыдущий элемент
  20.  
  21. # Класс DoublyLinkedList - реализация двунаправленного списка
  22. class DoublyLinkedList:
  23.     """
  24.    \brief Класс DoublyLinkedList реализует двунаправленный список с операциями добавления,
  25.           удаления, печати и поиска k-й порядковой статистики.
  26.  
  27.    \param p_first [in/out] - указатель на первый элемент списка
  28.    \param p_cur [in/out]   - указатель на текущий элемент списка (для удобства добавления)
  29.    """
  30.     def __init__(self):
  31.         # Инициализация указателей списка
  32.         self.p_first = None  # указатель на первый элемент списка
  33.         self.p_cur = None    # указатель на текущий элемент списка
  34.  
  35.     def add(self, item):
  36.         """
  37.        \brief Функция добавления элемента в список.
  38.  
  39.        \param item [in] - значение добавляемого элемента.
  40.        \retval None
  41.        """
  42.         # Создаем новый элемент списка с заданным значением
  43.         p_tmp = Node(item)
  44.         # Если список пуст, новый элемент становится первым
  45.         if self.p_first is None:
  46.             self.p_first = p_tmp  # устанавливаем p_first
  47.             self.p_cur = p_tmp    # устанавливаем p_cur
  48.             # Организуем циклическую связь для одиночного элемента
  49.             p_tmp.p_next = p_tmp  # ссылка на самого себя как на следующий элемент
  50.             p_tmp.p_prev = p_tmp  # ссылка на самого себя как на предыдущий элемент
  51.         else:
  52.             # Добавление нового элемента после текущего элемента p_cur
  53.             p_tmp.p_next = self.p_cur.p_next  # назначаем ссылку на следующий элемент
  54.             p_tmp.p_prev = self.p_cur         # назначаем ссылку на предыдущий элемент
  55.             self.p_cur.p_next.p_prev = p_tmp  # обновляем ссылку предыдущего элемента
  56.             self.p_cur.p_next = p_tmp         # обновляем ссылку следующего элемента
  57.             self.p_cur = p_tmp                # перемещаем указатель p_cur
  58.         # Конец функции add
  59.  
  60.     def remove(self, item):
  61.         """
  62.        \brief Функция удаления первого найденного элемента из списка по значению.
  63.  
  64.        \param item [in] - значение удаляемого элемента.
  65.        \retval None
  66.        """
  67.         # Если список пуст, удалять нечего
  68.         if self.p_first is None:
  69.             return
  70.  
  71.         p_tmp = self.p_first  # начинаем поиск с первого элемента
  72.         while True:
  73.             if p_tmp.data == item:
  74.                 if p_tmp.p_next == p_tmp:  # единственный элемент
  75.                     self.p_first = None
  76.                     self.p_cur = None
  77.                 else:
  78.                     if p_tmp == self.p_first:  # удаление первого элемента
  79.                         self.p_first = p_tmp.p_next
  80.                     p_tmp.p_prev.p_next = p_tmp.p_next
  81.                     p_tmp.p_next.p_prev = p_tmp.p_prev
  82.                     if p_tmp == self.p_cur:
  83.                         self.p_cur = p_tmp.p_prev
  84.                 return
  85.             p_tmp = p_tmp.p_next
  86.             if p_tmp == self.p_first:  # элемент не найден
  87.                 break
  88.         # Конец функции remove
  89.  
  90.     def print_list(self):
  91.         """
  92.        \brief Функция печати элементов списка на экран.
  93.  
  94.        \retval None
  95.        """
  96.         if self.p_first is None:
  97.             print("Список пуст")
  98.             return
  99.         print("Элементы списка:", end=" ")
  100.         p_tmp = self.p_first
  101.         print(p_tmp.data, end=" ")
  102.         p_tmp = p_tmp.p_next
  103.         while p_tmp != self.p_first:
  104.             print(p_tmp.data, end=" ")
  105.             p_tmp = p_tmp.p_next
  106.         print()
  107.         # Конец функции print_list
  108.  
  109.     def get_list_as_array(self):
  110.         """
  111.        \brief Функция преобразования списка в массив для сортировки или поиска.
  112.  
  113.        \retval list - массив значений элементов списка
  114.        """
  115.         if self.p_first is None:
  116.             return []
  117.         result = []
  118.         p_tmp = self.p_first
  119.         result.append(p_tmp.data)
  120.         p_tmp = p_tmp.p_next
  121.         while p_tmp != self.p_first:
  122.             result.append(p_tmp.data)
  123.             p_tmp = p_tmp.p_next
  124.         return result
  125.  
  126.     def order_statistic(self, k):
  127.         """
  128.        \brief Функция поиска k-й порядковой статистики с помощью алгоритма QuickSelect.
  129.  
  130.        \param k [in] - порядковый номер искомого элемента (нумерация с 0).
  131.        \retval tuple - (значение k-й статистики, количество перестановок) или (None, 0), если k некорректен
  132.        """
  133.         # Получаем массив элементов списка
  134.         arr = self.get_list_as_array()
  135.         if not arr or k < 0 or k >= len(arr):
  136.             return None, 0
  137.  
  138.         # Счетчик перестановок
  139.         swaps = [0]  # Используем список, чтобы изменять значение внутри вложенной функции
  140.  
  141.         def swap(arr, i, j):
  142.             """Вспомогательная функция для перестановки элементов"""
  143.             arr[i], arr[j] = arr[j], arr[i]
  144.             swaps[0] += 1
  145.  
  146.         def partition(arr, left, right):
  147.             """
  148.            \brief Разделение массива относительно опорного элемента.
  149.  
  150.            \param arr [in/out] - массив для разделения
  151.            \param left [in] - левая граница
  152.            \param right [in] - правая граница
  153.            \retval int - позиция опорного элемента после разделения
  154.            """
  155.             pivot = arr[left]  # Опорный элемент - первый в подмассиве
  156.             i = left
  157.             j = right
  158.             while i <= j:
  159.                 while arr[i] < pivot:
  160.                     i += 1
  161.                 while arr[j] > pivot:
  162.                     j -= 1
  163.                 if i <= j:
  164.                     swap(arr, i, j)
  165.                     i += 1
  166.                     j -= 1
  167.             return i - 1
  168.  
  169.         def quick_select(arr, left, right, k):
  170.             """
  171.            \brief Рекурсивный алгоритм QuickSelect для поиска k-й статистики.
  172.  
  173.            \param arr [in/out] - массив для поиска
  174.            \param left [in] - левая граница
  175.            \param right [in] - правая граница
  176.            \param k [in] - искомый индекс
  177.            \retval int - значение k-й статистики
  178.            """
  179.             if left == right:
  180.                 return arr[left]
  181.             pivot_idx = partition(arr, left, right)
  182.             if k == pivot_idx:
  183.                 return arr[k]
  184.             elif k < pivot_idx:
  185.                 return quick_select(arr, left, pivot_idx - 1, k)
  186.             else:
  187.                 return quick_select(arr, pivot_idx + 1, right, k)
  188.  
  189.         # Запускаем QuickSelect и возвращаем результат с количеством перестановок
  190.         result = quick_select(arr, 0, len(arr) - 1, k)
  191.         return result, swaps[0]
  192.  
  193. def main():
  194.     """
  195.    \brief Основная функция программы, реализующая меню для работы со списком.
  196.  
  197.    \retval None
  198.    """
  199.     pList = DoublyLinkedList()
  200.  
  201.     while True:
  202.         print("\nПожалуйста, введите номер команды:")
  203.         print("0 - Выход")
  204.         print("1 - Добавить элемент")
  205.         print("2 - Удалить элемент")
  206.         print("3 - Напечатать список")
  207.         print("4 - Найти k-ю порядковую статистику")
  208.         try:
  209.             menuPos = int(input("Введите команду: "))
  210.         except ValueError:
  211.             print("Некорректный ввод. Попробуйте снова.")
  212.             continue
  213.  
  214.         if menuPos == 0:
  215.             print("... Завершение работы ...")
  216.             break
  217.         elif menuPos == 1:
  218.             data = input("-- Введите значение элемента (добавление): ")
  219.             try:
  220.                 data = int(data)
  221.             except ValueError:
  222.                 pass
  223.             pList.add(data)
  224.         elif menuPos == 2:
  225.             data = input("-- Введите значение элемента (удаление): ")
  226.             try:
  227.                 data = int(data)
  228.             except ValueError:
  229.                 pass
  230.             pList.remove(data)
  231.         elif menuPos == 3:
  232.             pList.print_list()
  233.         elif menuPos == 4:
  234.             try:
  235.                 k = int(input("-- Введите номер k (нумерация с 0): "))
  236.                 result, swaps = pList.order_statistic(k)
  237.                 if result is not None:
  238.                     print(f"k-я статистика (k={k}): {result}")
  239.                     print(f"Количество перестановок: {swaps}")
  240.                 else:
  241.                     print(f"k={k} некорректен или список пуст")
  242.             except ValueError:
  243.                 print("Некорректный ввод k. Введите число.")
  244.         else:
  245.             print("!!! Неверный номер команды !!!")
  246.  
  247. if __name__ == "__main__":
  248.     main()
Add Comment
Please, Sign In to add comment