递归查找链表中的第 n 个到最后一个元素
Posted
技术标签:
【中文标题】递归查找链表中的第 n 个到最后一个元素【英文标题】:Recursively find nth to last element in linked list 【发布时间】: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 就个人而言,我认为没有一种递归方式的好方法。是的,有办法,但没有一个是好的
【参考方案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
。这可以在没有静态变量的情况下完成:***.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】:您可以通过以下几种方式做到这一点:
-
递归遍历列表一次以找到列表长度,然后编写递归方法返回第 kth 元素(一个更容易的问题)。
使用辅助结构来保存结果加上剩余长度;这实质上用一个递归替换了第一个选项的两个递归:
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;
【讨论】:
以上是关于递归查找链表中的第 n 个到最后一个元素的主要内容,如果未能解决你的问题,请参考以下文章