Algorithm to Find the pointer to the Node M Steps to Tail in a Singly Linked List

♀尐吖头ヾ 提交于 2019-12-06 15:39:53

You should use a circular buffer

Allocate an array of M + 1 pointers, and fill in the (i mod M + 1)th pointer for each node through the list. When you reach the end, look M back in the array (wrapping around if you need to).

That way, you only have N writes!

Here's a hack job sample program in c++

node* get_Mth_from_end(node* head, int m)
{
  int pos = 0;
  node** node_array = new node*[m + 1];
  node_array[0] = head;
  while (node_array[(pos + 1) % (m + 1)] = node_array[pos % (m + 1)]->next)
     pos++;
  if (pos < m)
  {
     delete[] node_array;
     return NULL;
  }
  pos = pos - m;
  if (pos < 0)
     pos = pos + m + 1;
  node* retval = node_array[pos];
  delete[] node_array;
  return retval;
}

This should be checked for off by 1 errors. My pointer syntax may be a little off too, but the idea is there.

I like the recursive suggestion. However, I don't believe it will increase performance over the double pointer algorithm in the question.

Rubancache is correct in that the data structure doesn't support faster operations. It is made for slow traversals but with fast insertion times.

Other than your proposed counting-style algorithm, you could use a recursive function similar to:

int getNumNodesAfter(Node *node){
   if( node->next == NULL ){
      return 0;
   }else{
      return getNumNodesAfter(node->next) + 1;
   }
}

Of course, you'll want to find a good way to store the node in question when the function returns the number you're looking for.

(Edit: this is not likely more efficient than the counting algorithm, just an alternative)

The real answer to the question, though, is: your data structure (singly linked list) does not facilitate a fast implementation of the operation you want to perform on it, so you should choose a new data structure.

Can be done with N+4M pointer assignments and log(M) space (well, extra space, your list already takes up N). Here's how (the idea is the same idea people use in go-back debuggers). Pseudo code follows, where M < 2^m, and p is a circular buffer of length m

for (n=0, front = 0; p[front] != end; ++n, ++p[front]) {
  for (j = 0; j < m; ++j)
    if (n % j = 0)
      ++ front
  front = front % m
}
front = (front-1) % m
for (j = M; j < n-2^m - (n mod 2^m); ++j)
  ++p[front]

p[front] is now your answer

I think something a little better would be something like:

FindNFromEnd(head, n)
    counter = 0;
    first = head
    firstplusn = head
    while (head.next != null)
        counter++
        head = head.next

        if counter == n
            first = firstplusn
            firstplusn = head
            counter = 0

     while counter < n
         counter++
         first = first.next

     return first

for example, if n = 3, it would look something like:

    0   1   2   3   4   5   6   7
1   FNH
2   FN  H   
3   FN      H
1   F           NH 
2   F           N   H
3   F           N       H
1   F           N           H
2               F           N   H
---
3                  [F]

So F keeps track of head - N - counter, N keeps track of head-counter, and you only need to do something (N + M + N/M) rather than O(2*N - M).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!