面试:对两个链表中的数字求和

发布于 2024-12-12 04:20:12 字数 437 浏览 0 评论 0原文

在昨天的一次采访中,有人问我如何对两个包含数字的单链表的值求和。他们还表示,这些列表的长度可能不等。

我问列表是否是向后存储的,因为这就是我在大学了解到的,但他们说不是,它是向前存储的。他们还说我不能简单地反转列表,然后添加,然后反转它以再次向前移动,因为该选项需要太多处理。我在网上能找到的都是这种解决方案。

即使他们暗示我应该使用递归函数来完成此操作,我也无法给出答案。

谁能帮我解决这个问题。这是一份 C++ 工作,我希望如果我接到回电并且我能够解释我研究了该解决方案,他们可能会认为这是一个好兆头。谢谢。

对于那些对求和应该如何工作感到困惑的人,它以这种方式呈现。

列表1:1->2->9 列表 2:1->3

因此,由于数字是向前存储的,因此我需要首先添加 9 和 3(两个列表的末尾)。然后取 1 进位并执行 1 + 2 + 1。等等。

During an interview yesterday, I was asked how I would go about summing the values of two singly linked lists that contained digits. They also said the lists could be unequal lengths.

I asked if the list was stored backwards, as that's how I learned about it at uni, but they said no, it was stored forward. They also said I couldn't simply reverse the lists, add then, then reverse it to get it forward again because that option required too much processing. This sort of solution is all I've been able to find online.

I was unable to give an answer, even after they hinted that I should be doing this with a recursive function.

Can anyone help me out with what the solution would have been. This was for a C++ job and I'm hoping that if I ever get called back and I'm able to explain I researched the solution, they might see that as a good sign. Thank you.

For those confused about how the summation is supposed to work, it was presented in this way.

List 1: 1->2->9
List 2: 1->3

So since the numbers are stored forward, I would need to begin by adding the 9 and 3 (end of both lists). Then take the 1 carry and do 1 + 2 + 1. Etc.

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

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

发布评论

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

评论(4

许一世地老天荒 2024-12-19 04:20:12

您计算两个列表的长度。您在较短的列表的开头填充一些 0 数字,以便它们的长度相等。现在你用额外的 0 填充两个数字(它将被第一个数字的进位使用。因此 9 + 1 = 10 是可能的)。
您创建第三个链表,其长度等于前两个链表。

现在你创建一个像这样的类:

class Digit
{
    public:
    Digit *Next;
    int Dt;
}

和一个像这样的函数:

int Sum(const Digit* left, const Digit* right, Digit* runningTotal)
{
    int carry = 0;

    if (left->Next != NULL)
    {
        carry = Sum(left->Next, right->Next, runningTotal->Next);
    }

    carry += left->Dt + right->Dt;

    runningTotal->Dt = carry % 10;

    carry /= 10;

    return carry;
}
  • 这是“版本 0”。
  • 在“版本 1”中,您删除了最后一个携带的额外填充,并且仅在需要时添加它。
  • 在“版本 2”中,您从链接列表的前面删除了不必要的“0”数字。
  • 在“版本 3”中,您直接在 Sum 中创建 runningTotal 链接列表。您只向第一级 Sum 提供运行总计的“头”。
  • 在“版本 4”中,您不是填充较短的 LL,而是传递一个关于从最长 LL 跳过的位数的参数(这是最困难的段落)。

还有另一种可能性,更复杂,但不需要预先计算列表的长度。它使用两个递归函数:

第一个递归函数只是在左右都存在时简单地遍历左右两个函数。如果两者同时完成,那么您可以像前面的示例一样简单地回滚。

如果其中一个先于另一个完成,那么您可以使用另一个递归函数,如下所示(*extraDigits 的初始值为 1):

void SaveRemainingDigits(const Digit *remaining, int *extraDigits, int **buffer)
{
    int currentDigit = *extraDigits - 1;

    *extraDigits = *extraDigits + 1;

    if (remaining->Next)
    {
        SaveRemainingDigits(remaining->Next, extraDigits, buffer);    
    }
    else
    {
        *buffer = (int*)malloc(sizeof(int) * extraDigits);
    }

    (*buffer)[currentDigit] = remaining->Dt;
}

当该函数最终返回时,我们有一个暂存器,从中提取数字和暂存器的长度

我们的第一个递归函数的最内层现在必须将最短链表的当前数字与暂存器的最后一位数字相加,并将最长链表的当前数字放入暂存器中代替刚刚使用的数字。现在,您展开递归函数,并将暂存器用作循环数组。完成展开后,您可以将元素直接从暂存器添加到 runningTotal 链接列表中。

正如我所说,它有点复杂,但在 1-2 小时内我可以把它写成一个程序。

示例(不带进位)

1 2 3 4
6 5

递归前两个元素的 。现在您

1-6 (in the first level)
2-5 (in the second level)

看到第二个列表已完成并且您使用了第二个函数。

3 (extraDigit enters as 0, is modified to 1. currentDigit = 0)
4 (extraDigit enters as 1, is modified to 2. currentDigit = 1. 
   malloc of 2 elements,
   buffer[currentDigit] = 4 => buffer[1] = 4)

unroll and we return to the previous row

3 (currentDigit = 0
   buffer[currentDigit] = 3 => buffer[0] = 3)

现在我们回到之前的函数

2-5 (in the second level, 
     with a lengthBuffer == 2, 
     we set index = length(buffer) - 1
     currentDigitTotal = 5 + buffer[index] => currentDigitTotal = 5 + 4
     buffer[index] = 2 => buffer[1] = 2;
     index = (index - 1 + lengthBuffer) % lengthBuffer => index = 0

1-6 (in the first level, 
     with a lengthBuffer == 2, 
     index = 0,
     currentDigitTotal = 6 + buffer[index] => currentDigitTotal = 6 + 3
     buffer[index] = 1 => buffer[0] = 1;
     index = (index - 1 + lengthBuffer) % lengthBuffer => index = 1

now we exited the recursive function. 
In an external function we see that we have a buffer. 
We add its elements to the head of the total.

Our Linked list now is 9-9 and our buffer is 1,2 with index 1

for (int i = 0; i < lengthBuffer; i++)
{
    runningTotal.AddHead(buffer(index));
    index = (index - 1 + lengthBuffer) % lengthBuffer
}

You count the length of both lists. You pad at the beginning the shorter list with a number of 0 digits so that they are equal in length. Now you pad both numbers with an extra 0 (it will be used by the carry of the first digits. So that it's possible that 9 + 1 = 10).
You create a third linked list of length equal to the previous two.

Now you do a class like this:

class Digit
{
    public:
    Digit *Next;
    int Dt;
}

and a function like this:

int Sum(const Digit* left, const Digit* right, Digit* runningTotal)
{
    int carry = 0;

    if (left->Next != NULL)
    {
        carry = Sum(left->Next, right->Next, runningTotal->Next);
    }

    carry += left->Dt + right->Dt;

    runningTotal->Dt = carry % 10;

    carry /= 10;

    return carry;
}
  • This is "version 0".
  • In "version 1" you remove the extra padding for the last carry and you add it only if needed.
  • In "version 2" you remove unnecessary "0" digits from the front of the linked lists.
  • In "version 3" you create the runningTotal linked list directly in Sum. You give to the first level Sum only the "Head" of the Running Total.
  • In "version 4" instead of padding the shorter LL, you pass a parameter on the number of digits to skip from the longest LL (this is the most difficult passage).

There is another possibility, much more complex, but that doesn't require to pre-count the length of the lists. It uses two recursive functions:

The first recursive function simply traverses left and right while both are present. If both finishes at the same time then you can simply roll-back as in the previous example.

If one of them finishes before the other, then you use another recursive function like this (the initial value of *extraDigits is 1):

void SaveRemainingDigits(const Digit *remaining, int *extraDigits, int **buffer)
{
    int currentDigit = *extraDigits - 1;

    *extraDigits = *extraDigits + 1;

    if (remaining->Next)
    {
        SaveRemainingDigits(remaining->Next, extraDigits, buffer);    
    }
    else
    {
        *buffer = (int*)malloc(sizeof(int) * extraDigits);
    }

    (*buffer)[currentDigit] = remaining->Dt;
}

when this function finally returns, we have a scratchpad from where to extract the digits and the length of the scratchpad

The innermost level of our first recursive function has now to sum its current digit of the shortest linked list with the last digit of the scratchpad and put the current digit of the longest linked list in the scratchpad in place of the digit just used. Now you unroll your recursive function and you use the scratchpad as a circular array. When you finish unrolling, then you add elements to the runningTotal linked list taking them directly from the scratchpad.

As I've said, it's a little complex, but in 1-2 hours I could write it down as a program.

An example (without carry)

1 2 3 4
6 5

you recurse the first two elements. So you have

1-6 (in the first level)
2-5 (in the second level)

Now you see that the second list is finished and you use the second function.

3 (extraDigit enters as 0, is modified to 1. currentDigit = 0)
4 (extraDigit enters as 1, is modified to 2. currentDigit = 1. 
   malloc of 2 elements,
   buffer[currentDigit] = 4 => buffer[1] = 4)

unroll and we return to the previous row

3 (currentDigit = 0
   buffer[currentDigit] = 3 => buffer[0] = 3)

Now we return to the previous function

2-5 (in the second level, 
     with a lengthBuffer == 2, 
     we set index = length(buffer) - 1
     currentDigitTotal = 5 + buffer[index] => currentDigitTotal = 5 + 4
     buffer[index] = 2 => buffer[1] = 2;
     index = (index - 1 + lengthBuffer) % lengthBuffer => index = 0

1-6 (in the first level, 
     with a lengthBuffer == 2, 
     index = 0,
     currentDigitTotal = 6 + buffer[index] => currentDigitTotal = 6 + 3
     buffer[index] = 1 => buffer[0] = 1;
     index = (index - 1 + lengthBuffer) % lengthBuffer => index = 1

now we exited the recursive function. 
In an external function we see that we have a buffer. 
We add its elements to the head of the total.

Our Linked list now is 9-9 and our buffer is 1,2 with index 1

for (int i = 0; i < lengthBuffer; i++)
{
    runningTotal.AddHead(buffer(index));
    index = (index - 1 + lengthBuffer) % lengthBuffer
}
抹茶夏天i‖ 2024-12-19 04:20:12

我将以这样的方式解决这个问题

让我们假设这两个列表是:
1->2->7->6->4->3 和
5->7->2

总和为1->2->7 + Sum(6->4->3, 5->7->2)

现在我们创建一个函数,它接受 2 个相同大小的列表并返回它们的总和

类似

list1->val + list2->val + Sum(list1->next, list2->next)

,这与基本情况 if(list1->next == NULL) return list1->val+list2->val;

注意::我们可以轻松处理下一次传递中的进位,或者您可以在我们的 sum 函数本身中处理它

所以毕竟我们的 ans 将是 1 ->2->7->11->11->5
然后递归执行 %10 并取进位并将其添加到先前的值。

所以最终的 ans 将是 1->2->8->2->1->5

I will approach this problem in something like this

Let's suppose the 2 lists are :
1->2->7->6->4->3 and
5->7->2

The sum is 1->2->7 + Sum(6->4->3, 5->7->2)

Now we make a function that take 2 lists of same size and returns their sum

which will be something like

list1->val + list2->val + Sum(list1->next, list2->next)

with base case if(list1->next == NULL) return list1->val+list2->val;

Note :: we can handle the carry in next pass easily or you can handle that in our sum function itself

So after all this our ans will be 1->2->7->11->11->5
then recursively do %10 and take carry and add it to previous value.

so final ans will be 1->2->8->2->1->5

海螺姑娘 2024-12-19 04:20:12

我会创建一个像 *head 或 *tail 这样的节点来存储我开始的节点的地址,然后迭代列表以确保我不会回到起点。这不需要计算每个的长度,这听起来效率很低。

至于递归性,只需在函数顶部进行此检查并返回 (node->value + myfunct(node->prev));如果您只做一次数学计算,那么效率会更高。

I would have created a node like *head or *tail to store the address of the node that I started from, then iterate through the list making sure im not back at my start point. This doesn't require to to have to count the length of each, which sounds inefficient.

As for the recursiveness just do this check at the top of the function and return (node->value + myfunct(node->prev)); It'd be more efficient given you're doing the math once.

风轻花落早 2024-12-19 04:20:12

列表“1,2,9”和“1,3”分别代表数字“129”和“13”,在这种情况下总和是“142”。

使用递归

  • 计算每个列表的长度。
  • 如果长度不同,请在最短的开头用零填充。
  • 递归地迭代列表,返回:a)进位数(如果有),否则为零,b)列表的尾部。

在伪代码中:

def sum_lists_rec(a, b, start_a, start_b, length_a, length_b):
    """Returns a pair of two elements: carry and the tail of the list."""

    if the end of the lists:
        return (0, empty_list)

    result = sum_lists_rec(a+1, b+1, start_a+1, start_b+1, length_a, length_b)

    carry = (a[0] + b[0] + result[0]) / 10
    digit = (a[0] + b[0] + result[0]) % 10

    return (carry, [digit] ++ result[1])

def sum_lists1(a, b):
    length_a = length(a)
    length_b = length(b)

    if length_a < length_b:
        a = [0, 0, ..., (length_b - length_a)] ++ a
    else if length_b < length_a:
        b = [0, 0, ..., (length_a - length_b)] ++ b

    result = sum_lists_rec(a, b, length_a, length_b, 0, 0)

    if result[0] != 0:
        return [result[0]] ++ result[1]
    else:
        return result[1]

作为替代方案,您可以使用堆栈:

  • 计算每个列表的长度。
  • 如果长度不同,请在最短的开头用零填充。
  • 将两个列表的每个数字压入堆栈。
  • 弹出堆栈直到为空,创建新列表。

The lists "1, 2, 9" and "1, 3" each represent the numbers "129" and "13", in which case the sum is "142".

Using recursion

  • Compute the length of each list.
  • If the lengths differ, pad the shortest with zeroes at the beggining.
  • Iterate over the lists recursively, returning: a) the carry number if any, or zero otherwise, and b) the tail of the list.

In pseudocode:

def sum_lists_rec(a, b, start_a, start_b, length_a, length_b):
    """Returns a pair of two elements: carry and the tail of the list."""

    if the end of the lists:
        return (0, empty_list)

    result = sum_lists_rec(a+1, b+1, start_a+1, start_b+1, length_a, length_b)

    carry = (a[0] + b[0] + result[0]) / 10
    digit = (a[0] + b[0] + result[0]) % 10

    return (carry, [digit] ++ result[1])

def sum_lists1(a, b):
    length_a = length(a)
    length_b = length(b)

    if length_a < length_b:
        a = [0, 0, ..., (length_b - length_a)] ++ a
    else if length_b < length_a:
        b = [0, 0, ..., (length_a - length_b)] ++ b

    result = sum_lists_rec(a, b, length_a, length_b, 0, 0)

    if result[0] != 0:
        return [result[0]] ++ result[1]
    else:
        return result[1]

As an alternative, you can use a stack:

  • Compute the length of each list.
  • If the lengths differ, pad the shortest with zeroes at the beggining.
  • Push each digit of both lists on the stack.
  • Pop the stack until is empty, creating the new list.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文