【问题标题】:Recursively find nth to last element in linked list递归查找链表中的第 n 个到最后一个元素
【发布时间】:2013-12-10 22:14:01
【问题描述】:

我正在练习基本的数据结构,但在递归方面遇到了一些困难。我知道如何通过迭代来做到这一点,但是我所有通过递归从链表的最后一个节点返回第 n 个节点的尝试都导致 null。到目前为止,这是我的代码:

public static int i = 0; 
public static Link.Node findnthToLastRecursion(Link.Node node, int pos) {
    if(node == null) return null; 
    else{
    findnthToLastRecursion(node.next(), pos);
    if(++i == pos) return node; 
    return null; 
    }

谁能帮我理解我哪里出错了?

这是我的迭代解决方案,效果很好,但我真的很想知道如何将其转换为递归:

public static Link.Node findnthToLast(Link.Node head, int n) {
    if (n < 1 || head == null) {
        return null;
    }
    Link.Node pntr1 = head, pntr2 = head;
    for (int i = 0; i < n - 1; i++) {
        if (pntr2 == null) {
            return null;
        } else {
            pntr2 = pntr2.next();
        }
    }
    while (pntr2.next() != null) {
        pntr1 = pntr1.next();
        pntr2 = pntr2.next();
    }
    return pntr1;
}

【问题讨论】:

  • 第一次调用时node 的值是多少?您是向前还是向后工作?我原以为您需要从头开始并致电previous(),或者如果您不知道结尾是什么,请从头开始,一直工作到最后,然后退出n 次。这段代码没有做那样的事情......
  • 找不到最后一个节点的位置(或大小),怎么知道第n个最后一个节点?这(如果编写正确)将找到第 n - 1 个节点(不是第 n 个最后一个节点)
  • 快速提问:你知道你的链表的长度是多少
  • 初始值为head,没有previous()函数可以调用。从头开始,一直工作到结束,然后通过迭代退出 n 次对我来说是有意义的,但我似乎无法理解如何递归地做到这一点。
  • @user3029486 就个人而言,我认为没有一种递归方式的好方法。是的,有办法,但没有一个是好的

标签: java recursion linked-list


【解决方案1】:

您需要走到尽头,然后计算返回的路数,确保每次传回节点时都将其传回。我喜欢一个返回点

public static int i = 0;  
public static Link.Node findnthToLastRecursion(Link.Node node, int pos) {

    Link.Node result = node;

    if(node != null) {
        result = findnthToLastRecursion(node.next, pos);

        if(i++ == pos){
            result = node;
        }
    }
    return result;
}

工作示例输出 7 作为 2 远离第 9 个和最后一个节点:

public class NodeTest {

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

/**
 * @param args
 */
public static void main(String[] args) {
    Node first = null;
    Node prev = null;
    for (int i = 0; i < 10; i++) {

        Node current = new Node(prev, Integer.toString(i),null);
        if(i==0){
            first = current;
        }
        if(prev != null){
            prev.next = current;
        }
        prev = current;
    }

    System.out.println( findnthToLastRecursion(first,2).item);
}

public static int i = 0;

public static Node findnthToLastRecursion(Node node, int pos) {

    Node result = node;

    if (node != null) {
        result = findnthToLastRecursion(node.next, pos);

        if (i++ == pos) {
            result = node;
        }
    }

    return result;
}
}

【讨论】:

  • 这应该是公认的答案,因为如果是面试问题,没有辅助函数的递归可以更好地理解递归。
  • @OpenUserX03 好吧,这里的助手是static int i。这可以在没有静态变量的情况下完成:stackoverflow.com/a/29937328/885464
【解决方案2】:

不需要静态变量。

public class List {
    private Node head = null;

    // [...] Other methods

    public Node findNthLastRecursive(int nth) {
        if (nth <= 0) return null;
        return this.findNthLastRecursive(this.head, nth, new int[] {0});
    }

    private Node findNthLastRecursive(Node p, int nth, int[] pos) {
        if (p == null) {
            return null;
        }
        Node n = findNthLastRecursive(p.next, nth, pos);
        pos[0]++;
        if (pos[0] == nth) {
            n = p;
        }
        return n;
    }
}

【讨论】:

  • 拥有静态变量和添加额外参数/拥有辅助函数有什么区别?我不明白为什么后者应该比前者更好。
  • @michal 当然可以使用静态变量,但严格来说这不是一个完美的递归方法。使用堆栈来保持递归的当前状态通常被认为是常见的做法。
  • 我的意思是,你还使用了一个额外的变量,你称之为 pos。无论它是静态的,还是您将它从一个调用传递到另一个调用,都没有太大变化。换句话说,这两种方法都求助于调用堆栈之外的额外变量。你明白我的意思吗?
  • 我认为这种方法更好,因为它避免了在类级别添加静态变量。 “findNthRecursive()”函数可能只是“LinkedList”类中​​其他几个成员函数之一。因此,仅为此函数在类级别添加静态变量看起来不够干净/最小。话虽如此,我还不清楚为什么 pos 需要是 int[] 类型。为什么不能只是一个 int?
【解决方案3】:

我误解了这个问题。这是基于您的迭代解决方案的答案:

public static Link.Node findnthToLast(Link.Node head, int n) {
    return findnthToLastHelper(head, head, n);
}

private static Link.Node findnthToLastHelper(Link.Node head, Link.Node end, int n) {
    if ( end == null ) {
        return ( n > 0 ? null : head);
    } elseif ( n > 0 ) {
        return findnthToLastHelper(head, end.next(), n-1);
    } else {
        return findnthToLastHelper(head.next(), end.next(), 0);
    }
}

【讨论】:

    【解决方案4】:

    您可以通过以下几种方式做到这一点:

    1. 递归遍历列表一次以找到列表长度,然后编写递归方法返回第 kth 元素(一个更容易的问题)。
    2. 使用辅助结构来保存结果加上剩余长度;这实质上用一个递归替换了第一个选项的两个递归:

      static class State {
          Link.Node result;
          int trailingLength;
      }
      public static Link.Node findnthToLastRecursion(Link.Node node, int pos) {
          if(node == null) return null;
          State state = new State();
          findnthToLastRecursion(node, pos, state);
          return state.result;
      }
      
      private static void findnthToLastRecursion(Link.Node node, int pos, State state) {
          if (node == null) {
              state.trailingLength = 0;
          } else {
              findnthToLastRecursion(node.next(), state);
              if (pos == state.trailingLength) {
                  state.result = node;
              }
              ++state.trailingLength;
          }
      }
      

    【讨论】:

      【解决方案5】:

      实际上你不需要 public static int i = 0; 。对于utill 方法,pos 是:

      pos = 链表长度 - 上一个的 pos + 1

      public static Node findnthToLastRecursion(Node node, int pos) {
          if(node ==null){ //if null then return null
              return null;
          }
          int length = length(node);//find the length of the liked list
          if(length < pos){
              return null;
          }
          else{
              return utill(node, length - pos + 1);
          }
      }
      private static int length(Node n){//method which finds the length of the linked list
          if(n==null){
              return 0;
          }
          int count = 0;
          while(n!=null){
              count++;
              n=n.next;
          }
          return count;
      }
      private static Node utill(Node node, int pos) {
          if(node == null) {
              return null;
          }
          if(pos ==1){
              return node;
          }
          else{
              return utill(node.next, pos-1);   
          }
      }
      

      这里的node.next 是next 节点。我直接访问next 节点而不是调用next() 方法。希望对您有所帮助。

      【讨论】:

      • 倒数第n个;不是第n个
      • 谢谢,现在这更有意义了。看起来知道长度和使用辅助功能是关键。
      • @user3029486 非常正确。
      【解决方案6】:

      这(有点)作弊,但看起来不错。

      public class Test {
        List<String> list = new ArrayList<> (Arrays.asList("Zero","One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"));
        public static String findNthToLastUsingRecursionCheatingALittle(List<String> list, int n) {
          int s = list.size();
          return s > n
                  // Go deeper!
                  ? findNthToLastUsingRecursionCheatingALittle(list.subList(1, list.size()), n)
                  // Found it.
                  : s == n ? list.get(0)
                  // Too far.
                  : null;
        }
        public void test() {
          System.out.println(findNthToLastUsingRecursionCheating(list,3));
        }
      
        public static void main(String args[]) {
          new Test().test();
        }
      }
      

      打印出来:

      Eight
      

      我认为是正确的。

      我使用 List 而不是一些 LinkedList 变体,因为我不想重新发明任何东西。

      【讨论】:

        【解决方案7】:
        int nthNode(struct  Node* head, int n)
        {
            if (head == NULL)
                return 0;
            else {
            int i;
            i = nthNode(head->left, n) + 1;
            printf("=%d,%d,%d\n", head->data,i,n);
            if (i == n)
                printf("%d\n", head->data);
            }
        }
        

        【讨论】:

          【解决方案8】:
          public class NthElementFromLast {
          public static void main(String[] args) {
              List<String> list = new LinkedList<>();
              Stream.of("A","B","C","D","E").forEach(s -> list.add(s));
              System.out.println(list);
              System.out.println(getNthElementFromLast(list,2));
          
          }
          
          private static String getNthElementFromLast(List list, int positionFromLast) {
              String current = (String) list.get(0);
              int index = positionFromLast;
          
              ListIterator<String> listIterator = list.listIterator();
              while(positionFromLast>0 && listIterator.hasNext()){
                  positionFromLast--;
                  current = listIterator.next();
              }
              if(positionFromLast != 0) {
                  return null;
              }
              String nthFromLast = null;
              ListIterator<String> stringListIterator = list.listIterator();
              while(listIterator.hasNext()) {
                  current = listIterator.next();
                  nthFromLast = stringListIterator.next();
              }
              return nthFromLast;
          }
          

          }

          这将从最后一个元素中找到第 N 个元素。

          【讨论】:

            【解决方案9】:

            我的方法简单直接,你可以根据需要改变数组大小:

            int pos_from_tail(node *k,int n)
            {   static  int count=0,a[100];
                if(!k) return -1;
                else
                pos_from_tail(k->next,n);
                a[count++]=k->data;
                return a[n];
            }
            

            【讨论】:

              【解决方案10】:

              您将在代码中稍作改动:

              public static int i = 0; 
              public static Link.Node findnthToLastRecursion(Link.Node node, int pos) {
                  if(node == null) return null; 
                  else{
                      **Link.Node temp = findnthToLastRecursion(node.next(), pos);
                      if(temp!=null)
                          return temp;**
                      if(++i == pos) return node; 
                        return null; 
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2018-08-15
                • 2014-01-11
                • 1970-01-01
                • 2023-03-09
                • 1970-01-01
                • 1970-01-01
                • 2019-08-28
                相关资源
                最近更新 更多