Как найти 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 ]

1 голос
/ 16 марта 2016

Чтобы понять эту проблему, мы должны сделать простую аналогию с примером измерения. Допустим, вы должны найти место вашей руки, которое находится на расстоянии 1 метра от среднего пальца, как бы вы измерили? Вы бы просто схватили линейку длиной 1 метр и приложили верхний конец этой линейки к кончику среднего пальца, а нижний конец метра будет точно на расстоянии 1 метра от верха вашего среднего пальца. палец.

То, что мы делаем в этом примере, будет таким же, нам просто нужен кадр с шириной n-элемент, и нам нужно поместить кадр в конец списка, таким образом, начальный узел кадра будет ровно n-й элемент до конца списка.

Это наш список, предполагая, что в списке M элементов, а наш фрейм с шириной N элементов;

HEAD -> EL(1) -> EL(2) -> ... -> EL(M-1) -> EL(M)

<-- Frame -->

Однако нам нужны только границы кадра, поэтому конечная граница кадра будет точно (N-1) элементов удалена от начальной границы кадра. Так что нужно хранить только эти граничные элементы. Давайте назовем их A и B;

HEAD -> EL(1) -> EL(2) -> ... -> EL(M-1) -> EL(M)

A <- N-Element Wide-> B

Первое, что мы должны сделать, это найти B, который является концом кадра.

ListNode<T> b = head;
int count = 1;

while(count < n && b != null) {
    b = b.next;
    count++;
}

Теперь b - это n-й элемент массива, а a находится на HEAD . Таким образом, наш фрейм установлен, и мы будем постепенно увеличивать оба граничных узла, пока b не достигнет конца списка, где a будет n-ным к последний элемент;

ListNode<T> a = head;

while(b.next != null) {
    a = a.next;
    b = b.next;
}

return a;

Чтобы собрать все, и с проверками HEAD, N

public ListNode<T> findNthToLast(int n) {
    if(head == null) {
        return null;
    } else {
        ListNode<T> b = head;
        int count = 1;

        while(count < n && b != null) {
            b = b.next;
            count++;
        }

        if(count == n && b!=null) {
            ListNode<T> a = head;

            while(b.next != null) {
                a = a.next;
                b = b.next;
            }

            return a;
        } else {
            System.out.print("N(" + n + ") must be equal or smaller then the size of the list");
            return null;
        }
    }
}
1 голос
/ 11 мая 2015

Используйте два указателя pTemp и NthNode.Первоначально оба указателя указывают на головной узел списка.NthNode начинает двигаться только после того, как pTemp сделал n ходов.От обоих движется вперед, пока pTemp не достигнет конца списка.В результате NthNode указывает на n-й узел из конца связанного списка.

public ListNode NthNodeFromEnd(int n){
    ListNode pTemp = head, NthNode = null;
   for(int count=1; count<n;count++){
     if(pTemp!=null){
       pTemp = pTemp.getNext();
     }
   }
   while(pTemp!=null){
     if(NthNode==null){
         NthNode = head;
     }
     else{
        NthNode = NthNode.getNext();
     }
     pTemp = pTemp.getNext();
   }
   if(NthNode!=null){
     NthNode = NthNode.getNext();
     return NthNode;
   }
return null;
}

См. Учебник: «Структура данных и алгоритмы, упрощенные в Java»

1 голос
/ 14 апреля 2015
public int nthFromLast(int n){
    Node current = head;
    Node reference = head;      
    for(int i=0;i<n;i++){
        reference=reference.getNext();
    }
    while(reference != null){
        current = current.getNext();
        reference = reference.getNext();
    }
    return current.getData();
}
1 голос
/ 03 февраля 2012

Нет, вы не знаете длину связанного списка ... Вам придется пройти один раз, чтобы получить длину списка избранного, чтобы ваш подход был малоэффективным;

1 голос
/ 25 августа 2014

Здесь мы берем два указателя pNode и qNode, оба начальных пункта указывают на заголовок qNode. Затем проходите до конца списка, и pNode будет проходить только тогда, когда разница между счетчиком и положением больше 0 и pthNode увеличивается один раз в каждом цикле.

static ListNode nthNode(int pos){
ListNode pNode=head;
ListNode qNode=head;
int count =0;
while(qNode!=null){
    count++;
    if(count - pos > 0)
        pNode=pNode.next;
    qNode=qNode.next;
}
return pNode;
}
0 голосов
/ 30 июня 2015

В Java я буду использовать-

public class LL {
  Node head;
  int linksCount;

   LL(){
     head = new Node();
     linksCount = 0;
   }

  //TRAVERSE TO INDEX
  public Node getNodeAt(int index){
    Node temp= head;
    if(index > linksCount){
        System.out.println("index out of bound !");
        return null;
    }
    for(int i=0;i<index && (temp.getNext() != null);i++){
        temp = temp.getNext();
    }
    return temp.getNext();
  }
}
0 голосов
/ 26 декабря 2014

мой подход, что я думаю, прост и имеет временную сложность O (n).

Шаг 1: Сначала получите количество узлов. Запустите цикл for, начиная с первого до последнего узла

Шаг 2: Как только у вас будет счетчик, примените простую математику, например, если мы нашли 7-й узел для последнего узла и количество всех узлов равно 12, то (count - index) - 1 даст некоторый k-й узел , до которого вам придется пройти, и это будет n-й узел до последнего узла. В этом случае (12 -7) -1 = 4

Если элементы 8-> 10-> 5-> 7-> 2-> 1-> 5-> 4-> 10-> 10, то результат с 7-го по последний узел равен 7, что является ничем иным, как 4-й узел от начала.

0 голосов
/ 18 августа 2012

Вы также можете решить вышеуказанную проблему, используя хеш-таблицы. Записи хеш-таблицы - это положение узла и адрес узла. Поэтому, если мы хотим найти n-й узел с конца (это означает, что m-n + 1 с первого, где m - это число узлов). Теперь, когда мы вводим записи в хеш-таблицу, мы получаем количество узлов. Шаги: -

1. Обойдите каждый узел и сделайте соответствующие записи в хеш-таблице.

2. Посмотрите на узел m-n + 1 в хеш-таблице, мы получим адрес.

Сложность времени O (n).

0 голосов
/ 09 мая 2014

Вот код с использованием двух указателей: (* ​​1001 * source )

Подход медленного и быстрого указателя

struct node
{
  int data;
  struct node *next;
}mynode;


mynode * nthNodeFrmEnd(mynode *head, int n /*pass 0 for last node*/)
{
  mynode *ptr1,*ptr2;
  int count;

  if(!head)
  {
    return(NULL);
  }

  ptr1  = head;
  ptr2  = head;
  count = 0;

  while(count < n)
  {
     count++;
     if((ptr1=ptr1->next)==NULL)
     {
        //Length of the linked list less than n. Error.
        return(NULL);
     }
  }

  while((ptr1=ptr1->next)!=NULL)
  {
    ptr2=ptr2->next;
  }

  return(ptr2);
}


Рекурсия

node* findNthNode (node* head, int find, int& found){
    if(!head) {
        found = 1;
        return 0;
    }
    node* retval = findNthNode(head->next, find, found);
    if(found==find)
        retval = head;
    found = found + 1;
    return retval;
}

0 голосов
/ 25 ноября 2016

В зависимости от допустимого отклонения стоимости памяти (O (k) в этом решении) мы можем выделить массив указателей длины k и заполнить его узлами в виде кругового массива при обходе связанного списка.

Когда мы закончим обход связанного списка, первый элемент массива (просто убедитесь, что 0-индекс правильно рассчитан, поскольку это круговой массив), у нас будет ответ.

Если первый элемент массива равен нулю, наша проблема не решена.

...