Алгоритм черепахи и кролика всегда O (N)?
Я собираюсь предварить это тем фактом, что я не полностью осведомлен о нотации Big O, поэтому, возможно, мои мысли об этом выключены.
я случайно просматривал, поэтому, когда я наткнулся на вопрос об обнаружении бесконечных циклов в связанных списках. Это указало мне на алгоритм здесь известный как черепаха и Кролик.
class Node {
Node next;
}
boolean isInInfiniteLoop(Node node) {
if (node == null) {
return false;
}
Node turtle = node; // slower moving node
Node rabbit = node.next; // faster moving node
while (rabbit != null) {
if (rabbit.equals(turtle)) {
// the faster moving node has caught up with the slower moving node
return true;
} else if (rabbit.next == null) {
// reached the end of list
return false;
} else {
turtle = turtle.next;
rabbit = rabbit.next.next;
}
}
// rabbit reached the end
return false;
}
в статье он упоминает, что это O(N). Из того, что я понимаю, O (N) означает, что скорость алгоритма растет линейно по отношению к количеству элементов в списке.
однако, если я не смотрю на вещи неправильно, переменная кролика всегда пропускает 1 узел(так что это "быстрее"), что означает, что у нее есть потенциал пропустить узел черепахи, таким образом, имея потенциал цикла вокруг бесконечного цикла 1 или более раз, прежде чем он станет тем же узлом, что и переменная черепахи, что означает, что худший сценарий не O (N).
Я что-то пропустила? Я думаю оптимизация может состоять в том, чтобы проверить, если rabbit.Next.Equals(turtle)
а также, но ни один из комментариев не указывает на это, поэтому мне интересно, если я что-то упускаю.
5 ответов
кролик никогда не перепрыгнет через черепаху, потому что разница между скоростью равна 1.
сначала кролик входит в петлю, затем черепаха. Как только черепаха входит в петлю, разница кролика и черепахи решается, и вы можете считать, что кролик находится за черепахой. Тогда разница уменьшается на 1 на каждом шаге.
таким образом, общие шаги не будут превышать N, и, таким образом, Это O(n).
небольшое моделирование рук должно показать вам, что, хотя кролик может пропустить черепаху один раз, второй раз вокруг петли, он наступит на нее (так сказать). (EDIT: это применяется, как только кролик и черепаха находятся в цикле, что произойдет не более чем в O(N) итерациях.) Поскольку O(2*N) = O(N), это все еще алгоритм O (N).
хороший алгоритм тоже. +1 за ссылку.
кролик не может перепрыгнуть через черепаху, так как черепаха тоже движется. В ASCII art:
R _ T
R T
X
иными словами, пока кролик находится за черепахой, каждая итерация цикла уменьшает их расстояние на 1. Таким образом, будет итерация, где расстояние становится ровно 0.
этот алгоритм становится яснее, если вы вместо этого запускаете кролика и черепаху на одном узле. Когда черепаха переместила N узлов вперед, кролик переместил 2N узлов вперед. Если существует цикл длины N, Турле вернется в начальную точку после N ходов, и 2n ходов кролика также посадит его на начальный узел, дважды обойдя цикл.
есть две ситуации:
- есть четное количество узлов, и
- существует нечетное количество узлов.
рассмотрим оба.
в 1, у нас есть это:
T R x x
затем
x T x R
затем
x R T x
затем
x x x RT
в 2, мы имеем это:
T R x
затем
R T x
затем
x x RT
учитывая, что кролик и заяц могут двигаться только с максимальным шагом два, это единственные стоит отметить два условия. Вероятно, здесь есть правильное индуктивное доказательство, но показ фаз объясняет, почему это работает, даже если кролик пропускает черепаху. Кролик может пропустить черепаху, только если он позади черепахи, а если он позади черепахи, то либо он не будет прыгать, либо он столкнется.
когда кролик пропускает черепаху, как в 1, Нам нужно только N+1
ходы черепахи и кролика, так что 2N+2
для N - длина списка, которая является O(N)