-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Решение задачи на нахождение середины связного списка #318
base: master
Are you sure you want to change the base?
Conversation
@@ -0,0 +1,38 @@ | |||
# Название |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Прямо вместо Название лучше и написать Середина односвязного списка
@@ -0,0 +1,38 @@ | |||
# Название | |||
Середина односвязного списка | |||
## Введение |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут не по стандарту оформлено
Стандарт: тут
Пример: можно посмотреть этот
|
||
Если вы забыли или не знаете, что такое односвязный список, то можете посмотреть [статью](https://github.com/qcha/JBook/blob/master/jcore/collections/list/linked_list.md). Вкратце, основная особенность в том, что каждый элемент знает лишь своё значение и хранит "ссылку" на следующий элемент списка. Соответственно, мы не можем обратиться к любому элементу по индексу за О(1), так как чтобы найти n-ый элемент списка, нужно проитерироваться по всему списку от головы до n-ого элемента (каждый раз переходя от текущего на следующий элемент). | ||
## Основная часть | ||
### Постановка задачи |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Между заголовками перенос строки
|
||
**Как думать**: в любой алгоритмической задаче на связные списки нужно как-то корректно перемещаться по списку, запоминать указатели на "важные" элементы, правильно переприсваивать их и учитывать то, что список обычно не бесконечный. Раз мы никогда не обладаем "полной информацией" о списке, не можем получить ее за О(1) и не умеем обращаться по индексу; значит нужно иметь указатель на средний элемент списка всегда, на любой итерации алгоритма. Тогда мы в любой момент сможем ответить на вопрос: "какой срединный элемент в той части списка, по которой мы проитерировались к данной итерации?". | ||
|
||
### Решение |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
После заголовка перенос строки
|
||
Первоначальное значение флага - false, так как после первой итерации указатель на срединный элемент сдвинуться не должен. | ||
|
||
Тогда псевдо-код решения будет выглядеть так: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В целом лучше сразу код на Java писать, так как на собеседованиях обычно просят выбрать язык и на нем решение реализовать
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public static ListNode middleNode(ListNode head) {
ListNode curr = head;
ListNode mid = head;
boolean isMoveMid = false;
while (curr != null) {
curr = curr.next;
if (isMoveMid) {
mid = mid.next;
}
isMoveMid = !isMoveMid;
}
return mid;
}
}
### Решение | ||
Изначально, всё что у нас есть - это указатель на первый элемент списка (голова). | ||
|
||
Все это наталкивает на довольно простой и часто встречающийся алгоритм в easy-задачах на массивы и списки под общим названием "Два указателя - two pointers". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут лучше подробнее про алгоритм
### Постановка задачи | ||
Наша задача найти срединный элемент односвязного списка (естественно, итерироваться по списку можно не более 1 раза). | ||
|
||
Срединным будем считать элемент с индексом `n div 2` (индекс считается с 0), где `n` - длина списка. *Это уточнение однозначно определит, какой элемент считается срединным, если в списке чётное количество элементов.* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Лучше просто так и написать N / 2
|
||
Следовательно, перед каждой итерацией один наш указатель (`curr`) всегда указывает на следующий "обозреваемый" элемент, а второй (`mid`) - на срединный элемент части списка от его начала до следующего элемента не включительно. Изначально: следующий элемент - это голова списка, серединный элемент - также голова списка. | ||
|
||
Остается лишь корректно сдвигать оба указателя в каждой итерации. Для этого можно завести булевый флаг (`isShiftMiddle`), значение которого мы будем менять на противоположное в конце абсолютно каждой итерации. Текущий указатель мы будем сдвигать тоже на каждой итерации, а вот указатель на текущую середину - только когда значение флага = true. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Назначение флага не сразу может быть понятно, лучше чуть подробнее рассказать об этом
} | ||
|
||
return mid | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В конце хорошо бы сказать про алгоритмическую сложность
|
||
Первоначальное значение флага - false, так как после первой итерации указатель на срединный элемент сдвинуться не должен. | ||
|
||
Тогда псевдо-код решения будет выглядеть так: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public static ListNode middleNode(ListNode head) {
ListNode curr = head;
ListNode mid = head;
boolean isMoveMid = false;
while (curr != null) {
curr = curr.next;
if (isMoveMid) {
mid = mid.next;
}
isMoveMid = !isMoveMid;
}
return mid;
}
}
closes #282