Как найти n-й элемент в конце односвязного списка? - PullRequest
59 голосов
/ 08 апреля 2010

Следующая функция пытается найти элементы от nth до last из односвязного списка.

Например:

Если элементы 8->10->5->7->2->1->5->4->10->10 тогда результат будет 7th до последнего узла 7.

Кто-нибудь может мне помочь, как работает этот код, или есть лучший и более простой подход?

LinkedListNode nthToLast(LinkedListNode head, int n) {
  if (head == null || n < 1) {
    return null;
  }

  LinkedListNode p1 = head;
  LinkedListNode p2 = head;

  for (int j = 0; j < n - 1; ++j) { // skip n-1 steps ahead
    if (p2 == null) {
      return null; // not found since list size < n
    }
    p2 = p2.next;
  }

  while (p2.next != null) {
    p1 = p1.next;
    p2 = p2.next;
  }

  return p1;
}

Ответы [ 27 ]

0 голосов
/ 01 июля 2013

Рекурсивное решение:

Node findKth (Node head, int count, int k) {
    if(head == null)
        return head;
    else {
        Node n =findKth(head.next,count,k);
        count++;

        if(count == k)
            return head;

        return n;
    }
}
0 голосов
/ 23 декабря 2012

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

Вот мой код:

    public void findntolast(int index)
    {
        Node ptr = front; int count = 0;
        while(ptr!=null)
        {
            count++;
            if (count == index)
            {
                front = ptr;
                break;
            }
            ptr = ptr.next;
        }
        Node temp=front;
        while(temp!=null)
        {
            Console.WriteLine(temp.data);
            temp=temp.next;
        }
    }
0 голосов
/ 09 апреля 2016

Никто здесь не заметил, что версия Джонатана выдаст исключение NullPinterException, если n больше, чем длина LinkedList.Вот моя версия:

public Node nth(int n){
        if(head == null || n < 1) return null;

        Node n1 = head;
        Node n2 = head;
        for(int i = 1; i < n; i++){
            if(n1.next == null) return null; 
            n1 = n1.next;
        }

        while (n1.next != null){
            n1 = n1.next;
            n2 = n2.next;
        }
        return n2;
}

Я просто немного изменяю здесь: когда узел n1 шагает вперед, вместо того, чтобы проверять, является ли n1 нулевым, я проверяю погоду n1.next на ноль или в цикле while n1.next создаст исключение NullPinterException.

0 голосов
/ 22 августа 2016

Вот версия C # для поиска n-го ребенка из списка ссылок.

public Node GetNthLast(Node head, int n)
    {
        Node current, nth;
        current = nth = head;
        int counter = 0;

        while (current.next != null)
        {
            counter++;
            if (counter % n == 0)
            {
                for (var i = 0; i < n - 1; i++)
                {
                    nth = nth.next;
                }
            }
            current = current.next;
        }
        var remainingCounts = counter % n;
        for (var i = 0; i < remainingCounts; i++)
        {
            nth = nth.next;
        }
        return nth;
    }
0 голосов
/ 05 августа 2012

Я думаю, что есть один недостаток в коде вопроса, и мне интересно, если это было взято из книги, как это возможно ... он может выполняться правильно, но код несколько логически некорректен.Внутри цикла for ... условие if должно быть проверено по отношению к p2->next ! = NULL

 for (int j = 0; j < n - 1; ++j) { // skip n-1 steps ahead
       if (p2->next == null) {
       return null; // not found since list size < n
   }

... остальное в порядке и объяснение, как уже дано, код сдвигает позиции p2 (n-1) на1007 *, затем в цикле while он перемещает их одновременно до тех пор, пока p2->next не достигнет конца ... может сказать, что мой ответ неверен

0 голосов
/ 21 января 2019

Прежде всего

Как упомянуто в комментарии, но, чтобы быть более ясным, вопрос от :

<Cracking the coding interview 6th> | IX Interview Questions | 2. Linked Lists | Question 2.2.

Это отличная книга Gayle Laakmann McDowell, инженера-программиста из Google, который опросил много людей.


Подходы

(при условии, что связанный список не отслеживает длину) , есть 2 подхода за O (n) времени и O (1) пробел:

  • Сначала найдите длину, затем зацикливайтесь на элементе (len-k + 1).
    Это решение не упоминается в книге, насколько я помню.
  • Цикл, через 2 указателя, держите их (k-1) на расстоянии.
    Это решение из книги, как и в вопросе.

Код

Ниже приведена реализация в Java с модульным тестом (без использования какой-либо продвинутой структуры данных в самом JDK) .

KthToEnd.java

/**
 * Find k-th element to end of singly linked list, whose size unknown,
 * <p>1-th is the last, 2-th is the one before last,
 *
 * @author eric
 * @date 1/21/19 4:41 PM
 */
public class KthToEnd {
    /**
     * Find the k-th to end element, by find length first.
     *
     * @param head
     * @param k
     * @return
     */
    public static Integer kthToEndViaLen(LinkedListNode<Integer> head, int k) {
        int len = head.getCount(); // find length,

        if (len < k) return null; // not enough element,

        return (Integer) head.getKth(len - k).value; // get target element with its position calculated,
    }

    /**
     * Find the k-th to end element, via 2 pinter that has (k-1) distance.
     *
     * @param head
     * @param k
     * @return
     */
    public static Integer kthToEndVia2Pointer(LinkedListNode<Integer> head, int k) {
        LinkedListNode<Integer> p0 = head; // begin at 0-th element,
        LinkedListNode<Integer> p1 = head.getKth(k - 1); // begin at (k-1)-th element,

        while (p1.next != null) {
            p0 = p0.next;
            p1 = p1.next;
        }

        return p0.value;
    }

    static class LinkedListNode<T> {
        private T value;
        private LinkedListNode next;

        public LinkedListNode(T value) {
            this.value = value;
        }

        /**
         * Append a new node to end.
         *
         * @param value
         * @return new node
         */
        public LinkedListNode append(T value) {
            LinkedListNode end = getEnd();
            end.next = new LinkedListNode(value);
            return end.next;
        }

        /**
         * Append a range of number, range [start, end).
         *
         * @param start included,
         * @param end   excluded,
         */
        public void appendRangeNum(Integer start, Integer end) {
            KthToEnd.LinkedListNode last = getEnd();
            for (int i = start; i < end; i++) {
                last = last.append(i);
            }
        }

        /**
         * Get end element of the linked list this node belongs to, time complexity: O(n).
         *
         * @return
         */
        public LinkedListNode getEnd() {
            LinkedListNode end = this;
            while (end != null && end.next != null) {
                end = end.next;
            }

            return end;
        }

        /**
         * Count of element, with this as head of linked list.
         *
         * @return
         */
        public int getCount() {
            LinkedListNode end = this;
            int count = 0;
            while (end != null) {
                count++;
                end = end.next;
            }

            return count;
        }

        /**
         * Get k-th element from beginning, k start from 0.
         *
         * @param k
         * @return
         */
        public LinkedListNode getKth(int k) {
            LinkedListNode<T> target = this;
            while (k-- > 0) {
                target = target.next;
            }

            return target;
        }
    }
}

KthToEndTest.java

(модульный тест, используя TestNG, или вы изменяете на JUnit / .., по желанию)

import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

/**
 * KthToEnd test.
 *
 * @author eric
 * @date 1/21/19 5:20 PM
 */
public class KthToEndTest {
    private int len = 10;
    private KthToEnd.LinkedListNode<Integer> head;

    @BeforeClass
    public void prepare() {
        // prepare linked list with value [0, len-1],
        head = new KthToEnd.LinkedListNode(0);
        head.appendRangeNum(1, len);
    }

    @Test
    public void testKthToEndViaLen() {
        // validate
        for (int i = 1; i <= len; i++) {
            Assert.assertEquals(KthToEnd.kthToEndViaLen(head, i).intValue(), len - i);
        }
    }

    @Test
    public void testKthToEndVia2Pointer() {
        // validate
        for (int i = 1; i <= len; i++) {
            Assert.assertEquals(KthToEnd.kthToEndVia2Pointer(head, i).intValue(), len - i);
        }
    }
}

Советы:

  • KthToEnd.LinkedListNode
    Это простой односвязный узел списка, реализованный с нуля, он представляет собой связанный список, начинающийся с самого себя.
    Он не отслеживает дополнительно голову / хвост / длину, хотя для этого есть методы.
0 голосов
/ 27 декабря 2013

можете ли вы использовать дополнительную структуру данных ... если так, то все будет просто ... начните помещать все узлы в стек, сохраняйте счетчик для всплывающего окна. согласно вашему примеру, 8-> 10-> 5-> 7-> 2-> 1-> 5-> 4-> 10-> 10 начинают чтение связанного списка и начинают помещать узлы или данные узла-> на стек. поэтому стек будет выглядеть как top -> {10, 10,4, 5, 1, 2, 7, 5, 10, 8} <- bottom. </p>

теперь начинайте хлопать с вершины стека, поддерживая счетчик = 1, и каждый раз, когда вы щелкаете, увеличивайте счетчик на 1, когда вы достигнете n-го элемента (в вашем примере 7-го элемента), прекратите хлопать.

примечание: это будет печатать или извлекать данные / узлы в обратном порядке

...