Современные технологии стремительно развиваются. А вместе с тем появляются разнообразные способы создания софта. В процессе реализации поставленной задачи задействуются всевозможные элементы: односвязный список, а также операторы, переменные и так далее. Довольно популярным является некий Java. Он достоин внимания каждого уважающего себя программиста.

О языках программирования

Языком программирования считают формальные правила и их наборы, необходимые для создания софта. Своеобразный элемент, посредством которого пользователь «общается» с компьютером и приложениями. Аналогичным образом техника «разговаривает» с программным обеспечением.

У каждого языка программирования имеется лексика – функции и операторы, при помощи которых согласно синтаксисным принципам составляются всевозможные выражения. Чтобы создать приложение, нужно выбрать язык и набрать исходный код. Кодификаций очень много. Одним из самых популярных является некий Java.

Java – определение

Java – способ «общения» пользователя с компьютером. Относится к объектно-ориентированному программированию и предусматривает строгую типизацию. Многофункциональный, универсальный.

Джава используется для написания:

  • Android-приложений;
  • компьютерного софта;
  • банковских утилит;
  • научных программ;
  • программного обеспечения, предназначенного для Big Data;
  • веб-утилит;
  • корпоративного ПО;
  • строенных систем (и маленькие чипы, и спецкомпьюетры).

Среди недостатков выделяют следующие черты:

  • невысокую скорость работы;
  • высокие требования к памяти устройства;
  • отсутствие поддержки низкоуровневого программирования;
  • обновления 2019 года для бизнеса и коммерции – платные;
  • составление новой утилиты отнимает большое количество времени.

Но Java может оказаться полезным. Это – объектно-ориентированная среда, функциональная и с простым синтаксисом. Надежный и независимый. Элементы, написанные на Джаве, запускаются на всех поддерживающих «языковую раскладку» девайсах.

В процессе составления кода приложения используются односвязные списки и другие составляющие синтаксиса. Этот элемент является крайне важным в программировании.

Связный список – понятие

Связным списком (linked list) называется структура данных, в которой элементы упорядочены линейным образом. Сам порядок определяется не как в массивах по номерам этих самых составляющих, а указателями. Последние входят в состав элементов списка, применяются для указания на следующий «этап».

Должен содержать две «детали»:

  • голову;
  • хвост.

Без этих составляющих списки линейного типа немыслимы.

Плюсы и минусы перед массивами

Корректировка связных элементов осуществляется за постоянное время (0(1)), чего нет в массивах. Может с легкостью расширяться. Для этого достаточно добавить очередной элемент.

Но перед использованием соответствующей составляющей языка важно помнить: для поиска конкретного «предмета» требуется каждый раз проходить весь список. Время доступа к искомому = O (n).

Узлы

Новый узел (new node) – это элемент линейного списка. Содержит информацию, подлежащую дальнейшему сохранению, а также указатель на следующий элемент в «перечне» или значение NULL, если рассматриваемая «часть» является последней.

Java и его списки: полезная и важная информация

В приведенном примере описан код структуры, реализующий элемент связного списка (и всей «конструкции» тоже). Информация представляется числовыми данными.

Простыми словами: синтаксис структуры для создания собственного типа информации для инкапсуляции узлов активно используется программистом. Здесь:

  • Int n – сведения, которые хочется сохранить в узле;
  • struct node*next – указатель на следующую составляющую в списке;
  • typedef-ed – не присваивается до выполнения соответствующих строк.

Так, сначала записывается узел struct вместо предыдущего элемента перед и внутри фигурных скобочек. В последней строчке предоставляется node для tupedef в качестве имени, используемого для очередного типа данных оставшейся части приложения.

Односвязные

Односвязные списки – такие объекты, которые включают в себя «маркеры»-указатели на следующий узел. Из точки А можно попасть лишь в точку Б. Так пользователь будет двигаться в самый конец перечня. То есть, пошагово, последовательно.

Вследствие происходит образование потоков, текущих в одном заданном направлении. Полем указателя последнего элемента служит нулевое значение. То есть, NULL.

Основные манипуляции, осуществляемые посредством ОЛС:

  • инициализация;
  • добавление новых элементов в перечень;
  • удаление узлов и корней;
  • вывод составляющих списка;
  • взаимообмен нескольких «частей» перечня.

Далее будут рассмотрены все эти операции с примерами кодов для большей наглядности.

Инициализация

Операция необходима для того, чтобы создавать корневые узлы списков, у которых поля с указателями на следующие составляющие обладают нулевым значением:

struct list * init(int Z) // Z- информация в первом узле
{
  struct list *lst;
  // осуществление выделения необходимой памяти для дальнейшей работы
  lst = (struct list*)malloc(sizeof(struct list));
  lst->field = Z;
  lst->ptr = NULL; // обозначение последнего узла
  return(lst);
}

Это – самый простой вариант развития событий. Но в процессе работы пригодятся и другие манипуляции.

Добавление

При добавлении элементов в списки функциям присваиваются следующие аргументы:

  • информация для добавляемой составляющей;
  • непосредственный указатель на задействованный элемент.

Проводится в несколько этапов. Сначала создается добавляемый узел и заполняются его поля информации. Затем переустанавливаются предыдущие указатели на тот, что хочется внедрить. Завершающим этапом является установка указателей добавляемого элемента туда, куда «показывал» прошлый узел.

struct list * addelem(list *lst, int number)
{
  struct list *temp, *p;
  temp = (struct list*)malloc(sizeof(list));
  p = lst->ptr; // сохранение «маркера» на последующий «объект»
  lst->ptr = temp; // указание предыдущим элементом на новый

  temp->field = number; // поля данных нового элемента сохраняются в память

  temp->ptr = p; // указание созданным узлом на предыдущий элемент
  return(temp);

Адрес добавленного узла – возвращаемое значение задействованной функции.

Удаление

При удалении элементов ОЛС функциями аргументов служат указатели на подлежащий стиранию «объект», а также «маркер» его корня. Функции возвращают указатель на элемент, который следует за удаленным.

Тоже проводится в несколько шагов:

  • постановка указателя предыдущей составляющей на следующий за тем, что подлежит деинсталляции;
  • высвобождение памяти устройства.

Код будет примерно следующим:

struct list * deletelem(list *lst, list *root)
{
  struct list *temp;
  temp = root;
  while (temp->ptr != lst) // список с корня просматривается с самого начала
  { // изучение до тех пор, пока не обнаружена «часть», предшествующая lst
    temp = temp->ptr;
  }
  temp->ptr = lst->ptr; // перестановка маркера в новое место
  free(lst); // освобождение положенной части памяти
  return(temp);
}

В случае со стиранием корней ситуация обстоит иначе:

struct list * deletehead(list *root)
{
  struct list *temp;
  temp = root->ptr;
  free(root); // высвобождается память корня, который удаляли
  return(temp); // создание нового корня
}

Функции удаления корней в списках в виде аргументов получают указатели на текущие корни перечней. Возвращаемое значение – новый корень. А именно – узел, на который указывал удаленный.

Вывод

Отобразить элемент списка можно следующим макаром:

void listprint(list *lst)
{
  struct list *p;
  p = lst;
  do {
    printf("%d ", p->field); // выводится значение, присвоенное ранее p
    p = p->ptr; // осуществляется переход к следующей составляющей
  } while (p != NULL);
}

Аргументом в функции вывода становится указатель на корень списка, а сама функция последовательно обходит все элементы перечня с последующим их выводом (значений).

Обеспечение взаимообмена

При подобных обстоятельствах кодификация, используемая для реализации поставленной задачи будет иметь примерно следующее представление:

struct list * swap(struct list *lst1, struct list *lst2, struct list *head)
{
  // Возвращение нового корня списка
  struct list *prev1, *prev2, *next1, *next2;
  prev1 = head;
  prev2 = head;
  if (prev1 == lst1)
    prev1 = NULL;
  else
    while (prev1->ptr != lst1) // ищется элемент, который предшествует lst1
      prev1 = prev1->ptr;
  if (prev2 == lst2)
    prev2 = NULL;
  else
    while (prev2->ptr != lst2) // осуществляется поиск узла, идущего перед lst2
      prev2 = prev2->ptr;
  next1 = lst1->ptr;  // «часть», идущая после lst1
  next2 = lst2->ptr;  // узел после lst2
  if (lst2 == next1)
  {                       // непосредственный обмен данными соседних элементов
    lst2->ptr = lst1;
    lst1->ptr = next2;
    if (lst1 != head)
      prev1->ptr = lst2;
  }
  else
    if (lst1 == next2)
    {
      // обмен соседних узлов
      lst1->ptr = lst2;
      lst2->ptr = next1;
      if (lst2 != head)
        prev2->ptr = lst2;
    }
    else
    {
      // обмен отстоящий элементов
      if (lst1 != head)
        prev1->ptr = lst2;
      lst2->ptr = next1;
      if (lst2 != head)
        prev2->ptr = lst1;
      lst1->ptr = next2;
    }
  if (lst1 == head)
    return(lst2);
  if (lst2 == head)
    return(lst1);
  return(head);
}

В этом случае работа связных списков основывается на обмене узлового характера. Аргументы функций ОЛС – это два указателя на обмениваемые узлы, а также указатели корня списка. Функция отвечает за возврат корневого «объекта» списка.

Могут рассматриваться разные ситуации:

  • заменяемые «частицы» находятся по соседству;
  • обрабатываемые узлы – не соседние.

Когда маркеры переустанавливаются, требуется проверка на причастность к корню списка. Если заменяемая составляющая является таковой, все нормально. В противном случае узел, предшествующий корневому, будет отсутствовать.

Двусвязные

Еще один тип связных списков – двусвязный. Он похож на обычный, но составляющие хранят ссылки не только на следующие, но и на предыдущие «частицы». Это позволяет осуществлять перемещение по списку туда-сюда. Односвязный список, в отличие от предложенного вниманию, предусматривает только движение вперед.

Каждый элемент в данном случае будет иметь несколько полей: next и prev, указатели на предыдущий/следующий составляющие соответственно.

Далее будут рассмотрены основные операции с двусвязными «перечнями». Пусть они предусматривают несколько конструкторов (Null и DoublyList), в которых возможны различные действия.

Для Null (изначально список пуст):

  • data – место хранения имеющихся значений;
  • next – указатель на следующую составляющую «перечня»;
  • previous – маркер предыдущего элемента.

Для DoublyList:

  • tail – конец списка;
  • head – начало списка;
  • _lenght – извлечение количества узлов в «перечне»;
  • add(value) – добавление новой составляющей;
  • remove(position) – отвечает за удаление;
  • searchNodeAt(position) – поиск узла на заданной позиции.

Коды будут выглядеть соответственно:

Java и его списки: полезная и важная информация

И public class Doubly:

Java и его списки: полезная и важная информация

Метод добавления

Двунаправленные списки предусматривают метод добавления. Приведенный пример отвечает за реализацию нескольких сценариев.  Если перечень пуст, список по голове и хвосту получает добавляемый узел. Добавляется новый элемент. Когда «перечень» уже имеет «составляющие», нужно найти конец и установить добавляемый узел как tail.next.

Java и его списки: полезная и важная информация

После этого задается двунаправленная обработка для нового окончания. Требуется установить в качестве tail.previous первоначальную конечную «составляющую».

Метод поиска

В данном случае действовать предстоит подобно ситуации с односвязными «перечнями»:

Java и его списки: полезная и важная информация

Предложенный спи сок будет обрабатываться для поиска элемента на заданной позиции.

Стирание

Это более сложный код для пользовательского понимания. Поэтому сначала стоит рассмотреть его представление:

Здесь remove(position) будет обрабатывать несколько возможных ситуаций:

Java и его списки: полезная и важная информация
  1. Позиции, которая передается в качестве аргумента remove не имеет места. На экран будет выводиться сообщение об ошибке.
  2. Позиция, служащая аргументом remove – это первый узел списка (голова). В случае подтверждения соответствующего утверждения head становится deleteNode, после чего в качестве нового начала назначается следующий узел в «перечне». Дальше осуществляется проверка на наличие большего количества «составляющих». Если они отсутствуют, head получает null, происходит переход к части if в операторе if-else. В теле «ифа» устанавливается конец (tail) в виде null. Происходит возврат списка в исходное «положение» пустого двусвязного «перечня». Когда удаляется первый узел и остается более одного элемента, осуществляется перемещение к else, после чего устанавливается свойство previous для головы на null. Это проделывается, так как перед началом других «объектов» больше нет.
  3. Позиция-аргумент remove – это окончание «перечня». Хвосту присваивается deleteNode, в качестве нового окончания выступает предыдущий узел. После нового tail другие узлы отсутствуют, а он получает свойство next как null.
  4. Цикл while разрывается, стоит лишь currentNide указать на элемент, который находится в позиции-аргументе remove. Далее происходит переназначение beforeNodeToDelete и afterToDelete на «объект», который идет после nodeToDelete и перед ним. Ссылки убираются на удаленный узел, переназначаются на правильные. Завершается операция установкой nodeToDelete в значении deletedNode, а значением служит null.

В результате описанного алгоритма происходит уменьшение длины списка с последующим возвратом deletedNode.

Теперь понятно, каким может быть первый элемент односвязного списка или двусвязного, как он называется и используется на практике. Приведенные примеры помогают лучше ориентироваться в изученной теме. Они не являются исчерпывающими.

Односвязный список — элементарный, без «пути назад». В программах чаще всего используются их двусвязные аналоги, предоставляющие большее поле для различных манипуляций.

Java и его списки: полезная и важная информация