【LeetCode】2. Add Two Numbers(C++)

本文解析了LeetCode上两数相加的链表问题,通过逆序链表表示数字并求和,提供了两种解决方案:一是直接修改第一个链表,二是创建新链表返回结果。代码详细展示了如何处理进位和遍历链表。

地址:https://leetcode.com/problems/add-two-numbers/

题目:

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example:

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.

理解:

两个链表,逆序表示两个非负的数字,要求求两个数字的和。
我的实现把l1作为返回结果的链表。每次算一位保存在l1,然后记录进位,直到某个链表遍历完了。如果遍历完的是l1,把l2剩余的补到l1的后面。然后把多的借位加到另一个链表即可。

实现:

class Solution {
public:
	ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
		int carry = 0;
		ListNode *p1, *p2;
		ListNode *head = l1;
		while (l1&&l2) {
			int tmp = l1->val + l2->val + carry;
			l1->val = tmp % 10;
			carry = tmp / 10;
			p1 = l1;
			p2 = l2;
			l1 = l1->next;
			l2 = l2->next;
		}
		if (l2) {
			p1->next = l2;
			l1 = l2;
		}
		while (carry||l1) {
			if (l1) {
				int tmp = l1->val + carry;
				l1->val = tmp % 10;
				carry = tmp / 10;
				p1 = l1;
				l1 = l1->next;
			}
			else {
				p1->next = new ListNode(1);
				break;
			}
		}
		return head;
	}
};

看了下别人的代码,重新建一个链表整体的思路似乎更清楚一点。重新实现了一下。

class Solution {
public:
	ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
		int carry = 0;
		ListNode *p1, *p2;
		ListNode *head=new ListNode(0),*phead=head;
		while (l1||l2) {
			int tmp = carry;
			if (l1) {
				tmp += l1->val;
				p1 = l1;
				l1 = l1->next;
			}
			if (l2) {
				tmp += l2->val;
				p2 = l2;
				l2 = l2->next;
			}
			phead->next = new ListNode(tmp % 10);
			carry = tmp / 10;
			phead = phead->next;
		}
		if (carry) {
			phead->next = new ListNode(1);
		}
		return head->next;
	}
};

不知道这样为什么比上面的快一些。去洗澡。

<think>我们面对的错误是:TypeError: 'ListNode' object is not iterable 用户的问题描述:在尝试将链表转换为数字时,遇到了“ListNode对象不可迭代”的错误。具体来说,用户试图使用map函数将链表节点的值转换为字符串,然后拼接成一个数字。 根据引用[2],链表是由节点组成的,每个节点包含数据部分和指向下一个节点的指针。因此,我们不能直接对链表对象使用map(),因为map()要求第二个参数是可迭代的(如列表、元组等),而链表节点(ListNode)默认不支持迭代。 解决方案:我们需要手动遍历链表,将每个节点的值收集到一个列表中,然后再使用map()函数。 步骤: 1. 创建一个空列表,用于存放链表节点的值。 2. 从头节点开始,遍历链表,将每个节点的值添加到列表中。 3. 使用map()函数将列表中的每个值转换为字符串。 4. 将字符串列表拼接成一个完整的字符串,然后转换为整数。 但是,用户的问题中还有另一个错误:在拼接字符串时,可能忽略了链表表示数字的顺序。通常,链表表示的数字是逆序的(如题目2.两数相加),但这里用户希望直接拼接成数字。所以我们需要根据题目的要求调整顺序。 在LeetCode2题“两数相加”中,链表表示的数字是逆序存储的,即个位在头节点。如果我们想得到原始数字,有两种方式: a) 按链表顺序(逆序)拼接,然后反转字符串得到正序数字(但这样可能效率不高) b) 或者,我们可以在遍历链表时,将每个节点的值插入到列表的头部,这样列表就是正序的。 然而,根据用户提供的错误代码行: l1_num = int("".join(map(str,l1))) 这里l1是一个链表头节点,而map函数要求l1是可迭代的,但链表节点本身不可迭代。 因此,我们首先需要将链表节点的值提取到一个列表中。然后,根据题目要求,链表是逆序表示数字的,所以我们需要将列表反转得到正序数字,或者直接按逆序拼接(注意:题目中逆序存储,所以遍历链表得到的顺序就是数字的逆序,例如链表2->4->3表示数字342,那么遍历得到[2,4,3],然后我们反转列表得到[3,4,2]再拼接成342,或者直接按逆序的列表拼接成字符串再反转?但这样效率低。实际上,我们可以直接按遍历顺序(即逆序的数字)拼接字符串,然后这个字符串就是数字的逆序字符串,再反转字符串?或者更简单:我们按遍历顺序拼接字符串,然后反转字符串得到正序数字字符串。 但是,注意:题目要求的是将链表表示的数字计算出来,而链表表示的是逆序,所以: 链表:2->4->3 表示数字342,即2是最高位(个位)?实际上,链表头是个位,然后十位,百位...所以: 数字 = 节点0 * 10^0 + 节点1 * 10^1 + ... 因此,我们也可以不通过字符串,而是通过累加的方式:遍历链表,每次将当前节点的值乘以10的幂次(从0开始递增)然后累加。但是,这样需要计算幂次,可能效率不如字符串转换(如果数字不是特别大的话)。 然而,用户使用了字符串拼接的方法,所以我们也用字符串方法。 具体步骤(以链表l1为例): 1. 初始化一个空列表:values = [] 2. 当前节点curr = l1 3. 遍历链表直到None: values.append(str(curr.val)) curr = curr.next 4. 此时values中存储的是从链表头(个位)到链表尾(最高位)的字符串。但是,我们实际需要的是数字的正序表示,所以需要将values反转,然后拼接。 例如:链表2->4->3,values为['2','4','3'],反转后为['3','4','2'],拼接成"342" 5. 然后转换为整数:int("342") 但是,注意:链表的顺序本身就是数字的逆序,所以我们可以直接使用列表,然后从后往前拼接?或者反转列表。 另一种方法:在遍历链表时,将每个节点的值插入到列表的头部,这样就不需要反转列表了。例如: values = [] curr = l1 while curr: values.insert(0, str(curr.val)) # 每次插入到头部,这样最后得到的是正序 curr = curr.next 然后 l1_num = int(''.join(values)) 但是,insert(0)操作会导致每次都要移动列表中的元素,时间复杂度为O(n^2),而先追加再反转列表的时间复杂度为O(n)(反转操作是O(n))。所以,推荐先追加再反转。 因此,我们可以这样写: values = [] curr = l1 while curr: values.append(str(curr.val)) curr = curr.next # 反转列表 values.reverse() l1_num = int(''.join(values)) 或者,我们也可以不反转列表,而是将列表反向拼接,使用reversed: l1_num = int(''.join(reversed(values))) 注意:如果链表为空,则values为空列表,拼接后得到空字符串,转换为整数会出错(因为int('')会报错)。所以需要处理空链表的情况。 但是,在LeetCode题目中,空链表表示数字0,所以我们可以这样处理: if not values: l1_num = 0 else: l1_num = int(''.join(values[::-1])) # 或者使用reversed 但是,这里有一个更直接的方法:我们不需要反转整个列表,因为链表遍历的顺序就是数字的逆序(个位、十位、百位...),所以我们可以直接使用这个逆序的字符串,然后通过数学方法计算数字?但用户要求用字符串转换。 实际上,我们也可以这样理解:链表遍历得到的顺序就是数字的逆序,所以如果我们直接拼接这个字符串,那么得到的是逆序的字符串,然后我们把这个字符串反转就可以得到正序的字符串。例如: 链表: 2->4->3 直接拼接得到字符串"243",反转得到"342",即342。 所以,我们可以: s = ''.join(values) # 得到"243" s = s[::-1] # 反转字符串得到"342" l1_num = int(s) 但是,注意:字符串反转同样需要O(n)时间,而且创建了额外的字符串。所以,两种方法在时间上差不多。 然而,在LeetCode题目中,我们通常不推荐使用字符串转换,因为链表可能很长,数字可能非常大,超过整型的表示范围(虽然Python的整型可以表示任意大的整数,但效率可能较低)。所以,更推荐使用数学方法: num = 0 multiplier = 1 # 从个位开始,乘数因子初始为1(10^0) curr = l1 while curr: num += curr.val * multiplier multiplier *= 10 curr = curr.next 这样,遍历一次链表即可,且没有字符串转换的开销。但注意,这种方法在数字特别大时,乘数因子multiplier也会很大,但Python可以处理。 但是,用户已经使用了字符串转换,并且遇到了类型错误,所以我们的重点是如何正确遍历链表。 所以,针对用户原来的代码: l1_num = int("".join(map(str, l1))) # 这里l1是链表头节点,不可迭代 我们需要修改为: # 先遍历链表,将节点的值收集到列表中 values = [] curr = l1 while curr: values.append(str(curr.val)) curr = curr.next # 反转列表得到正序数字的字符串表示(因为链表是逆序存储的) # 或者反转字符串 num_str = ''.join(values)[::-1] # 如果链表为空,则num_str为空字符串,此时应该为0 if num_str == '': l1_num = 0 else: l1_num = int(num_str) 或者更简洁地,使用列表反转: values = [] curr = l1 while curr: values.append(str(curr.val)) curr = curr.next # 反转列表,然后拼接 values.reverse() l1_num = int(''.join(values)) if values else 0 但是,注意:在LeetCode 2.两数相加中,链表表示的数字就是逆序的,所以上面的方法得到的是正确的数字。 但是,我们也可以不反转,而是通过数学方法(更高效,且避免字符串操作): l1_num = 0 multiplier = 1 curr = l1 while curr: l1_num += curr.val * multiplier multiplier *= 10 curr = curr.next 这种方法更直接,且避免了字符串转换和反转操作。 因此,推荐使用数学方法。 但是,用户的问题是如何解决“不可迭代”的错误,所以我们需要告诉用户:不能直接将链表节点传递给map(),因为链表节点不是可迭代对象。必须手动遍历链表,将每个节点的值取出。 总结步骤: 1. 遍历链表,将每个节点的值取出(使用循环,通过next指针移动)。 2. 将取出的值用于后续计算(拼接字符串或数学计算)。 示例代码(数学方法): def get_number_from_linked_list(head): num = 0 multiplier = 1 curr = head while curr: num += curr.val * multiplier multiplier *= 10 curr = curr.next return num 然后,在addTwoNumbers函数中: l1_num = get_number_from_linked_list(l1) l2_num = get_number_from_linked_list(l2) 但是,注意:这种方法在链表很长时,乘数因子会变得非常大,但Python可以处理大整数。如果题目要求不能使用大整数(比如用C++等语言有整数范围限制),则不能使用这种方法。但题目没有特别说明,且用户使用了字符串转换,所以这里我们主要解决迭代问题。 另外,在LeetCode 2.两数相加的官方解法中,通常不将链表转换为整数,而是直接逐位相加,因为这样更高效,且不受整数范围限制。但用户的问题是如何将链表转换为整数,所以我们提供了两种方法。 最后,针对用户遇到的错误,我们给出修改后的代码片段(使用数学方法避免迭代错误): l1_num = 0 digit = 1 node = l1 while node: l1_num += node.val * digit digit *= 10 node = node.next l2_num = 0 digit = 1 node = l2 while node: l2_num += node.val * digit digit *= 10 node = node.next 然后计算两数之和,再将和转换为链表(逆序存储)。但注意,题目要求返回的是链表,所以还需要将和转换为逆序链表。这可能需要额外的步骤。 因此,我们也可以考虑直接逐位相加的方法,这样更符合题目要求(且题目要求不能将链表转换为整数,因为可能溢出?但题目没有明确,而且Python不会溢出)。不过,既然用户遇到了这个错误,我们主要解决链表遍历问题。 所以,我们回答的重点是:链表节点不可迭代,必须通过循环遍历每个节点。 因此,回答如下:</think>### 解决 `TypeError: 'ListNode' object is not iterable` 错误 当出现此错误时,说明您尝试直接对链表节点使用 `map()` 或迭代操作,但链表节点本身不支持迭代。以下是逐步解决方案: #### 错误原因分析 1. **链表结构特性** 链表节点(`ListNode`)包含数据域(`val`)和指针域(`next`),但**不是可迭代对象**(如列表、元组等)[^2]。 2. **直接对节点使用迭代函数** 如 `map(str, l1)` 中的 `l1` 是链表头节点,无法直接迭代。 #### 正确遍历链表的两种方法 ##### 方法1:使用 `while` 循环遍历(推荐) ```python def list_to_num(head): values = [] curr = head # 当前节点指针 while curr: # 当节点存在时 values.append(str(curr.val)) # 收集节点值 curr = curr.next # 移动到下一个节点 # 反转链表值(因链表是逆序存储) reversed_str = ''.join(values)[::-1] return int(reversed_str) if reversed_str else 0 # 示例用法 l1_num = list_to_num(l1_head) ``` ##### 方法2:递归遍历 ```python def list_to_num_recursive(head, values=None): if values is None: values = [] if not head: # 基线条件:空节点 reversed_str = ''.join(values)[::-1] return int(reversed_str) if reversed_str else 0 values.append(str(head.val)) return list_to_num_recursive(head.next, values) ``` #### 关键说明 1. **链表逆序特性** 在类似 LeetCode "两数相加" 的问题中,链表以逆序存储数字(如 `2->4->3` 表示 `342`)。解决方案中通过 `[::-1]` 反转字符串得到正确数值[^3]。 2. **空链表处理** 当链表为空时,返回数字 `0` 避免 `int('')` 错误。 #### 完整解决方案示例 ```python class Solution: def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: def to_num(head): vals = [] while head: vals.append(str(head.val)) head = head.next return int(''.join(vals)[::-1]) if vals else 0 total = to_num(l1) + to_num(l2) # 将结果转换为逆序链表(略) ``` > **为什么不能直接迭代?** > Python 要求可迭代对象必须实现 `__iter__()` 方法,而链表节点未实现此方法[^1]。必须通过 `next` 指针手动遍历。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值