如何在给定头节点的情况下递归地找到链表中最大的项?

发布于 2024-08-23 03:00:29 字数 886 浏览 3 评论 0原文

int findLargest (ListNode *p)
// --------------------------------------------------------------------------
// Preconditions: list head pointer is passed as a parameter.
// Postconditions: returns the largest value in the linked list.
// --------------------------------------------------------------------------
{
    if (p->item != NULL)
    {
        int largest = p->item;
        if (largest > p->next->item)
            ...
    }

    ...
}

是否可以编写仅传递指针作为参数的递归函数?我不知道如何在不添加更多参数的情况下做到这一点。有什么想法吗?我只使用顺序搜索。没什么花哨的。

这是类 List 中需要的部分:

    struct ListNode 
    {
        ListItemType item;  // A data item on the list.
        ListNode    *next;  // Pointer to next node
    }; // end ListNode

    ListNode   *head; // Pointer to linked list of items.

我主要担心问题的可行性。仅用指针作为参数可以完成此操作吗?

int findLargest (ListNode *p)
// --------------------------------------------------------------------------
// Preconditions: list head pointer is passed as a parameter.
// Postconditions: returns the largest value in the linked list.
// --------------------------------------------------------------------------
{
    if (p->item != NULL)
    {
        int largest = p->item;
        if (largest > p->next->item)
            ...
    }

    ...
}

Is it possible to write this recursive function passing only a pointer as a parameter? I can't figure out how to do this without adding more parameters. Any ideas? I am only using sequential search. Nothing fancy.

Here is the portion of class List that will be needed:

    struct ListNode 
    {
        ListItemType item;  // A data item on the list.
        ListNode    *next;  // Pointer to next node
    }; // end ListNode

    ListNode   *head; // Pointer to linked list of items.

I am mainly worried about the feasibility of the problem. Can this be done with only a pointer as a parameter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

尾戒 2024-08-30 03:00:29

虽然 C 不需要尾递归优化,但如果您可以将其转换为尾递归(并且您不需要在这里做很多工作),那么,当应用该优化时,您可以保持可读性、清晰度和递归的简洁性与最佳非递归解决方案具有相同的性能(时间和空间)。

我稍微修改了函数的条件,以便它可以在没有节点的空列表上工作(其中 p 为 null),并且在这种情况下将返回 null。这是尾递归,并且确实需要另一个参数:

ListNode* findLargestRecurse(ListNode* p, ListNode* largest) {
  // this is an implementation detail, and would not be called directly
  // requires that largest is not null, but p may be null
  if (!p) return largest;
  if (p->item > largest->item) largest = p;
  return findLargestRecurse(p->next, largest);
}

// preconditions: list head pointer is passed as a parameter, and may be null
// postcondition: returns the node with the largest value, or null
ListNode* findLargest(ListNode* p) {
  if (!p) return 0; // mark A, see below
  return findLargestRecurse(p->next, p);
}

请注意,您使用主条目 findLargest 来设置实际递归 findLargestRecurse 的初始条件/上下文。

但是,我会将尾递归编写为迭代 while 循环,以减少对当前编译器特定且在 C 中通常不熟悉的内容的依赖,这并不难做到:

// preconditions: list head pointer is passed as a parameter, and may be null
// postcondition: returns the node with the largest value, or null
ListNode* findLargest(ListNode* p) {
  ListNode* largest = 0;
  for (; p; p = p->next) {
    if (!largest || p->item > largest->item) {
      largest = p;
    }
  }
  return largest;
}

您可以分离出 !largest< /code> 通过预先执行此操作来从循环中获得条件,通过检查基本情况,就像第一个 findLargest 所做的那样(“标记 A”)。

现在看看这个,你可能想知道为什么我称递归版本更简洁:它确实不适合这个简单的例子。这部分是因为 C 的设计有利于迭代(特别注意 for 循环),部分是因为我试图在递归中变得冗长,而不是像通常那样将其压缩(这样你可以更容易地理解它),剩下的就是因为这只是一个简单的问题。

Though tail-recursion optimization isn't required by C, if you can transform it into tail-recursion (and you can without a lot of work here), then, when that optimization is applied, you can maintain the readability, clarity, and conciseness of recursion with the same performance (time and space) as the best non-recursive solution.

I've slightly modified the function's conditions so it can work on an empty list with no nodes (where p is null) and will return null in that case. This is tail-recursive, and does require another parameter:

ListNode* findLargestRecurse(ListNode* p, ListNode* largest) {
  // this is an implementation detail, and would not be called directly
  // requires that largest is not null, but p may be null
  if (!p) return largest;
  if (p->item > largest->item) largest = p;
  return findLargestRecurse(p->next, largest);
}

// preconditions: list head pointer is passed as a parameter, and may be null
// postcondition: returns the node with the largest value, or null
ListNode* findLargest(ListNode* p) {
  if (!p) return 0; // mark A, see below
  return findLargestRecurse(p->next, p);
}

Notice you use the main entry, findLargest, to setup the initial conditions/context for the actual recursion, findLargestRecurse.

However, I'd write that tail-recursion as an iterative while loop to rely less on what's currently both very compiler-specific and generally unfamiliar in C, which is not hard to do:

// preconditions: list head pointer is passed as a parameter, and may be null
// postcondition: returns the node with the largest value, or null
ListNode* findLargest(ListNode* p) {
  ListNode* largest = 0;
  for (; p; p = p->next) {
    if (!largest || p->item > largest->item) {
      largest = p;
    }
  }
  return largest;
}

You can separate out the !largest condition from the loop by doing it beforehand, by checking a base case just like the first findLargest does ("mark A").

And looking at this now, you may wonder why I called the recursive version more concise: it really isn't for this trivial example. That's partially because C is designed to favor iteration (notice the for loop in particular), somewhat because I tried to be verbose in the recursion instead of squashing it down as I would normally (so you could understand it easier), and the rest is because this is just a simple problem.

荭秂 2024-08-30 03:00:29

我发现大多数递归问题都可以使用思考框架/模板来解决:

  • 我现在手头有哪些信息?
  • 如果进行递归调用,我会得到什么信息?
  • 我怎样才能将这两者结合起来得到最终结果?
  • (另外,一定要清楚“基本情况”。)

在这种情况下,答案是

  • 当前节点中的值,即
  • 该节点之后的列表“后缀”中的最大元素,
  • 嗯,那就是给你的弄清楚
  • (对于空列表,你应该返回什么?家庭作业说了?)

玩得开心。 :)

I find that most recursive problems can be solved using the framework/template of thinking about:

  • What information do I have at hand right now?
  • What information will I get if I make a recursive call?
  • How can I combine those two to make a final result?
  • (Also, be sure to be clear about the 'base case'.)

In this case, the answers are

  • the value in the current node
  • the largest element in the 'suffix' of the list that comes after this node
  • hmmm, that's for you to figure out
  • (What should you return for the empty list? Does the homework say?)

Have fun. :)

这个俗人 2024-08-30 03:00:29

我已经看到一些发布的代码,并且忍不住添加我自己的......因为我真的认为它可以更简单地完成:)

我认为 item 是数字类型。

#include <algorithm> // std::max
#include <limits>    // std::numeric_limits

ListItemType findLargest(const ListNode* p)
{
  if (p == 0)
    return std::numeric_limits<ListItemType>::min();
  else
    return std::max(p->item, findLargest(p->next));
}

正如您所看到的,简单得多,而且我冒昧地添加了一个 const 因为我们当然不需要修改列表本身!

I have seen some code posted and could not refrain to add my own... because I really think it could be done more simply :)

I suppose that item is of a numeric type.

#include <algorithm> // std::max
#include <limits>    // std::numeric_limits

ListItemType findLargest(const ListNode* p)
{
  if (p == 0)
    return std::numeric_limits<ListItemType>::min();
  else
    return std::max(p->item, findLargest(p->next));
}

As you can see, much simpler, and I took the liberty to add a const since we're certainly not going to have to modify the list itself!

我乃一代侩神 2024-08-30 03:00:29

这绝对是可行的,尽管我同意递归不是解决这个问题的最佳方案。在这种情况下,非递归代码将更容易阅读(递归),更快(函数调用的开销),并且内存效率更高(显然有更多的堆栈帧)。

每个递归调用都返回其值或列表其余部分的值中的较大者。

int findLargest (ListNode *p) {
    int current = p->item;
    int next;

    if (p->next == NULL) {
        //The value at this node is obviously larger than a non-existent value
        return current;
    } else {
        //Recur to find the highest value from the rest of the LinkedList
        next = findLargest(p->next);
    }

    //Return the highest value between this node and the end of the list
    if (current > next) {
        return current;
    } else {
        return next;
    }
}

next 项为空时,递归停止。

This is definitely feasible, although I agree that recursion is not the best solution to solve this problem. In this case, non-recursive code would be easier to read (recursion), faster (overhead of function call), and more memory efficient (obviously more stack frames).

Each recursive call returns the greater of either it's value or the value from the rest of the list.

int findLargest (ListNode *p) {
    int current = p->item;
    int next;

    if (p->next == NULL) {
        //The value at this node is obviously larger than a non-existent value
        return current;
    } else {
        //Recur to find the highest value from the rest of the LinkedList
        next = findLargest(p->next);
    }

    //Return the highest value between this node and the end of the list
    if (current > next) {
        return current;
    } else {
        return next;
    }
}

Recursion stops when the next item is null.

木格 2024-08-30 03:00:29

Java版本

return max(head, head.value);
int max(Node node, int currentMax)
{
    if(node==null)
        return currentMax;
    if(node.value>currentMax)
        return max(node.next, node.value);
    else
        return max(node.next, currentMax);
}

Java Version

return max(head, head.value);
int max(Node node, int currentMax)
{
    if(node==null)
        return currentMax;
    if(node.value>currentMax)
        return max(node.next, node.value);
    else
        return max(node.next, currentMax);
}
︶葆Ⅱㄣ 2024-08-30 03:00:29

如果您只想返回最大值,那么您几乎已经写好了。

int FindLargest(ListNode* node){
  if (node != NULL){
    int downTheListLargest = FindLargest(node->next);
    if (downTheListLargest > node->item){
      return downTheListLargest;
    }
    return node->item;
  }
  return //?? some max negative value
}

如果您希望返回指向最大节点的指针,则参数需要是双指针(**),或者函数需要返回指针。

ListNode* FindLargest(ListNode* node){
  if (node == NULL){
    return NULL;
  }
  ListNode* downTheListLargestNode = FindLargest(node->next);
  if (downTheListLargestNode && downTheListLargestNode->item > node->item){
    return downTheListLargestNode;
  }
  return node;
}

实际上没有理由递归地执行此操作。我认为这只是学习递归的练习,但我觉得这是一个糟糕的学习示例。

If you are looking to return just the largest value, then yes you pretty much already have it written.

int FindLargest(ListNode* node){
  if (node != NULL){
    int downTheListLargest = FindLargest(node->next);
    if (downTheListLargest > node->item){
      return downTheListLargest;
    }
    return node->item;
  }
  return //?? some max negative value
}

If you are looking to return a pointer to the largest node, then the parameter needs to be a double pointer (**), or the function needs to return a pointer.

ListNode* FindLargest(ListNode* node){
  if (node == NULL){
    return NULL;
  }
  ListNode* downTheListLargestNode = FindLargest(node->next);
  if (downTheListLargestNode && downTheListLargestNode->item > node->item){
    return downTheListLargestNode;
  }
  return node;
}

Really there is no reason to do this recursively. I assume this is just an exercise to learn about recursion, but I feel it is a poor learning example.

记忆消瘦 2024-08-30 03:00:29

这是另一个惯用的递归解决方案,类似于 Matthieu 的解决方案。与他的解决方案不同,这个解决方案需要一个空列表 - 可以说,获取空列表中的最小项目并不是一个有意义的操作:

// Precondition: list is non-empty.
int find_largest(ListNode const* n) {
    assert(n != 0 && "find_largest requires non-empty list.");
    return n->next == 0 ? n->item
                        : max(n->item, find_largest(n->next));
}

这个解决方案读起来很像数学定义,使用“cases”符号:

             { item(i),                   if i is the last node
largest(i) = {
             { max{item(i), largest(i+1)} else.

Here’s another idiomatic recursive solution, similar to Matthieu’s. Unlike his solution, this one requires an empty list – arguably, taking the smallest item of an empty list isn’t a meaningful operation:

// Precondition: list is non-empty.
int find_largest(ListNode const* n) {
    assert(n != 0 && "find_largest requires non-empty list.");
    return n->next == 0 ? n->item
                        : max(n->item, find_largest(n->next));
}

This one reads much like a mathematical definition, using the “cases” notation:

             { item(i),                   if i is the last node
largest(i) = {
             { max{item(i), largest(i+1)} else.
久隐师 2024-08-30 03:00:29

不需要递归,并且您的示例不是递归(它必须调用自身)。

仅使用指针作为参数就可以做到这一点。

提示:将 p 分配给 p->next 以在列表中前进。

No need for recursion, and your example isn't recursion (it would have to call itself).

It is possible to do this with only a pointer as a parameter.

Hint: Assign p to p->next to advance through the list.

绅士风度i 2024-08-30 03:00:29

始终将递归问题分为两个步骤:停止条件和“问题的其余部分”。首先考虑停止条件。在链表中,它通常是空节点。但就您的情况而言,请考虑当给定节点为空时会发生什么。你会返回什么?事实是,无论如何你都必须返回最大值,并且当列表中没有元素时,就没有 max 元素。在这种情况下,也许您可​​以假设列表必须至少有一个元素。

那么停止条件是什么呢?停止条件是当列表中有单个元素时;在这种情况下,最大值是该节点的值。

下一步是递归步骤。假设您有一个链接到列表的元素。请注意我如何描述链表:链接到链表的节点。如果该节点的最大值大于列表的最大值,则最大值为该节点的值,否则为列表的最大值。

Always break recursion problems into two steps: the stop condition and "the rest of the problem". Start by thinking about the stop condition. In linked lists it's usually the null node. But in your case, think what happens when the given node is null. What would you return? The truth is that you have to return the max value no matter what, and when there are no elements in the list there is no max element. In this case maybe you can just assume that a list must therefore have at least one element.

So what's the stop condition? The stop condition is when there's a single element in the list; and in this case the max value is that node's value.

The next step is the recursive step. Suppose you have an element linked to a list. And pay attention how I describe a linked list: a node linked to a linked list. The max value is the value of that node if it's larger than the largest value of the list, or the largest value of the list otherwise.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文