这似乎正在回答正确的答案,但我不确定这是否真的是最好的方法。好像我正在访问前n个节点太多次了。有什么建议?请注意,我必须使用单链表进行此操作。
Node *findNodeFromLast( Node *head, int n )
{
Node *currentNode;
Node *behindCurrent;
currentNode = head;
for( int i = 0; i < n; i++ ) {
if( currentNode->next ) {
currentNode = currentNode->next;
} else {
return NULL;
}
}
behindCurrent = head;
while( currentNode->next ) {
currentNode = currentNode->next;
behindCurrent = behindCurrent->next;
}
return behindCurrent;
}
答案 0 :(得分:14)
另外两次访问节点的方法如下:
创建一个大小为n的空数组,从索引0开始指向此数组的指针,并从链表的开头开始迭代。每次访问节点时,都会将其存储在数组的当前索引中并推进数组指针。填充数组时,环绕并覆盖之前存储的元素。到达列表末尾时,指针将指向列表末尾的元素n。
但这也只是一个O(n)算法。你现在做的很好。我认为没有令人信服的理由改变它。
答案 1 :(得分:7)
开始两个指针。向前移动前一个N
元素,然后移动每个指针1元素。当第一个指针到达结尾时,第二个指针将给出答案。
编辑:是的,它与问题中给出的代码几乎相同。但我觉得伪代码让它更清晰。要回答这个问题,因为必须先访问两次N
个元素才能进行其他操作。如果N
很小,则无关紧要。如果N
很大,那么第二个循环就会很小。所以它始终是O(n)
解决方案。
答案 2 :(得分:6)
保持2个节点间的指针。当第一个指针到达尾部时,第二个指针将指向所需的节点。
代码:
typedef struct _node //define the list node
{
int i;
struct _node *next;
} Node;
Node * findNode(Node *listHead, int n)
{
Node *ptr1, *ptr2; // we need 2 pointers
ptr1 = ptr2 = listHead; // set the pointers to point to the list head initially
while(ptr1->next != NULL) // keep looping until we reach the tail (next will be NULL for the last node)
{
if(n > 0)
{
ptr1 = ptr1->next; //increment only the 1st pointer
n--;
}
else
{
ptr1 = ptr1->next; //increment both pointers
ptr2 = ptr2->next;
}
}
return ptr2; //now return the ptr2 which points to the nth node from the tail
}
答案 3 :(得分:4)
我正在使用一个静态变量'i',它将递增 从列表的末尾回溯。就像在 问题陈述,我们基本上会跟踪Nth 元素从链表的末尾开始。 递归有助于我们从最后进行跟踪。
static int i;
public static void NthToLast(LinkedListNode head, int n)
{
if (head == null)
return;
NthToLast(head.Next, n);
i++;
if (n == i)
{
Console.WriteLine("Mth to last" + head.Data);
return;
}
}
答案 4 :(得分:1)
您的运行时间仍然是O(n),所以我没有看到它有任何问题。
从概念上讲,您可以将列表分为两部分:返回节点之前的部分和之后的部分。其中一个部分必须走两次。您的实现选择了第一个,没有额外的内存使用(除了一些临时变量)。
或者,您可以创建一个堆栈,遍历列表并将每个元素放入堆栈,然后弹出n个项目。然后你将走两次列表的末尾,而不是开头。这具有将列表存储在存储器中两次的缺点。 (你可以通过只存储n个元素并将它们从堆栈的底部放下来添加新的元素来使堆栈变得更聪明;然后你只使用足够的空间来存储n个节点。)
我假设您不能通过将其反转到位来吹走清单。然后它是恒定的记忆,仍然是O(n),仍然在列表的末尾两次。
答案 5 :(得分:1)
Node* fetchNNodeFrmLast(int arg)
{
Node *temp = head;
Node *nNode = head;
if(head == NULL || arg <= 0)
{
Printf("Either head is null or invalid number entered");
return;
}
while(temp != NULL)
{
temp = temp->next;
if(arg > 1)
{
arg--;
continue;
}
nNode = nNode->next;
}
return nNode;
}
答案 6 :(得分:1)
使用两个指针pTemp和NthNode。最初,两者都指向列表的头节点。只有在pTemp进行了n次移动后,NthNode才开始移动。从两者向前移动直到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;
}
参考TextBook:“用Java简化数据结构和算法”
答案 7 :(得分:0)
您可以使用双向链接列表,这是一个链接列表,也存储其父级的地址。横向更容易,因为你可以从最后开始并按照自己的方式开始。
答案 8 :(得分:0)
首先计算列表中的节点数。然后再次遍历,计算n个较少的节点。仍然是O(n)算法,这是不可避免的。
答案 9 :(得分:0)
非常简单.. 拿两个指针p1,p2 在p1已遍历'n'个节点并让p1向上传播到p2之后启动p2.p2指向的节点将是从最后一个节点开始的第n个节点
答案 10 :(得分:0)
此代码似乎更清晰。
public Node<T> findNthNodeFromLast(int n) {
Node<T> current = head;
Node<T> findElement = head;
int length = 0;
while (current != null && current.next != null) {
length++;
if (length >= n) {
findElement = findElement.next;
}
current = current.next;
}
return findElement;
}